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

@@ -11,9 +11,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as types from 'vs/workbench/api/node/extHostTypes';
import * as EditorCommon from 'vs/editor/common/editorCommon';
import { Model as EditorModel } from 'vs/editor/common/model/model';
import { TestThreadService } from './testThreadService';
import { TextModel as EditorModel } from 'vs/editor/common/model/textModel';
import { TestRPCProtocol } from './testRPCProtocol';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
@@ -32,10 +31,11 @@ import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import * as vscode from 'vscode';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import 'vs/workbench/parts/search/electron-browser/search.contribution';
import { NoopLogService } from 'vs/platform/log/common/log';
import { NullLogService } from 'vs/platform/log/common/log';
import { ITextModel } from 'vs/editor/common/model';
const defaultSelector = { scheme: 'far' };
const model: EditorCommon.IModel = EditorModel.createFromString(
const model: ITextModel = EditorModel.createFromString(
[
'This is the first line',
'This is the second line',
@@ -45,7 +45,7 @@ const model: EditorCommon.IModel = EditorModel.createFromString(
undefined,
URI.parse('far://testing/file.b'));
let threadService: TestThreadService;
let rpcProtocol: TestRPCProtocol;
let extHost: ExtHostLanguageFeatures;
let mainThread: MainThreadLanguageFeatures;
let commands: ExtHostCommands;
@@ -63,7 +63,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
let inst: IInstantiationService;
{
let instantiationService = new TestInstantiationService();
threadService = new TestThreadService();
rpcProtocol = new TestRPCProtocol();
instantiationService.stub(IHeapService, {
_serviceBrand: undefined,
trackRecursive(args) {
@@ -98,36 +98,36 @@ suite('ExtHostLanguageFeatureCommands', function () {
inst = instantiationService;
}
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService);
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol);
extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
versionId: model.getVersionId(),
modeId: model.getLanguageIdentifier().language,
url: model.uri,
uri: model.uri,
lines: model.getValue().split(model.getEOL()),
EOL: model.getEOL(),
}]
});
const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors);
threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors);
rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
const heapService = new ExtHostHeapService();
commands = new ExtHostCommands(threadService, heapService, new NoopLogService());
threadService.set(ExtHostContext.ExtHostCommands, commands);
threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService));
commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService());
rpcProtocol.set(ExtHostContext.ExtHostCommands, commands);
rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol));
ExtHostApiCommands.register(commands);
const diagnostics = new ExtHostDiagnostics(threadService);
threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
const diagnostics = new ExtHostDiagnostics(rpcProtocol);
rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, heapService, diagnostics);
threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
extHost = new ExtHostLanguageFeatures(rpcProtocol, extHostDocuments, commands, heapService, diagnostics);
rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
mainThread = <MainThreadLanguageFeatures>threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, threadService));
mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol));
threadService.sync().then(done, done);
rpcProtocol.sync().then(done, done);
});
suiteTeardown(() => {
@@ -140,7 +140,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
while (disposables.length) {
disposables.pop().dispose();
}
threadService.sync()
rpcProtocol.sync()
.then(() => done(), err => done(err));
});
@@ -154,13 +154,11 @@ suite('ExtHostLanguageFeatureCommands', function () {
commands.executeCommand('vscode.executeWorkspaceSymbolProvider', true)
];
// threadService.sync().then(() => {
TPromise.join(<any[]>promises).then(undefined, (err: any[]) => {
assert.equal(err.length, 4);
done();
return [];
});
// });
});
test('WorkspaceSymbols, back and forth', function (done) {
@@ -182,7 +180,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
threadService.sync().then(() => {
rpcProtocol.sync().then(() => {
commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeWorkspaceSymbolProvider', 'testing').then(value => {
for (let info of value) {
@@ -204,11 +202,11 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
await threadService.sync();
await rpcProtocol.sync();
let symbols = await commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeWorkspaceSymbolProvider', '');
assert.equal(symbols.length, 1);
await threadService.sync();
await rpcProtocol.sync();
symbols = await commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeWorkspaceSymbolProvider', '*');
assert.equal(symbols.length, 1);
});
@@ -223,13 +221,11 @@ suite('ExtHostLanguageFeatureCommands', function () {
commands.executeCommand('vscode.executeDefinitionProvider', true, false)
];
// threadService.sync().then(() => {
TPromise.join(<any[]>promises).then(undefined, (err: any[]) => {
assert.equal(err.length, 4);
done();
return [];
});
// });
});
test('Definition, back and forth', function () {
@@ -249,7 +245,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.Location[]>('vscode.executeDefinitionProvider', model.uri, new types.Position(0, 0)).then(values => {
assert.equal(values.length, 4);
for (let v of values) {
@@ -295,7 +291,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
threadService.sync().then(() => {
rpcProtocol.sync().then(() => {
commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeDocumentSymbolProvider', model.uri).then(values => {
assert.equal(values.length, 2);
let [first, second] = values;
@@ -325,7 +321,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => {
assert.ok(list instanceof types.CompletionList);
@@ -375,7 +371,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}, []));
threadService.sync().then(() => {
rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => {
assert.ok(list instanceof types.CompletionList);
assert.equal(list.isIncomplete, true);
@@ -393,7 +389,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.Command[]>('vscode.executeCodeActionProvider', model.uri, new types.Range(0, 0, 1, 1)).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -404,6 +400,35 @@ suite('ExtHostLanguageFeatureCommands', function () {
});
});
test('vscode.executeCodeActionProvider results seem to be missing their `command` property #45124', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, {
provideCodeActions(document, range): vscode.CodeAction[] {
return [{
command: {
arguments: [document, range],
command: 'command',
title: 'command_title',
},
kind: types.CodeActionKind.Empty.append('foo'),
title: 'title',
}];
}
}));
return rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', model.uri, new types.Range(0, 0, 1, 1)).then(value => {
assert.equal(value.length, 1);
let [first] = value;
assert.ok(first.command);
assert.equal(first.command.command, 'command');
assert.equal(first.command.title, 'command_title');
assert.equal(first.kind.value, 'foo');
assert.equal(first.title, 'title');
});
});
});
// --- code lens
test('CodeLens, back and forth', function () {
@@ -420,7 +445,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.CodeLens[]>('vscode.executeCodeLensProvider', model.uri).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -442,7 +467,7 @@ suite('ExtHostLanguageFeatureCommands', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return commands.executeCommand<vscode.DocumentLink[]>('vscode.executeLinkProvider', model.uri).then(value => {
assert.equal(value.length, 1);
let [first] = value;

View File

@@ -8,11 +8,10 @@
import * as assert from 'assert';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { MainThreadCommandsShape } from 'vs/workbench/api/node/extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { OneGetThreadService } from './testThreadService';
import { SingleProxyRPCProtocol } from './testRPCProtocol';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { NoopLogService } from 'vs/platform/log/common/log';
import { NullLogService } from 'vs/platform/log/common/log';
suite('ExtHostCommands', function () {
@@ -21,16 +20,15 @@ suite('ExtHostCommands', function () {
let lastUnregister: string;
const shape = new class extends mock<MainThreadCommandsShape>() {
$registerCommand(id: string): TPromise<any> {
return undefined;
$registerCommand(id: string): void {
//
}
$unregisterCommand(id: string): TPromise<any> {
$unregisterCommand(id: string): void {
lastUnregister = id;
return undefined;
}
};
const commands = new ExtHostCommands(OneGetThreadService(shape), undefined, new NoopLogService());
const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), undefined, new NullLogService());
commands.registerCommand('foo', (): any => { }).dispose();
assert.equal(lastUnregister, 'foo');
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
@@ -42,16 +40,15 @@ suite('ExtHostCommands', function () {
let unregisterCounter = 0;
const shape = new class extends mock<MainThreadCommandsShape>() {
$registerCommand(id: string): TPromise<any> {
return undefined;
$registerCommand(id: string): void {
//
}
$unregisterCommand(id: string): TPromise<any> {
$unregisterCommand(id: string): void {
unregisterCounter += 1;
return undefined;
}
};
const commands = new ExtHostCommands(OneGetThreadService(shape), undefined, new NoopLogService());
const commands = new ExtHostCommands(SingleProxyRPCProtocol(shape), undefined, new NullLogService());
const reg = commands.registerCommand('foo', (): any => { });
reg.dispose();
reg.dispose();

View File

@@ -12,10 +12,11 @@ import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration
import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { TestThreadService } from './testThreadService';
import { TestRPCProtocol } from './testRPCProtocol';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { NullLogService } from 'vs/platform/log/common/log';
suite('ExtHostConfiguration', function () {
@@ -31,7 +32,7 @@ suite('ExtHostConfiguration', function () {
if (!shape) {
shape = new class extends mock<MainThreadConfigurationShape>() { };
}
return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestThreadService(), null), createConfigurationData(contents));
return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService()), createConfigurationData(contents));
}
function createConfigurationData(contents: any): IConfigurationInitData {
@@ -40,7 +41,7 @@ suite('ExtHostConfiguration', function () {
user: new ConfigurationModel(contents),
workspace: new ConfigurationModel(),
folders: Object.create(null),
configurationScopes: []
configurationScopes: {}
};
}
@@ -135,7 +136,7 @@ suite('ExtHostConfiguration', function () {
test('inspect in no workspace context', function () {
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), null),
new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService()),
{
defaults: new ConfigurationModel({
'editor': {
@@ -149,7 +150,7 @@ suite('ExtHostConfiguration', function () {
}, ['editor.wordWrap']),
workspace: new ConfigurationModel({}, []),
folders: Object.create(null),
configurationScopes: []
configurationScopes: {}
}
);
@@ -177,11 +178,11 @@ suite('ExtHostConfiguration', function () {
folders[workspaceUri.toString()] = workspace;
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), {
new ExtHostWorkspace(new TestRPCProtocol(), {
'id': 'foo',
'folders': [aWorkspaceFolder(URI.file('foo'), 0)],
'name': 'foo'
}),
}, new NullLogService()),
{
defaults: new ConfigurationModel({
'editor': {
@@ -195,7 +196,7 @@ suite('ExtHostConfiguration', function () {
}, ['editor.wordWrap']),
workspace,
folders,
configurationScopes: []
configurationScopes: {}
}
);
@@ -250,11 +251,11 @@ suite('ExtHostConfiguration', function () {
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), {
new ExtHostWorkspace(new TestRPCProtocol(), {
'id': 'foo',
'folders': [aWorkspaceFolder(firstRoot, 0), aWorkspaceFolder(secondRoot, 1)],
'name': 'foo'
}),
}, new NullLogService()),
{
defaults: new ConfigurationModel({
'editor': {
@@ -269,7 +270,7 @@ suite('ExtHostConfiguration', function () {
}, ['editor.wordWrap']),
workspace,
folders,
configurationScopes: []
configurationScopes: {}
}
);
@@ -458,11 +459,11 @@ suite('ExtHostConfiguration', function () {
const workspaceFolder = aWorkspaceFolder(URI.file('folder1'), 0);
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), {
new ExtHostWorkspace(new TestRPCProtocol(), {
'id': 'foo',
'folders': [workspaceFolder],
'name': 'foo'
}),
}, new NullLogService()),
createConfigurationData({
'farboo': {
'config': false,

View File

@@ -6,23 +6,22 @@
'use strict';
import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import URI, { UriComponents } from 'vs/base/common/uri';
import Severity from 'vs/base/common/severity';
import { DiagnosticCollection } from 'vs/workbench/api/node/extHostDiagnostics';
import { Diagnostic, DiagnosticSeverity, Range } from 'vs/workbench/api/node/extHostTypes';
import { MainThreadDiagnosticsShape } from 'vs/workbench/api/node/extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostDiagnostics', () => {
class DiagnosticsShape extends mock<MainThreadDiagnosticsShape>() {
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
return TPromise.as(null);
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
//
}
$clear(owner: string): TPromise<any> {
return TPromise.as(null);
$clear(owner: string): void {
//
}
}
@@ -163,9 +162,9 @@ suite('ExtHostDiagnostics', () => {
test('diagnostics collection, set tuple overrides, #11547', function () {
let lastEntries: [URI, IMarkerData[]][];
let lastEntries: [UriComponents, IMarkerData[]][];
let collection = new DiagnosticCollection('test', new class extends DiagnosticsShape {
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
lastEntries = entries;
return super.$changeMany(owner, entries);
}
@@ -237,9 +236,9 @@ suite('ExtHostDiagnostics', () => {
test('diagnostic capping', function () {
let lastEntries: [URI, IMarkerData[]][];
let lastEntries: [UriComponents, IMarkerData[]][];
let collection = new DiagnosticCollection('test', new class extends DiagnosticsShape {
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
lastEntries = entries;
return super.$changeMany(owner, entries);
}

View File

@@ -12,7 +12,7 @@ import { Position } from 'vs/workbench/api/node/extHostTypes';
import { Range } from 'vs/editor/common/core/range';
import { MainThreadDocumentsShape } from 'vs/workbench/api/node/extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorModel';
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';

View File

@@ -10,44 +10,58 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { TextDocumentSaveReason, TextEdit, Position, EndOfLine } from 'vs/workbench/api/node/extHostTypes';
import { MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
import { MainThreadTextEditorsShape, WorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
import { OneGetThreadService } from './testThreadService';
import { SingleProxyRPCProtocol } from './testRPCProtocol';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { NullLogService } from 'vs/platform/log/common/log';
import { isResourceTextEdit, ResourceTextEdit } from 'vs/editor/common/modes';
suite('ExtHostDocumentSaveParticipant', () => {
let resource = URI.parse('foo:bar');
let mainThreadEditors = new class extends mock<MainThreadEditorsShape>() { };
let mainThreadEditors = new class extends mock<MainThreadTextEditorsShape>() { };
let documents: ExtHostDocuments;
let nullLogService = new NullLogService();
let nullExtensionDescription: IExtensionDescription = {
id: 'nullExtensionDescription',
name: 'Null Extension Description',
publisher: 'vscode',
enableProposedApi: false,
engines: undefined,
extensionFolderPath: undefined,
isBuiltin: false,
version: undefined
};
setup(() => {
const documentsAndEditors = new ExtHostDocumentsAndEditors(OneGetThreadService(null));
const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null));
documentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
modeId: 'foo',
url: resource,
uri: resource,
versionId: 1,
lines: ['foo'],
EOL: '\n',
}]
});
documents = new ExtHostDocuments(OneGetThreadService(null), documentsAndEditors);
documents = new ExtHostDocuments(SingleProxyRPCProtocol(null), documentsAndEditors);
});
test('no listeners, no problem', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true));
});
test('event delivery', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let event: vscode.TextDocumentWillSaveEvent;
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
event = e;
});
@@ -61,10 +75,10 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, immutable', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let event: vscode.TextDocumentWillSaveEvent;
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
event = e;
});
@@ -77,9 +91,9 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, bad listener', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
throw new Error('💀');
});
@@ -92,13 +106,13 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, bad listener doesn\'t prevent more events', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
throw new Error('💀');
});
let event: vscode.TextDocumentWillSaveEvent;
let sub2 = participant.onWillSaveTextDocumentEvent(function (e) {
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
event = e;
});
@@ -111,14 +125,14 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, in subscriber order', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let counter = 0;
let sub1 = participant.onWillSaveTextDocumentEvent(function (event) {
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
assert.equal(counter++, 0);
});
let sub2 = participant.onWillSaveTextDocumentEvent(function (event) {
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
assert.equal(counter++, 1);
});
@@ -128,42 +142,39 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
});
test('event delivery, ignore bad listeners', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 5, errors: 1 });
test('event delivery, ignore bad listeners', async () => {
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 5, errors: 1 });
let callCount = 0;
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
callCount += 1;
throw new Error('boom');
});
return TPromise.join([
participant.$participateInSave(resource, SaveReason.EXPLICIT),
participant.$participateInSave(resource, SaveReason.EXPLICIT),
participant.$participateInSave(resource, SaveReason.EXPLICIT),
participant.$participateInSave(resource, SaveReason.EXPLICIT)
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
]).then(values => {
sub.dispose();
assert.equal(callCount, 2);
});
sub.dispose();
assert.equal(callCount, 2);
});
test('event delivery, overall timeout', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 20, errors: 5 });
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 20, errors: 5 });
let callCount = 0;
let sub1 = participant.onWillSaveTextDocumentEvent(function (event) {
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
callCount += 1;
event.waitUntil(TPromise.timeout(17));
});
let sub2 = participant.onWillSaveTextDocumentEvent(function (event) {
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
callCount += 1;
event.waitUntil(TPromise.timeout(17));
});
let sub3 = participant.onWillSaveTextDocumentEvent(function (event) {
let sub3 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
callCount += 1;
});
@@ -178,9 +189,9 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
event.waitUntil(TPromise.timeout(10));
event.waitUntil(TPromise.timeout(10));
@@ -194,9 +205,9 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil must be called sync', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
event.waitUntil(new TPromise((resolve, reject) => {
setTimeout(() => {
@@ -217,9 +228,9 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil will timeout', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors, { timeout: 5, errors: 3 });
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 5, errors: 3 });
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
event.waitUntil(TPromise.timeout(15));
});
@@ -232,14 +243,14 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
test('event delivery, waitUntil failure handling', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, mainThreadEditors);
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
e.waitUntil(TPromise.wrapError(new Error('dddd')));
});
let event: vscode.TextDocumentWillSaveEvent;
let sub2 = participant.onWillSaveTextDocumentEvent(function (e) {
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
event = e;
});
@@ -252,15 +263,15 @@ suite('ExtHostDocumentSaveParticipant', () => {
test('event delivery, pushEdits sync', () => {
let edits: IWorkspaceResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: IWorkspaceResourceEdit[]) {
edits = _edits;
let dto: WorkspaceEditDto;
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, new class extends mock<MainThreadTextEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: WorkspaceEditDto) {
dto = _edits;
return TPromise.as(true);
}
});
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')]));
e.waitUntil(TPromise.as([TextEdit.setEndOfLine(EndOfLine.CRLF)]));
});
@@ -268,25 +279,26 @@ suite('ExtHostDocumentSaveParticipant', () => {
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.equal(edits.length, 1);
assert.equal(edits[0].edits.length, 2);
assert.equal(dto.edits.length, 1);
assert.ok(isResourceTextEdit(dto.edits[0]));
assert.equal((<ResourceTextEdit>dto.edits[0]).edits.length, 2);
});
});
test('event delivery, concurrent change', () => {
let edits: IWorkspaceResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: IWorkspaceResourceEdit[]) {
let edits: WorkspaceEditDto;
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, new class extends mock<MainThreadTextEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: WorkspaceEditDto) {
edits = _edits;
return TPromise.as(true);
}
});
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
// concurrent change from somewhere
documents.$acceptModelChanged(resource.toString(), {
documents.$acceptModelChanged(resource, {
changes: [{
range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 },
rangeLength: undefined,
@@ -310,19 +322,24 @@ suite('ExtHostDocumentSaveParticipant', () => {
test('event delivery, two listeners -> two document states', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_edits: IWorkspaceResourceEdit[]) {
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, new class extends mock<MainThreadTextEditorsShape>() {
$tryApplyWorkspaceEdit(dto: WorkspaceEditDto) {
for (const { resource, edits } of _edits) {
for (const { newText, range } of edits) {
documents.$acceptModelChanged(resource.toString(), {
for (const edit of dto.edits) {
if (!isResourceTextEdit(edit)) {
continue;
}
const { resource, edits } = edit;
const uri = URI.revive(resource);
for (const { text, range } of edits) {
documents.$acceptModelChanged(uri, {
changes: [{
range,
text,
rangeLength: undefined,
text: newText
}],
eol: undefined,
versionId: documents.getDocumentData(resource).version + 1
versionId: documents.getDocumentData(uri).version + 1
}, true);
}
}
@@ -333,7 +350,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
const document = documents.getDocumentData(resource).document;
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
// the document state we started with
assert.equal(document.version, 1);
assert.equal(document.getText(), 'foo');
@@ -341,7 +358,7 @@ suite('ExtHostDocumentSaveParticipant', () => {
e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')]));
});
let sub2 = participant.onWillSaveTextDocumentEvent(function (e) {
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
// the document state AFTER the first listener kicked in
assert.equal(document.version, 2);
assert.equal(document.getText(), 'barfoo');
@@ -359,4 +376,23 @@ suite('ExtHostDocumentSaveParticipant', () => {
});
});
test('Log failing listener', function () {
let didLogSomething = false;
let participant = new ExtHostDocumentSaveParticipant(new class extends NullLogService {
error(message: string | Error, ...args: any[]): void {
didLogSomething = true;
}
}, documents, mainThreadEditors);
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
throw new Error('boom');
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.equal(didLogSomething, true);
});
});
});

View File

@@ -8,8 +8,6 @@
import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { TPromise } from 'vs/base/common/winjs.base';
suite('ExtHostDocumentsAndEditors', () => {
@@ -17,7 +15,9 @@ suite('ExtHostDocumentsAndEditors', () => {
setup(function () {
editors = new ExtHostDocumentsAndEditors({
get() { return undefined; }
getProxy: () => { return undefined; },
set: undefined,
assertRegistered: undefined
});
});
@@ -28,7 +28,7 @@ suite('ExtHostDocumentsAndEditors', () => {
EOL: '\n',
isDirty: true,
modeId: 'fooLang',
url: URI.parse('foo:bar'),
uri: URI.parse('foo:bar'),
versionId: 1,
lines: [
'first',
@@ -37,7 +37,7 @@ suite('ExtHostDocumentsAndEditors', () => {
}]
});
return new TPromise((resolve, reject) => {
return new Promise((resolve, reject) => {
editors.onDidRemoveDocuments(e => {
try {
@@ -52,7 +52,7 @@ suite('ExtHostDocumentsAndEditors', () => {
});
editors.$acceptDocumentsAndEditorsDelta({
removedDocuments: ['foo:bar']
removedDocuments: [URI.parse('foo:bar')]
});
});

View File

@@ -10,11 +10,10 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import * as types from 'vs/workbench/api/node/extHostTypes';
import * as EditorCommon from 'vs/editor/common/editorCommon';
import { Model as EditorModel } from 'vs/editor/common/model/model';
import { TextModel as EditorModel } from 'vs/editor/common/model/textModel';
import { Position as EditorPosition } from 'vs/editor/common/core/position';
import { Range as EditorRange } from 'vs/editor/common/core/range';
import { TestThreadService } from './testThreadService';
import { TestRPCProtocol } from './testRPCProtocol';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
@@ -25,7 +24,7 @@ import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapSe
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/quickOpen';
import { DocumentSymbolProviderRegistry, DocumentHighlightKind, Hover } from 'vs/editor/common/modes';
import { DocumentSymbolProviderRegistry, DocumentHighlightKind, Hover, ResourceTextEdit } from 'vs/editor/common/modes';
import { getCodeLensData } from 'vs/editor/contrib/codelens/codelens';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from 'vs/editor/contrib/goToDeclaration/goToDeclaration';
import { getHover } from 'vs/editor/contrib/hover/getHover';
@@ -44,10 +43,11 @@ import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import * as vscode from 'vscode';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { NoopLogService } from 'vs/platform/log/common/log';
import { NullLogService } from 'vs/platform/log/common/log';
import { ITextModel, EndOfLineSequence } from 'vs/editor/common/model';
const defaultSelector = { scheme: 'far' };
const model: EditorCommon.IModel = EditorModel.createFromString(
const model: ITextModel = EditorModel.createFromString(
[
'This is the first line',
'This is the second line',
@@ -60,14 +60,14 @@ const model: EditorCommon.IModel = EditorModel.createFromString(
let extHost: ExtHostLanguageFeatures;
let mainThread: MainThreadLanguageFeatures;
let disposables: vscode.Disposable[] = [];
let threadService: TestThreadService;
let rpcProtocol: TestRPCProtocol;
let originalErrorHandler: (e: any) => any;
suite('ExtHostLanguageFeatures', function () {
suiteSetup(() => {
threadService = new TestThreadService();
rpcProtocol = new TestRPCProtocol();
// Use IInstantiationService to get typechecking when instantiating
let inst: IInstantiationService;
@@ -87,33 +87,33 @@ suite('ExtHostLanguageFeatures', function () {
originalErrorHandler = errorHandler.getUnexpectedErrorHandler();
setUnexpectedErrorHandler(() => { });
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService);
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol);
extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
versionId: model.getVersionId(),
modeId: model.getLanguageIdentifier().language,
url: model.uri,
uri: model.uri,
lines: model.getValue().split(model.getEOL()),
EOL: model.getEOL(),
}]
});
const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors);
threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors);
rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
const heapService = new ExtHostHeapService();
const commands = new ExtHostCommands(threadService, heapService, new NoopLogService());
threadService.set(ExtHostContext.ExtHostCommands, commands);
threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService));
const commands = new ExtHostCommands(rpcProtocol, heapService, new NullLogService());
rpcProtocol.set(ExtHostContext.ExtHostCommands, commands);
rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol));
const diagnostics = new ExtHostDiagnostics(threadService);
threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
const diagnostics = new ExtHostDiagnostics(rpcProtocol);
rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, heapService, diagnostics);
threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
extHost = new ExtHostLanguageFeatures(rpcProtocol, extHostDocuments, commands, heapService, diagnostics);
rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
mainThread = <MainThreadLanguageFeatures>threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, threadService));
mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, rpcProtocol));
});
suiteTeardown(() => {
@@ -126,7 +126,7 @@ suite('ExtHostLanguageFeatures', function () {
while (disposables.length) {
disposables.pop().dispose();
}
return threadService.sync();
return rpcProtocol.sync();
});
// --- outline
@@ -139,10 +139,10 @@ suite('ExtHostLanguageFeatures', function () {
}
});
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
assert.equal(DocumentSymbolProviderRegistry.all(model).length, 1);
d1.dispose();
return threadService.sync();
return rpcProtocol.sync();
});
});
@@ -159,7 +159,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentSymbols(model).then(value => {
assert.equal(value.entries.length, 1);
@@ -174,7 +174,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentSymbols(model).then(value => {
assert.equal(value.entries.length, 1);
@@ -201,7 +201,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getCodeLensData(model).then(value => {
assert.equal(value.length, 1);
});
@@ -221,7 +221,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getCodeLensData(model).then(value => {
assert.equal(value.length, 1);
@@ -245,7 +245,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getCodeLensData(model).then(value => {
assert.equal(value.length, 1);
@@ -272,7 +272,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 1);
@@ -296,7 +296,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 2);
@@ -318,7 +318,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 2);
@@ -343,7 +343,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 1);
@@ -361,7 +361,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getImplementationsAtPosition(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 1);
let [entry] = value;
@@ -381,7 +381,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getTypeDefinitionsAtPosition(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 1);
let [entry] = value;
@@ -401,7 +401,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
getHover(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 1);
let [entry] = value;
@@ -419,7 +419,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
getHover(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 1);
@@ -444,7 +444,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getHover(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value.length, 2);
let [first, second] = value as Hover[];
@@ -468,7 +468,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
getHover(model, new EditorPosition(1, 1)).then(value => {
@@ -487,7 +487,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 1);
@@ -511,7 +511,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 1);
@@ -535,7 +535,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 1);
@@ -560,7 +560,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getOccurrencesAtPosition(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 1);
@@ -584,7 +584,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideReferences(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 2);
@@ -604,7 +604,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideReferences(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 1);
@@ -630,7 +630,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideReferences(model, new EditorPosition(1, 2)).then(value => {
assert.equal(value.length, 1);
@@ -641,7 +641,7 @@ suite('ExtHostLanguageFeatures', function () {
// --- quick fix
test('Quick Fix, data conversion', function () {
test('Quick Fix, command data conversion', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, {
provideCodeActions(): vscode.Command[] {
@@ -652,7 +652,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getCodeActions(model, model.getFullModelRange()).then(value => {
assert.equal(value.length, 2);
@@ -665,6 +665,34 @@ suite('ExtHostLanguageFeatures', function () {
});
});
test('Quick Fix, code action data conversion', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, {
provideCodeActions(): vscode.CodeAction[] {
return [
{
title: 'Testing1',
command: { title: 'Testing1Command', command: 'test1' },
kind: types.CodeActionKind.Empty.append('test.scope')
}
];
}
}));
return rpcProtocol.sync().then(() => {
return getCodeActions(model, model.getFullModelRange()).then(value => {
assert.equal(value.length, 1);
const [first] = value;
assert.equal(first.title, 'Testing1');
assert.equal(first.command.title, 'Testing1Command');
assert.equal(first.command.id, 'test1');
assert.equal(first.kind, 'test.scope');
});
});
});
test('Cannot read property \'id\' of undefined, #29469', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, <vscode.CodeActionProvider>{
@@ -677,7 +705,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getCodeActions(model, model.getFullModelRange()).then(value => {
assert.equal(value.length, 1);
});
@@ -697,7 +725,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getCodeActions(model, model.getFullModelRange()).then(value => {
assert.equal(value.length, 1);
});
@@ -720,7 +748,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getWorkspaceSymbols('').then(value => {
assert.equal(value.length, 1);
@@ -742,7 +770,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return rename(model, new EditorPosition(1, 1), 'newName').then(value => {
throw Error();
@@ -760,7 +788,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return rename(model, new EditorPosition(1, 1), 'newName').then(value => {
assert.equal(value.rejectReason, 'evil');
@@ -784,7 +812,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return rename(model, new EditorPosition(1, 1), 'newName').then(value => {
assert.equal(value.edits.length, 1);
@@ -809,10 +837,11 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return rename(model, new EditorPosition(1, 1), 'newName').then(value => {
assert.equal(value.edits.length, 2); // least relevant renamer
assert.equal(value.edits.length, 1); // least relevant renamer
assert.equal((<ResourceTextEdit[]>value.edits)[0].edits.length, 2); // least relevant renamer
});
});
});
@@ -837,7 +866,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideSignatureHelp(model, new EditorPosition(1, 1)).then(value => {
assert.ok(value);
@@ -852,7 +881,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideSignatureHelp(model, new EditorPosition(1, 1)).then(value => {
assert.equal(value, undefined);
@@ -876,7 +905,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => {
assert.equal(value.length, 1);
assert.equal(value[0].suggestion.insertText, 'testing2');
@@ -898,7 +927,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => {
assert.equal(value.length, 1);
assert.equal(value[0].suggestion.insertText, 'weak-selector');
@@ -920,7 +949,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => {
assert.equal(value.length, 2);
assert.equal(value[0].suggestion.insertText, 'strong-1'); // sort by label
@@ -944,7 +973,7 @@ suite('ExtHostLanguageFeatures', function () {
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => {
assert.equal(value[0].container.incomplete, undefined);
@@ -960,7 +989,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, []));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => {
assert.equal(value[0].container.incomplete, true);
@@ -977,14 +1006,14 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => {
assert.equal(value.length, 2);
let [first, second] = value;
assert.equal(first.text, 'testing');
assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 });
assert.equal(second.eol, EditorCommon.EndOfLineSequence.LF);
assert.equal(second.eol, EndOfLineSequence.LF);
assert.equal(second.text, '');
assert.equal(second.range, undefined);
});
@@ -998,7 +1027,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 });
});
});
@@ -1016,7 +1045,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -1033,7 +1062,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -1054,7 +1083,7 @@ suite('ExtHostLanguageFeatures', function () {
return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'doc')];
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -1070,7 +1099,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 });
});
});
@@ -1083,7 +1112,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}, [';']));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getOnTypeFormattingEdits(model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -1102,7 +1131,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getLinks(model).then(value => {
assert.equal(value.length, 1);
let [first] = value;
@@ -1127,7 +1156,7 @@ suite('ExtHostLanguageFeatures', function () {
}
}));
return threadService.sync().then(() => {
return rpcProtocol.sync().then(() => {
return getLinks(model).then(value => {
assert.equal(value.length, 1);
let [first] = value;

View File

@@ -6,91 +6,83 @@
'use strict';
import * as assert from 'assert';
import { Action } from 'vs/base/common/actions';
import { MainThreadMessageService } from 'vs/workbench/api/electron-browser/mainThreadMessageService';
import { TPromise as Promise } from 'vs/base/common/winjs.base';
import { IMessageService, IChoiceService } from 'vs/platform/message/common/message';
import { TPromise as Promise, TPromise } from 'vs/base/common/winjs.base';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService, INotification, NoOpNotification, INotificationHandle } from 'vs/platform/notification/common/notification';
import { ICommandService } from 'vs/platform/commands/common/commands';
const emptyChoiceService = new class implements IChoiceService {
_serviceBrand: 'choiceService';
choose(severity, message, options, modal): never {
throw new Error('not implemented');
}
};
const emptyCommandService: ICommandService = {
_serviceBrand: undefined,
onWillExecuteCommand: () => ({ dispose: () => { } }),
executeCommand: (commandId: string, ...args: any[]): TPromise<any> => {
return TPromise.as(void 0);
}
};
const emptyNotificationService = new class implements INotificationService {
_serviceBrand: 'notificiationService';
notify(...args: any[]): never {
throw new Error('not implemented');
}
info(...args: any[]): never {
throw new Error('not implemented');
}
warn(...args: any[]): never {
throw new Error('not implemented');
}
error(...args: any[]): never {
throw new Error('not implemented');
}
};
class EmptyNotificationService implements INotificationService {
_serviceBrand: any;
constructor(private withNotify: (notification: INotification) => void) {
}
notify(notification: INotification): INotificationHandle {
this.withNotify(notification);
return new NoOpNotification();
}
info(message: any): void {
throw new Error('Method not implemented.');
}
warn(message: any): void {
throw new Error('Method not implemented.');
}
error(message: any): void {
throw new Error('Method not implemented.');
}
}
suite('ExtHostMessageService', function () {
test('propagte handle on select', function () {
let service = new MainThreadMessageService(null, {
show(sev: number, m: { actions: Action[] }) {
assert.equal(m.actions.length, 1);
setImmediate(() => m.actions[0].run());
return () => { };
}
} as IMessageService, {
choose(severity, message, options, modal) {
throw new Error('not implemented');
}
} as IChoiceService);
let service = new MainThreadMessageService(null, new EmptyNotificationService(notification => {
assert.equal(notification.actions.primary.length, 1);
setImmediate(() => notification.actions.primary[0].run());
}), emptyCommandService, emptyChoiceService, null);
return service.$showMessage(1, 'h', {}, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]).then(handle => {
assert.equal(handle, 42);
});
});
test('isCloseAffordance', function () {
let actions: Action[];
let service = new MainThreadMessageService(null, {
show(sev: number, m: { actions: Action[] }) {
actions = m.actions;
}
} as IMessageService, {
choose(severity, message, options, modal) {
throw new Error('not implemented');
}
} as IChoiceService);
// default close action
service.$showMessage(1, '', {}, [{ title: 'a thing', isCloseAffordance: false, handle: 0 }]);
assert.equal(actions.length, 2);
let [first, second] = actions;
assert.equal(first.label, 'a thing');
assert.equal(second.label, 'Close');
// override close action
service.$showMessage(1, '', {}, [{ title: 'a thing', isCloseAffordance: true, handle: 0 }]);
assert.equal(actions.length, 1);
first = actions[0];
assert.equal(first.label, 'a thing');
});
test('hide on select', function () {
let actions: Action[];
let c: number;
let service = new MainThreadMessageService(null, {
show(sev: number, m: { actions: Action[] }) {
c = 0;
actions = m.actions;
return () => {
c += 1;
};
}
} as IMessageService, {
choose(severity, message, options, modal) {
throw new Error('not implemented');
}
} as IChoiceService);
service.$showMessage(1, '', {}, [{ title: 'a thing', isCloseAffordance: true, handle: 0 }]);
assert.equal(actions.length, 1);
actions[0].run();
assert.equal(c, 1);
});
suite('modal', () => {
test('calls choice service', () => {
const service = new MainThreadMessageService(null, {
show(sev: number, m: { actions: Action[] }) {
throw new Error('not implemented');
}
} as IMessageService, {
const service = new MainThreadMessageService(null, emptyNotificationService, emptyCommandService, {
choose(severity, message, options, modal) {
assert.equal(severity, 1);
assert.equal(message, 'h');
@@ -98,7 +90,7 @@ suite('ExtHostMessageService', function () {
assert.equal(options[1], 'Cancel');
return Promise.as(0);
}
} as IChoiceService);
} as IChoiceService, null);
return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]).then(handle => {
assert.equal(handle, 42);
@@ -106,15 +98,11 @@ suite('ExtHostMessageService', function () {
});
test('returns undefined when cancelled', () => {
const service = new MainThreadMessageService(null, {
show(sev: number, m: { actions: Action[] }) {
throw new Error('not implemented');
}
} as IMessageService, {
const service = new MainThreadMessageService(null, emptyNotificationService, emptyCommandService, {
choose(severity, message, options, modal) {
return Promise.as(1);
}
} as IChoiceService);
} as IChoiceService, null);
return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]).then(handle => {
assert.equal(handle, undefined);
@@ -122,16 +110,12 @@ suite('ExtHostMessageService', function () {
});
test('hides Cancel button when not needed', () => {
const service = new MainThreadMessageService(null, {
show(sev: number, m: { actions: Action[] }) {
throw new Error('not implemented');
}
} as IMessageService, {
const service = new MainThreadMessageService(null, emptyNotificationService, emptyCommandService, {
choose(severity, message, options, modal) {
assert.equal(options.length, 1);
return Promise.as(0);
}
} as IChoiceService);
} as IChoiceService, null);
return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]).then(handle => {
assert.equal(handle, 42);

View File

@@ -8,7 +8,7 @@ import * as assert from 'assert';
import { TPromise } from 'vs/base/common/winjs.base';
import { TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { MainThreadEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/extHost.protocol';
import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/node/extHostTextEditor';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import URI from 'vs/base/common/uri';
@@ -21,7 +21,7 @@ suite('ExtHostTextEditor', () => {
let doc = new ExtHostDocumentData(undefined, URI.file(''), [
'aaaa bbbb+cccc abc'
], '\n', 'text', 1, false);
editor = new ExtHostTextEditor(null, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, 1);
editor = new ExtHostTextEditor(null, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4 }, [], 1);
});
test('disposed editor', () => {
@@ -48,7 +48,7 @@ suite('ExtHostTextEditorOptions', () => {
setup(() => {
calls = [];
let mockProxy: MainThreadEditorsShape = {
let mockProxy: MainThreadTextEditorsShape = {
dispose: undefined,
$trySetOptions: (id: string, options: ITextEditorConfigurationUpdate) => {
assert.equal(id, '1');

View File

@@ -7,49 +7,50 @@
import * as assert from 'assert';
import { TPromise } from 'vs/base/common/winjs.base';
import * as extHostTypes from 'vs/workbench/api/node/extHostTypes';
import { MainContext, MainThreadEditorsShape, IWorkspaceResourceEdit } from 'vs/workbench/api/node/extHost.protocol';
import { MainContext, MainThreadTextEditorsShape, WorkspaceEditDto } from 'vs/workbench/api/node/extHost.protocol';
import URI from 'vs/base/common/uri';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { OneGetThreadService, TestThreadService } from 'vs/workbench/test/electron-browser/api/testThreadService';
import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol';
import { ExtHostEditors } from 'vs/workbench/api/node/extHostTextEditors';
import { ResourceTextEdit } from 'vs/editor/common/modes';
suite('ExtHostTextEditors.applyWorkspaceEdit', () => {
const resource = URI.parse('foo:bar');
let editors: ExtHostEditors;
let workspaceResourceEdits: IWorkspaceResourceEdit[];
let workspaceResourceEdits: WorkspaceEditDto;
setup(() => {
workspaceResourceEdits = null;
let threadService = new TestThreadService();
threadService.setTestInstance(MainContext.MainThreadEditors, new class extends mock<MainThreadEditorsShape>() {
$tryApplyWorkspaceEdit(_workspaceResourceEdits: IWorkspaceResourceEdit[]): TPromise<boolean> {
let rpcProtocol = new TestRPCProtocol();
rpcProtocol.set(MainContext.MainThreadTextEditors, new class extends mock<MainThreadTextEditorsShape>() {
$tryApplyWorkspaceEdit(_workspaceResourceEdits: WorkspaceEditDto): TPromise<boolean> {
workspaceResourceEdits = _workspaceResourceEdits;
return TPromise.as(true);
}
});
const documentsAndEditors = new ExtHostDocumentsAndEditors(OneGetThreadService(null));
const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null));
documentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
modeId: 'foo',
url: resource,
uri: resource,
versionId: 1337,
lines: ['foo'],
EOL: '\n',
}]
});
editors = new ExtHostEditors(threadService, documentsAndEditors);
editors = new ExtHostEditors(rpcProtocol, documentsAndEditors);
});
test('uses version id if document available', () => {
let edit = new extHostTypes.WorkspaceEdit();
edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello');
return editors.applyWorkspaceEdit(edit).then((result) => {
assert.equal(workspaceResourceEdits.length, 1);
assert.equal(workspaceResourceEdits[0].modelVersionId, 1337);
assert.equal(workspaceResourceEdits.edits.length, 1);
assert.equal((<ResourceTextEdit>workspaceResourceEdits.edits[0]).modelVersionId, 1337);
});
});
@@ -57,8 +58,8 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => {
let edit = new extHostTypes.WorkspaceEdit();
edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello');
return editors.applyWorkspaceEdit(edit).then((result) => {
assert.equal(workspaceResourceEdits.length, 1);
assert.ok(typeof workspaceResourceEdits[0].modelVersionId === 'undefined');
assert.equal(workspaceResourceEdits.edits.length, 1);
assert.ok(typeof (<ResourceTextEdit>workspaceResourceEdits.edits[0]).modelVersionId === 'undefined');
});
});

View File

@@ -6,12 +6,13 @@
'use strict';
import * as assert from 'assert';
import * as sinon from 'sinon';
import { Emitter } from 'vs/base/common/event';
import { ExtHostTreeViews } from 'vs/workbench/api/node/extHostTreeViews';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { MainThreadTreeViewsShape, MainContext } from 'vs/workbench/api/node/extHost.protocol';
import { TreeDataProvider, TreeItem } from 'vscode';
import { TestThreadService } from './testThreadService';
import { TestRPCProtocol } from './testRPCProtocol';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands';
@@ -19,7 +20,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { TPromise } from 'vs/base/common/winjs.base';
import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views';
import { NoopLogService } from 'vs/platform/log/common/log';
import { NullLogService } from 'vs/platform/log/common/log';
suite('ExtHostTreeView', function () {
@@ -27,18 +28,23 @@ suite('ExtHostTreeView', function () {
onRefresh = new Emitter<{ [treeItemHandle: string]: ITreeItem }>();
$registerView(treeViewId: string): void {
$registerTreeViewDataProvider(treeViewId: string): void {
}
$refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): void {
this.onRefresh.fire(itemsToRefresh);
}
$reveal(): TPromise<void> {
return null;
}
}
let testObject: ExtHostTreeViews;
let target: RecordingShape;
let onDidChangeTreeNode: Emitter<{ key: string }>;
let onDidChangeTreeKey: Emitter<string>;
let onDidChangeTreeNodeWithId: Emitter<{ key: string }>;
let tree, labels, nodes;
setup(() => {
@@ -56,7 +62,7 @@ suite('ExtHostTreeView', function () {
labels = {};
nodes = {};
let threadService = new TestThreadService();
let rpcProtocol = new TestRPCProtocol();
// Use IInstantiationService to get typechecking when instantiating
let inst: IInstantiationService;
{
@@ -64,15 +70,15 @@ suite('ExtHostTreeView', function () {
inst = instantiationService;
}
threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService));
rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol));
target = new RecordingShape();
testObject = new ExtHostTreeViews(target, new ExtHostCommands(threadService, new ExtHostHeapService(), new NoopLogService()));
testObject = new ExtHostTreeViews(target, new ExtHostCommands(rpcProtocol, new ExtHostHeapService(), new NullLogService()));
onDidChangeTreeNode = new Emitter<{ key: string }>();
onDidChangeTreeKey = new Emitter<string>();
testObject.registerTreeDataProvider('testNodeTreeProvider', aNodeTreeDataProvider());
testObject.registerTreeDataProvider('testStringTreeProvider', aStringTreeDataProvider());
onDidChangeTreeNodeWithId = new Emitter<{ key: string }>();
testObject.registerTreeDataProvider('testNodeTreeProvider', aNodeTreeDataProvider(), (fn) => fn);
testObject.registerTreeDataProvider('testNodeWithIdTreeProvider', aNodeWithIdTreeDataProvider(), (fn) => fn);
testObject.$getElements('testNodeTreeProvider').then(elements => {
testObject.$getChildren('testNodeTreeProvider').then(elements => {
for (const element of elements) {
testObject.$getChildren('testNodeTreeProvider', element.handle);
}
@@ -80,61 +86,82 @@ suite('ExtHostTreeView', function () {
});
test('construct node tree', () => {
return testObject.$getElements('testNodeTreeProvider')
return testObject.$getChildren('testNodeTreeProvider')
.then(elements => {
const actuals = elements.map(e => e.handle);
assert.deepEqual(actuals, ['0/0:a', '0/1:b']);
assert.deepEqual(actuals, ['0/0:a', '0/0:b']);
return TPromise.join([
testObject.$getChildren('testNodeTreeProvider', '0/0:a')
.then(children => {
const actuals = children.map(e => e.handle);
assert.deepEqual(actuals, ['0/0:a/0:aa', '0/0:a/1:ab']);
assert.deepEqual(actuals, ['0/0:a/0:aa', '0/0:a/0:ab']);
return TPromise.join([
testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:aa').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testNodeTreeProvider', '0/0:a/1:ab').then(children => assert.equal(children.length, 0))
testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:ab').then(children => assert.equal(children.length, 0))
]);
}),
testObject.$getChildren('testNodeTreeProvider', '0/1:b')
testObject.$getChildren('testNodeTreeProvider', '0/0:b')
.then(children => {
const actuals = children.map(e => e.handle);
assert.deepEqual(actuals, ['0/1:b/0:ba', '0/1:b/1:bb']);
assert.deepEqual(actuals, ['0/0:b/0:ba', '0/0:b/0:bb']);
return TPromise.join([
testObject.$getChildren('testNodeTreeProvider', '0/1:b/0:ba').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testNodeTreeProvider', '0/1:b/1:bb').then(children => assert.equal(children.length, 0))
testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:ba').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:bb').then(children => assert.equal(children.length, 0))
]);
})
]);
});
});
test('construct string tree', () => {
return testObject.$getElements('testStringTreeProvider')
test('construct id tree', () => {
return testObject.$getChildren('testNodeWithIdTreeProvider')
.then(elements => {
const actuals = elements.map(e => e.handle);
assert.deepEqual(actuals, ['a', 'b']);
assert.deepEqual(actuals, ['1/a', '1/b']);
return TPromise.join([
testObject.$getChildren('testStringTreeProvider', 'a')
testObject.$getChildren('testNodeWithIdTreeProvider', '1/a')
.then(children => {
const actuals = children.map(e => e.handle);
assert.deepEqual(actuals, ['aa', 'ab']);
assert.deepEqual(actuals, ['1/aa', '1/ab']);
return TPromise.join([
testObject.$getChildren('testStringTreeProvider', 'aa').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testStringTreeProvider', 'ab').then(children => assert.equal(children.length, 0))
testObject.$getChildren('testNodeWithIdTreeProvider', '1/aa').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testNodeWithIdTreeProvider', '1/ab').then(children => assert.equal(children.length, 0))
]);
}),
testObject.$getChildren('testStringTreeProvider', 'b')
testObject.$getChildren('testNodeWithIdTreeProvider', '1/b')
.then(children => {
const actuals = children.map(e => e.handle);
assert.deepEqual(actuals, ['ba', 'bb']);
assert.deepEqual(actuals, ['1/ba', '1/bb']);
return TPromise.join([
testObject.$getChildren('testStringTreeProvider', 'ba').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testStringTreeProvider', 'bb').then(children => assert.equal(children.length, 0))
testObject.$getChildren('testNodeWithIdTreeProvider', '1/ba').then(children => assert.equal(children.length, 0)),
testObject.$getChildren('testNodeWithIdTreeProvider', '1/bb').then(children => assert.equal(children.length, 0))
]);
})
]);
});
});
test('error is thrown if id is not unique', (done) => {
tree['a'] = {
'aa': {},
};
tree['b'] = {
'aa': {},
'ba': {}
};
target.onRefresh.event(() => {
testObject.$getChildren('testNodeWithIdTreeProvider')
.then(elements => {
const actuals = elements.map(e => e.handle);
assert.deepEqual(actuals, ['1/a', '1/b']);
return testObject.$getChildren('testNodeWithIdTreeProvider', '1/a')
.then(() => testObject.$getChildren('testNodeWithIdTreeProvider', '1/b'))
.then(() => { assert.fail('Should fail with duplicate id'); done(); }, () => done());
});
});
onDidChangeTreeNode.fire();
});
test('refresh root', function (done) {
target.onRefresh.event(actuals => {
assert.equal(undefined, actuals);
@@ -146,10 +173,11 @@ suite('ExtHostTreeView', function () {
test('refresh a parent node', () => {
return new TPromise((c, e) => {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/1:b'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/1:b']), {
handle: '0/1:b',
assert.deepEqual(['0/0:b'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
handle: '0/0:b',
label: 'b',
collapsibleState: TreeItemCollapsibleState.Collapsed
});
c(null);
});
@@ -159,11 +187,12 @@ suite('ExtHostTreeView', function () {
test('refresh a leaf node', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/1:b/1:bb'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/1:b/1:bb']), {
handle: '0/1:b/1:bb',
parentHandle: '0/1:b',
label: 'bb'
assert.deepEqual(['0/0:b/0:bb'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/0:b/0:bb']), {
handle: '0/0:b/0:bb',
parentHandle: '0/0:b',
label: 'bb',
collapsibleState: TreeItemCollapsibleState.None
});
done();
});
@@ -172,15 +201,17 @@ suite('ExtHostTreeView', function () {
test('refresh parent and child node trigger refresh only on parent - scenario 1', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/1:b', '0/0:a/0:aa'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/1:b']), {
handle: '0/1:b',
assert.deepEqual(['0/0:b', '0/0:a/0:aa'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
handle: '0/0:b',
label: 'b',
collapsibleState: TreeItemCollapsibleState.Collapsed
});
assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), {
handle: '0/0:a/0:aa',
parentHandle: '0/0:a',
label: 'aa',
collapsibleState: TreeItemCollapsibleState.None
});
done();
});
@@ -191,15 +222,17 @@ suite('ExtHostTreeView', function () {
test('refresh parent and child node trigger refresh only on parent - scenario 2', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/0:a/0:aa', '0/1:b'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/1:b']), {
handle: '0/1:b',
assert.deepEqual(['0/0:a/0:aa', '0/0:b'], Object.keys(actuals));
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
handle: '0/0:b',
label: 'b',
collapsibleState: TreeItemCollapsibleState.Collapsed
});
assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), {
handle: '0/0:a/0:aa',
parentHandle: '0/0:a',
label: 'aa',
collapsibleState: TreeItemCollapsibleState.None
});
done();
});
@@ -215,6 +248,7 @@ suite('ExtHostTreeView', function () {
assert.deepEqual(removeUnsetKeys(actuals['0/0:a']), {
handle: '0/0:aa',
label: 'aa',
collapsibleState: TreeItemCollapsibleState.Collapsed
});
done();
});
@@ -234,7 +268,7 @@ suite('ExtHostTreeView', function () {
test('refresh calls are throttled on elements', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/0:a', '0/1:b'], Object.keys(actuals));
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
done();
});
@@ -246,7 +280,7 @@ suite('ExtHostTreeView', function () {
test('refresh calls are throttled on unknown elements', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/0:a', '0/1:b'], Object.keys(actuals));
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
done();
});
@@ -280,16 +314,148 @@ suite('ExtHostTreeView', function () {
onDidChangeTreeNode.fire(getNode('a'));
});
test('generate unique handles from labels by escaping them', () => {
test('generate unique handles from labels by escaping them', (done) => {
tree = {
'a/0:b': {}
};
onDidChangeTreeNode.fire();
target.onRefresh.event(() => {
testObject.$getChildren('testNodeTreeProvider')
.then(elements => {
assert.deepEqual(elements.map(e => e.handle), ['0/0:a//0:b']);
done();
});
});
});
return testObject.$getElements('testNodeTreeProvider')
test('tree with duplicate labels', (done) => {
const dupItems = {
'adup1': 'c',
'adup2': 'g',
'bdup1': 'e',
'hdup1': 'i',
'hdup2': 'l',
'jdup1': 'k'
};
labels['c'] = 'a';
labels['e'] = 'b';
labels['g'] = 'a';
labels['i'] = 'h';
labels['l'] = 'h';
labels['k'] = 'j';
tree[dupItems['adup1']] = {};
tree['d'] = {};
const bdup1Tree = {};
bdup1Tree['h'] = {};
bdup1Tree[dupItems['hdup1']] = {};
bdup1Tree['j'] = {};
bdup1Tree[dupItems['jdup1']] = {};
bdup1Tree[dupItems['hdup2']] = {};
tree[dupItems['bdup1']] = bdup1Tree;
tree['f'] = {};
tree[dupItems['adup2']] = {};
onDidChangeTreeNode.fire();
target.onRefresh.event(() => {
testObject.$getChildren('testNodeTreeProvider')
.then(elements => {
const actuals = elements.map(e => e.handle);
assert.deepEqual(actuals, ['0/0:a', '0/0:b', '0/1:a', '0/0:d', '0/1:b', '0/0:f', '0/2:a']);
return testObject.$getChildren('testNodeTreeProvider', '0/1:b')
.then(elements => {
const actuals = elements.map(e => e.handle);
assert.deepEqual(actuals, ['0/1:b/0:h', '0/1:b/1:h', '0/1:b/0:j', '0/1:b/1:j', '0/1:b/2:h']);
done();
});
});
});
});
test('getChildren is not returned from cache if refreshed', (done) => {
tree = {
'c': {}
};
onDidChangeTreeNode.fire();
target.onRefresh.event(() => {
testObject.$getChildren('testNodeTreeProvider')
.then(elements => {
assert.deepEqual(elements.map(e => e.handle), ['0/0:c']);
done();
});
});
});
test('getChildren is returned from cache if not refreshed', () => {
tree = {
'c': {}
};
return testObject.$getChildren('testNodeTreeProvider')
.then(elements => {
assert.deepEqual(elements.map(e => e.handle), ['0/0:a//0:b']);
assert.deepEqual(elements.map(e => e.handle), ['0/0:a', '0/0:b']);
});
});
test('reveal will throw an error if getParent is not implemented', () => {
const treeView = testObject.registerTreeDataProvider('treeDataProvider', aNodeTreeDataProvider(), (fn) => fn);
return treeView.reveal({ key: 'a' })
.then(() => assert.fail('Reveal should throw an error as getParent is not implemented'), () => null);
});
test('reveal will return empty array for root element', () => {
const revealTarget = sinon.spy(target, '$reveal');
const treeView = testObject.registerTreeDataProvider('treeDataProvider', aCompleteNodeTreeDataProvider(), (fn) => fn);
return treeView.reveal({ key: 'a' })
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepEqual({ handle: '0/0:a', label: 'a', collapsibleState: TreeItemCollapsibleState.Collapsed }, removeUnsetKeys(revealTarget.args[0][1]));
assert.deepEqual([], revealTarget.args[0][2]);
assert.equal(void 0, revealTarget.args[0][3]);
});
});
test('reveal will return parents array for an element', () => {
const revealTarget = sinon.spy(target, '$reveal');
const treeView = testObject.registerTreeDataProvider('treeDataProvider', aCompleteNodeTreeDataProvider(), (fn) => fn);
return treeView.reveal({ key: 'aa' })
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepEqual({ handle: '0/0:a/0:aa', label: 'aa', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
assert.deepEqual([{ handle: '0/0:a', label: 'a', collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
assert.equal(void 0, revealTarget.args[0][3]);
});
});
test('reveal will return parents array for deeper element with no selection', () => {
tree = {
'b': {
'ba': {
'bac': {}
}
}
};
const revealTarget = sinon.spy(target, '$reveal');
const treeView = testObject.registerTreeDataProvider('treeDataProvider', aCompleteNodeTreeDataProvider(), (fn) => fn);
return treeView.reveal({ key: 'bac' }, { select: false })
.then(() => {
assert.ok(revealTarget.calledOnce);
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
assert.deepEqual({ handle: '0/0:b/0:ba/0:bac', label: 'bac', collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b/0:ba' }, removeUnsetKeys(revealTarget.args[0][1]));
assert.deepEqual([
{ handle: '0/0:b', label: 'b', collapsibleState: TreeItemCollapsibleState.Collapsed },
{ handle: '0/0:b/0:ba', label: 'ba', collapsibleState: TreeItemCollapsibleState.Collapsed, parentHandle: '0/0:b' }
], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
assert.deepEqual({ select: false }, revealTarget.args[0][3]);
});
});
@@ -315,15 +481,33 @@ suite('ExtHostTreeView', function () {
};
}
function aStringTreeDataProvider(): TreeDataProvider<string> {
function aCompleteNodeTreeDataProvider(): TreeDataProvider<{ key: string }> {
return {
getChildren: (element: string): string[] => {
return getChildren(element);
getChildren: (element: { key: string }): { key: string }[] => {
return getChildren(element ? element.key : undefined).map(key => getNode(key));
},
getTreeItem: (element: string): TreeItem => {
return getTreeItem(element);
getTreeItem: (element: { key: string }): TreeItem => {
return getTreeItem(element.key);
},
onDidChangeTreeData: onDidChangeTreeKey.event
getParent: ({ key }: { key: string }): { key: string } => {
const parentKey = key.substring(0, key.length - 1);
return parentKey ? new Key(parentKey) : void 0;
},
onDidChangeTreeData: onDidChangeTreeNode.event
};
}
function aNodeWithIdTreeDataProvider(): TreeDataProvider<{ key: string }> {
return {
getChildren: (element: { key: string }): { key: string }[] => {
return getChildren(element ? element.key : undefined).map(key => getNode(key));
},
getTreeItem: (element: { key: string }): TreeItem => {
const treeItem = getTreeItem(element.key);
treeItem.id = element.key;
return treeItem;
},
onDidChangeTreeData: onDidChangeTreeNodeWithId.event
};
}
@@ -344,12 +528,7 @@ suite('ExtHostTreeView', function () {
}
let treeElement = getTreeElement(key);
if (treeElement) {
const children = Object.keys(treeElement);
const collapsibleStateIndex = children.indexOf('collapsibleState');
if (collapsibleStateIndex !== -1) {
children.splice(collapsibleStateIndex, 1);
}
return children;
return Object.keys(treeElement);
}
return [];
}
@@ -358,15 +537,19 @@ suite('ExtHostTreeView', function () {
const treeElement = getTreeElement(key);
return {
label: labels[key] || key,
collapsibleState: treeElement ? treeElement['collapsibleState'] : TreeItemCollapsibleState.Collapsed
collapsibleState: treeElement && Object.keys(treeElement).length ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None
};
}
function getNode(key: string): { key: string } {
if (!nodes[key]) {
nodes[key] = { key };
nodes[key] = new Key(key);
}
return nodes[key];
}
});
class Key {
constructor(readonly key: string) { }
}
});

View File

@@ -362,6 +362,53 @@ suite('ExtHostTypes', function () {
});
// test('WorkspaceEdit should fail when editing deleted resource', () => {
// const resource = URI.parse('file:///a.ts');
// const edit = new types.WorkspaceEdit();
// edit.deleteResource(resource);
// try {
// edit.insert(resource, new types.Position(0, 0), '');
// assert.fail(false, 'Should disallow edit of deleted resource');
// } catch {
// // expected
// }
// });
// test('WorkspaceEdit - keep order of text and file changes', function () {
// const edit = new types.WorkspaceEdit();
// edit.replace(URI.parse('foo:a'), new types.Range(1, 1, 1, 1), 'foo');
// edit.renameResource(URI.parse('foo:a'), URI.parse('foo:b'));
// edit.replace(URI.parse('foo:a'), new types.Range(2, 1, 2, 1), 'bar');
// edit.replace(URI.parse('foo:b'), new types.Range(3, 1, 3, 1), 'bazz');
// const all = edit.allEntries();
// assert.equal(all.length, 3);
// function isFileChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, URI] {
// const [f, s] = thing;
// return URI.isUri(f) && URI.isUri(s);
// }
// function isTextChange(thing: [URI, types.TextEdit[]] | [URI, URI]): thing is [URI, types.TextEdit[]] {
// const [f, s] = thing;
// return URI.isUri(f) && Array.isArray(s);
// }
// const [first, second, third] = all;
// assert.equal(first[0].toString(), 'foo:a');
// assert.ok(!isFileChange(first));
// assert.ok(isTextChange(first) && first[1].length === 2);
// assert.equal(second[0].toString(), 'foo:a');
// assert.ok(isFileChange(second));
// assert.equal(third[0].toString(), 'foo:b');
// assert.ok(!isFileChange(third));
// assert.ok(isTextChange(third) && third[1].length === 1);
// });
test('DocumentLink', function () {
assert.throws(() => new types.DocumentLink(null, null));
assert.throws(() => new types.DocumentLink(new types.Range(1, 1, 1, 1), null));

View File

@@ -9,12 +9,25 @@ import * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { basename } from 'path';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { TestThreadService } from './testThreadService';
import { TestRPCProtocol } from './testRPCProtocol';
import { normalize } from 'vs/base/common/paths';
import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { NullLogService } from 'vs/platform/log/common/log';
suite('ExtHostWorkspace', function () {
const extensionDescriptor: IExtensionDescription = {
id: 'nullExtensionDescription',
name: 'ext',
publisher: 'vscode',
enableProposedApi: false,
engines: undefined,
extensionFolderPath: undefined,
isBuiltin: false,
version: undefined
};
function assertAsRelativePath(workspace: ExtHostWorkspace, input: string, expected: string, includeWorkspace?: boolean) {
const actual = workspace.getRelativePath(input, includeWorkspace);
if (actual === expected) {
@@ -25,6 +38,488 @@ suite('ExtHostWorkspace', function () {
}
test('asRelativePath', function () {
const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }, new NullLogService());
assertAsRelativePath(ws, '/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot');
assertAsRelativePath(ws, '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart',
'/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart');
assertAsRelativePath(ws, '', '');
assertAsRelativePath(ws, '/foo/bar', '/foo/bar');
assertAsRelativePath(ws, 'in/out', 'in/out');
});
test('asRelativePath, same paths, #11402', function () {
const root = '/home/aeschli/workspaces/samples/docker';
const input = '/home/aeschli/workspaces/samples/docker';
const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
assertAsRelativePath(ws, (input), input);
const input2 = '/home/aeschli/workspaces/samples/docker/a.file';
assertAsRelativePath(ws, (input2), 'a.file');
});
test('asRelativePath, no workspace', function () {
const ws = new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService());
assertAsRelativePath(ws, (''), '');
assertAsRelativePath(ws, ('/foo/bar'), '/foo/bar');
});
test('asRelativePath, multiple folders', function () {
const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService());
assertAsRelativePath(ws, '/Coding/One/file.txt', 'One/file.txt');
assertAsRelativePath(ws, '/Coding/Two/files/out.txt', 'Two/files/out.txt');
assertAsRelativePath(ws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt');
});
test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () {
const mrws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService());
assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt');
assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt', true);
assertAsRelativePath(mrws, '/Coding/One/file.txt', 'file.txt', false);
assertAsRelativePath(mrws, '/Coding/Two/files/out.txt', 'Two/files/out.txt');
assertAsRelativePath(mrws, '/Coding/Two/files/out.txt', 'Two/files/out.txt', true);
assertAsRelativePath(mrws, '/Coding/Two/files/out.txt', 'files/out.txt', false);
assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt');
assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true);
assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false);
const srws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0)], name: 'Test' }, new NullLogService());
assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt');
assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt', false);
assertAsRelativePath(srws, '/Coding/One/file.txt', 'One/file.txt', true);
assertAsRelativePath(srws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt');
assertAsRelativePath(srws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true);
assertAsRelativePath(srws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false);
});
test('getPath, legacy', function () {
let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
assert.equal(ws.getPath(), undefined);
ws = new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService());
assert.equal(ws.getPath(), undefined);
ws = new ExtHostWorkspace(new TestRPCProtocol(), undefined, new NullLogService());
assert.equal(ws.getPath(), undefined);
ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('Folder'), 0), aWorkspaceFolderData(URI.file('Another/Folder'), 1)] }, new NullLogService());
assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder');
ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Folder'), 0)] }, new NullLogService());
assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder');
});
test('WorkspaceFolder has name and index', function () {
const ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService());
const [one, two] = ws.getWorkspaceFolders();
assert.equal(one.name, 'One');
assert.equal(one.index, 0);
assert.equal(two.name, 'Two');
assert.equal(two.index, 1);
});
test('getContainingWorkspaceFolder', function () {
const ws = new ExtHostWorkspace(new TestRPCProtocol(), {
id: 'foo',
name: 'Test',
folders: [
aWorkspaceFolderData(URI.file('/Coding/One'), 0),
aWorkspaceFolderData(URI.file('/Coding/Two'), 1),
aWorkspaceFolderData(URI.file('/Coding/Two/Nested'), 2)
]
}, new NullLogService());
let folder = ws.getWorkspaceFolder(URI.file('/foo/bar'));
assert.equal(folder, undefined);
folder = ws.getWorkspaceFolder(URI.file('/Coding/One/file/path.txt'));
assert.equal(folder.name, 'One');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/file/path.txt'));
assert.equal(folder.name, 'Two');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nest'));
assert.equal(folder.name, 'Two');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/file'));
assert.equal(folder.name, 'Nested');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/f'));
assert.equal(folder.name, 'Nested');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested'), true);
assert.equal(folder.name, 'Two');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/'), true);
assert.equal(folder.name, 'Two');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested'));
assert.equal(folder.name, 'Nested');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/'));
assert.equal(folder.name, 'Nested');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two'), true);
assert.equal(folder, undefined);
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two'), false);
assert.equal(folder.name, 'Two');
});
test('Multiroot change event should have a delta, #29641', function (done) {
let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
let finished = false;
const finish = (error?) => {
if (!finished) {
finished = true;
done(error);
}
};
let sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.added, []);
assert.deepEqual(e.removed, []);
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [] });
sub.dispose();
sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.removed, []);
assert.equal(e.added.length, 1);
assert.equal(e.added[0].uri.toString(), 'foo:bar');
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] });
sub.dispose();
sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.removed, []);
assert.equal(e.added.length, 1);
assert.equal(e.added[0].uri.toString(), 'foo:bar2');
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar2'), 1)] });
sub.dispose();
sub = ws.onDidChangeWorkspace(e => {
try {
assert.equal(e.removed.length, 2);
assert.equal(e.removed[0].uri.toString(), 'foo:bar');
assert.equal(e.removed[1].uri.toString(), 'foo:bar2');
assert.equal(e.added.length, 1);
assert.equal(e.added[0].uri.toString(), 'foo:bar3');
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0)] });
sub.dispose();
finish();
});
test('Multiroot change keeps existing workspaces live', function () {
let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService());
let firstFolder = ws.getWorkspaceFolders()[0];
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar2'), 0), aWorkspaceFolderData(URI.parse('foo:bar'), 1, 'renamed')] });
assert.equal(ws.getWorkspaceFolders()[1], firstFolder);
assert.equal(firstFolder.index, 1);
assert.equal(firstFolder.name, 'renamed');
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0), aWorkspaceFolderData(URI.parse('foo:bar2'), 1), aWorkspaceFolderData(URI.parse('foo:bar'), 2)] });
assert.equal(ws.getWorkspaceFolders()[2], firstFolder);
assert.equal(firstFolder.index, 2);
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0)] });
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0), aWorkspaceFolderData(URI.parse('foo:bar'), 1)] });
assert.notEqual(firstFolder, ws.workspace.folders[0]);
});
test('updateWorkspaceFolders - invalid arguments', function () {
let ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, null, null));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 0));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 1));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 1, 0));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, -1, 0));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, -1, -1));
ws = new ExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService());
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 1, 1));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2));
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 1, asUpdateWorkspaceFolderData(URI.parse('foo:bar'))));
});
test('updateWorkspaceFolders - valid arguments', function (done) {
let finished = false;
const finish = (error?) => {
if (!finished) {
finished = true;
done(error);
}
};
const protocol = {
getProxy: () => { return undefined; },
set: undefined,
assertRegistered: undefined
};
const ws = new ExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
//
// Add one folder
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 0, asUpdateWorkspaceFolderData(URI.parse('foo:bar'))));
assert.equal(1, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar').toString());
const firstAddedFolder = ws.getWorkspaceFolders()[0];
let gotEvent = false;
let sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.removed, []);
assert.equal(e.added.length, 1);
assert.equal(e.added[0].uri.toString(), 'foo:bar');
assert.equal(e.added[0], firstAddedFolder); // verify object is still live
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], firstAddedFolder); // verify object is still live
//
// Add two more folders
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 1, 0, asUpdateWorkspaceFolderData(URI.parse('foo:bar1')), asUpdateWorkspaceFolderData(URI.parse('foo:bar2'))));
assert.equal(3, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar').toString());
assert.equal(ws.workspace.folders[1].uri.toString(), URI.parse('foo:bar1').toString());
assert.equal(ws.workspace.folders[2].uri.toString(), URI.parse('foo:bar2').toString());
const secondAddedFolder = ws.getWorkspaceFolders()[1];
const thirdAddedFolder = ws.getWorkspaceFolders()[2];
gotEvent = false;
sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.removed, []);
assert.equal(e.added.length, 2);
assert.equal(e.added[0].uri.toString(), 'foo:bar1');
assert.equal(e.added[1].uri.toString(), 'foo:bar2');
assert.equal(e.added[0], secondAddedFolder);
assert.equal(e.added[1], thirdAddedFolder);
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar1'), 1), aWorkspaceFolderData(URI.parse('foo:bar2'), 2)] }); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], firstAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], secondAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[2], thirdAddedFolder); // verify object is still live
//
// Remove one folder
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 2, 1));
assert.equal(2, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar').toString());
assert.equal(ws.workspace.folders[1].uri.toString(), URI.parse('foo:bar1').toString());
gotEvent = false;
sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.added, []);
assert.equal(e.removed.length, 1);
assert.equal(e.removed[0], thirdAddedFolder);
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar1'), 1)] }); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], firstAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], secondAddedFolder); // verify object is still live
//
// Rename folder
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2, asUpdateWorkspaceFolderData(URI.parse('foo:bar'), 'renamed 1'), asUpdateWorkspaceFolderData(URI.parse('foo:bar1'), 'renamed 2')));
assert.equal(2, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar').toString());
assert.equal(ws.workspace.folders[1].uri.toString(), URI.parse('foo:bar1').toString());
assert.equal(ws.workspace.folders[0].name, 'renamed 1');
assert.equal(ws.workspace.folders[1].name, 'renamed 2');
assert.equal(ws.getWorkspaceFolders()[0].name, 'renamed 1');
assert.equal(ws.getWorkspaceFolders()[1].name, 'renamed 2');
gotEvent = false;
sub = ws.onDidChangeWorkspace(e => {
try {
assert.deepEqual(e.added, []);
assert.equal(e.removed.length, []);
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0, 'renamed 1'), aWorkspaceFolderData(URI.parse('foo:bar1'), 1, 'renamed 2')] }); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], firstAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], secondAddedFolder); // verify object is still live
assert.equal(ws.workspace.folders[0].name, 'renamed 1');
assert.equal(ws.workspace.folders[1].name, 'renamed 2');
assert.equal(ws.getWorkspaceFolders()[0].name, 'renamed 1');
assert.equal(ws.getWorkspaceFolders()[1].name, 'renamed 2');
//
// Add and remove folders
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2, asUpdateWorkspaceFolderData(URI.parse('foo:bar3')), asUpdateWorkspaceFolderData(URI.parse('foo:bar4'))));
assert.equal(2, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar3').toString());
assert.equal(ws.workspace.folders[1].uri.toString(), URI.parse('foo:bar4').toString());
const fourthAddedFolder = ws.getWorkspaceFolders()[0];
const fifthAddedFolder = ws.getWorkspaceFolders()[1];
gotEvent = false;
sub = ws.onDidChangeWorkspace(e => {
try {
assert.equal(e.added.length, 2);
assert.equal(e.added[0], fourthAddedFolder);
assert.equal(e.added[1], fifthAddedFolder);
assert.equal(e.removed.length, 2);
assert.equal(e.removed[0], firstAddedFolder);
assert.equal(e.removed[1], secondAddedFolder);
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0), aWorkspaceFolderData(URI.parse('foo:bar4'), 1)] }); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], fourthAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], fifthAddedFolder); // verify object is still live
//
// Swap folders
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2, asUpdateWorkspaceFolderData(URI.parse('foo:bar4')), asUpdateWorkspaceFolderData(URI.parse('foo:bar3'))));
assert.equal(2, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar4').toString());
assert.equal(ws.workspace.folders[1].uri.toString(), URI.parse('foo:bar3').toString());
assert.equal(ws.getWorkspaceFolders()[0], fifthAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], fourthAddedFolder); // verify object is still live
gotEvent = false;
sub = ws.onDidChangeWorkspace(e => {
try {
assert.equal(e.added.length, 0);
assert.equal(e.removed.length, 0);
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar4'), 0), aWorkspaceFolderData(URI.parse('foo:bar3'), 1)] }); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], fifthAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], fourthAddedFolder); // verify object is still live
assert.equal(fifthAddedFolder.index, 0);
assert.equal(fourthAddedFolder.index, 1);
//
// Add one folder after the other without waiting for confirmation (not supported currently)
//
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 2, 0, asUpdateWorkspaceFolderData(URI.parse('foo:bar5'))));
assert.equal(3, ws.workspace.folders.length);
assert.equal(ws.workspace.folders[0].uri.toString(), URI.parse('foo:bar4').toString());
assert.equal(ws.workspace.folders[1].uri.toString(), URI.parse('foo:bar3').toString());
assert.equal(ws.workspace.folders[2].uri.toString(), URI.parse('foo:bar5').toString());
const sixthAddedFolder = ws.getWorkspaceFolders()[2];
gotEvent = false;
sub = ws.onDidChangeWorkspace(e => {
try {
assert.equal(e.added.length, 1);
assert.equal(e.added[0], sixthAddedFolder);
gotEvent = true;
} catch (error) {
finish(error);
}
});
ws.$acceptWorkspaceData({
id: 'foo', name: 'Test', folders: [
aWorkspaceFolderData(URI.parse('foo:bar4'), 0),
aWorkspaceFolderData(URI.parse('foo:bar3'), 1),
aWorkspaceFolderData(URI.parse('foo:bar5'), 2)
]
}); // simulate acknowledgement from main side
assert.equal(gotEvent, true);
sub.dispose();
assert.equal(ws.getWorkspaceFolders()[0], fifthAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[1], fourthAddedFolder); // verify object is still live
assert.equal(ws.getWorkspaceFolders()[2], sixthAddedFolder); // verify object is still live
finish();
});
test('`vscode.workspace.getWorkspaceFolder(file)` don\'t return workspace folder when file open from command line. #36221', function () {
let ws = new ExtHostWorkspace(new TestRPCProtocol(), {
id: 'foo', name: 'Test', folders: [
aWorkspaceFolderData(URI.file('c:/Users/marek/Desktop/vsc_test/'), 0)
]
}, new NullLogService());
assert.ok(ws.getWorkspaceFolder(URI.file('c:/Users/marek/Desktop/vsc_test/a.txt')));
assert.ok(ws.getWorkspaceFolder(URI.file('C:/Users/marek/Desktop/vsc_test/b.txt')));
});
function aWorkspaceFolderData(uri: URI, index: number, name: string = ''): IWorkspaceFolderData {
@@ -34,4 +529,8 @@ suite('ExtHostWorkspace', function () {
name: name || basename(uri.path)
};
}
function asUpdateWorkspaceFolderData(uri: URI, name?: string): { uri: URI, name?: string } {
return { uri, name };
}
});

View File

@@ -8,13 +8,13 @@
import * as assert from 'assert';
import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { OneGetThreadService } from './testThreadService';
import { SingleProxyRPCProtocol } from './testRPCProtocol';
suite('MainThreadCommands', function () {
test('dispose on unregister', function () {
const commands = new MainThreadCommands(OneGetThreadService(null), undefined);
const commands = new MainThreadCommands(SingleProxyRPCProtocol(null), undefined);
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
// register
@@ -28,7 +28,7 @@ suite('MainThreadCommands', function () {
test('unregister all on dispose', function () {
const commands = new MainThreadCommands(OneGetThreadService(null), undefined);
const commands = new MainThreadCommands(SingleProxyRPCProtocol(null), undefined);
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
commands.$registerCommand('foo');

View File

@@ -13,7 +13,7 @@ import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platf
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { MainThreadConfiguration } from 'vs/workbench/api/electron-browser/mainThreadConfiguration';
import { OneGetThreadService } from './testThreadService';
import { SingleProxyRPCProtocol } from './testRPCProtocol';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService';
@@ -56,7 +56,7 @@ suite('MainThreadConfiguration', function () {
test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null);
@@ -65,7 +65,7 @@ suite('MainThreadConfiguration', function () {
test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc'));
@@ -74,7 +74,7 @@ suite('MainThreadConfiguration', function () {
test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null);
@@ -83,7 +83,7 @@ suite('MainThreadConfiguration', function () {
test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null);
@@ -92,7 +92,7 @@ suite('MainThreadConfiguration', function () {
test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc'));
@@ -101,7 +101,7 @@ suite('MainThreadConfiguration', function () {
test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc'));
@@ -110,7 +110,7 @@ suite('MainThreadConfiguration', function () {
test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null);
@@ -119,7 +119,7 @@ suite('MainThreadConfiguration', function () {
test('update resource configuration without configuration target defaults to folder', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc'));
@@ -128,7 +128,7 @@ suite('MainThreadConfiguration', function () {
test('update configuration with user configuration target', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', URI.file('abc'));
@@ -137,7 +137,7 @@ suite('MainThreadConfiguration', function () {
test('update configuration with workspace configuration target', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', URI.file('abc'));
@@ -146,7 +146,7 @@ suite('MainThreadConfiguration', function () {
test('update configuration with folder configuration target', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE_FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc'));
@@ -155,7 +155,7 @@ suite('MainThreadConfiguration', function () {
test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null);
@@ -164,7 +164,7 @@ suite('MainThreadConfiguration', function () {
test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc'));
@@ -173,7 +173,7 @@ suite('MainThreadConfiguration', function () {
test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null);
@@ -182,7 +182,7 @@ suite('MainThreadConfiguration', function () {
test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null);
@@ -191,7 +191,7 @@ suite('MainThreadConfiguration', function () {
test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc'));
@@ -200,7 +200,7 @@ suite('MainThreadConfiguration', function () {
test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc'));
@@ -209,7 +209,7 @@ suite('MainThreadConfiguration', function () {
test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null);
@@ -218,7 +218,7 @@ suite('MainThreadConfiguration', function () {
test('remove configuration without configuration target defaults to folder', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc'));

View File

@@ -7,7 +7,7 @@
import * as assert from 'assert';
import { BoundModelReferenceCollection } from 'vs/workbench/api/electron-browser/mainThreadDocuments';
import { Model } from 'vs/editor/common/model/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { TPromise } from 'vs/base/common/winjs.base';
suite('BoundModelReferenceCollection', () => {
@@ -23,7 +23,7 @@ suite('BoundModelReferenceCollection', () => {
let didDispose = false;
col.add({
object: <any>{ textEditorModel: Model.createFromString('farboo') },
object: <any>{ textEditorModel: TextModel.createFromString('farboo') },
dispose() {
didDispose = true;
}
@@ -39,20 +39,20 @@ suite('BoundModelReferenceCollection', () => {
let disposed: number[] = [];
col.add({
object: <any>{ textEditorModel: Model.createFromString('farboo') },
object: <any>{ textEditorModel: TextModel.createFromString('farboo') },
dispose() {
disposed.push(0);
}
});
col.add({
object: <any>{ textEditorModel: Model.createFromString('boofar') },
object: <any>{ textEditorModel: TextModel.createFromString('boofar') },
dispose() {
disposed.push(1);
}
});
col.add({
object: <any>{ textEditorModel: Model.createFromString(new Array(71).join('x')) },
object: <any>{ textEditorModel: TextModel.createFromString(new Array(71).join('x')) },
dispose() {
disposed.push(2);
}

View File

@@ -7,7 +7,7 @@
import * as assert from 'assert';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors';
import { OneGetThreadService } from './testThreadService';
import { SingleProxyRPCProtocol } from './testRPCProtocol';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { TestCodeEditorService } from 'vs/editor/test/browser/testCodeEditorService';
@@ -53,7 +53,7 @@ suite('MainThreadDocumentsAndEditors', () => {
/* tslint:disable */
new MainThreadDocumentsAndEditors(
OneGetThreadService(new class extends mock<ExtHostDocumentsAndEditorsShape>() {
SingleProxyRPCProtocol(new class extends mock<ExtHostDocumentsAndEditorsShape>() {
$acceptDocumentsAndEditorsDelta(delta) { deltas.push(delta); }
}),
modelService,

View File

@@ -7,34 +7,62 @@
import * as assert from 'assert';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors';
import { OneGetThreadService, TestThreadService } from './testThreadService';
import { SingleProxyRPCProtocol, TestRPCProtocol } from './testRPCProtocol';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { TestCodeEditorService } from 'vs/editor/test/browser/testCodeEditorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditorsShape, IWorkspaceResourceEdit, ExtHostContext, ExtHostDocumentsShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentsAndEditorsShape, ExtHostContext, ExtHostDocumentsShape } from 'vs/workbench/api/node/extHost.protocol';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import Event from 'vs/base/common/event';
import { MainThreadEditors } from 'vs/workbench/api/electron-browser/mainThreadEditors';
import { MainThreadTextEditors } from 'vs/workbench/api/electron-browser/mainThreadEditors';
import URI from 'vs/base/common/uri';
import { Range } from 'vs/editor/common/core/range';
import { Position } from 'vs/editor/common/core/position';
import { IModelService } from 'vs/editor/common/services/modelService';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { TestFileService } from 'vs/workbench/test/workbenchTestServices';
import { TPromise } from 'vs/base/common/winjs.base';
import { IFileStat } from 'vs/platform/files/common/files';
import { ResourceTextEdit } from 'vs/editor/common/modes';
suite('MainThreadEditors', () => {
const resource = URI.parse('foo:bar');
let modelService: IModelService;
let editors: MainThreadEditors;
let editors: MainThreadTextEditors;
const movedResources = new Map<URI, URI>();
const createdResources = new Set<URI>();
const deletedResources = new Set<URI>();
setup(() => {
const configService = new TestConfigurationService();
modelService = new ModelServiceImpl(null, configService);
const codeEditorService = new TestCodeEditorService();
movedResources.clear();
createdResources.clear();
deletedResources.clear();
const fileService = new class extends TestFileService {
async moveFile(from, target): TPromise<IFileStat> {
movedResources.set(from, target);
return createMockFileStat(target);
}
async createFile(uri): TPromise<IFileStat> {
createdResources.add(uri);
return createMockFileStat(uri);
}
async del(uri): TPromise<any> {
deletedResources.add(uri);
}
};
const textFileService = new class extends mock<ITextFileService>() {
isDirty() { return false; }
models = <any>{
@@ -52,37 +80,37 @@ suite('MainThreadEditors', () => {
onEditorGroupMoved = Event.None;
};
const testThreadService = new TestThreadService(true);
testThreadService.setTestInstance(ExtHostContext.ExtHostDocuments, new class extends mock<ExtHostDocumentsShape>() {
const rpcProtocol = new TestRPCProtocol();
rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock<ExtHostDocumentsShape>() {
$acceptModelChanged(): void {
}
});
testThreadService.setTestInstance(ExtHostContext.ExtHostDocumentsAndEditors, new class extends mock<ExtHostDocumentsAndEditorsShape>() {
rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new class extends mock<ExtHostDocumentsAndEditorsShape>() {
$acceptDocumentsAndEditorsDelta(): void {
}
});
const documentAndEditor = new MainThreadDocumentsAndEditors(
testThreadService,
rpcProtocol,
modelService,
textFileService,
workbenchEditorService,
codeEditorService,
null,
null,
fileService,
null,
null,
editorGroupService,
);
editors = new MainThreadEditors(
editors = new MainThreadTextEditors(
documentAndEditor,
OneGetThreadService(null),
SingleProxyRPCProtocol(null),
codeEditorService,
workbenchEditorService,
editorGroupService,
null,
null,
fileService,
modelService
);
});
@@ -91,11 +119,11 @@ suite('MainThreadEditors', () => {
let model = modelService.createModel('something', null, resource);
let workspaceResourceEdit: IWorkspaceResourceEdit = {
let workspaceResourceEdit: ResourceTextEdit = {
resource: resource,
modelVersionId: model.getVersionId(),
edits: [{
newText: 'asdfg',
text: 'asdfg',
range: new Range(1, 1, 1, 1)
}]
};
@@ -103,8 +131,35 @@ suite('MainThreadEditors', () => {
// Act as if the user edited the model
model.applyEdits([EditOperation.insert(new Position(0, 0), 'something')]);
return editors.$tryApplyWorkspaceEdit([workspaceResourceEdit]).then((result) => {
return editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit] }).then((result) => {
assert.equal(result, false);
});
});
test(`applyWorkspaceEdit with only resource edit`, () => {
return editors.$tryApplyWorkspaceEdit({
edits: [
{ oldUri: resource, newUri: resource },
{ oldUri: undefined, newUri: resource },
{ oldUri: resource, newUri: undefined }
]
}).then((result) => {
assert.equal(result, true);
assert.equal(movedResources.get(resource), resource);
assert.equal(createdResources.has(resource), true);
assert.equal(deletedResources.has(resource), true);
});
});
});
function createMockFileStat(target: URI): IFileStat {
return {
etag: '',
isDirectory: false,
name: target.path,
mtime: 0,
resource: target
};
}

View File

@@ -12,9 +12,12 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices';
import { toResource } from 'vs/base/test/common/utils';
import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { ITextFileService, SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
import { snapshotToString } from 'vs/platform/files/common/files';
class ServiceAccessor {
constructor( @ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService) {
@@ -49,25 +52,25 @@ suite('MainThreadSaveParticipant', function () {
let lineContent = '';
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), lineContent);
assert.equal(snapshotToString(model.createSnapshot()), lineContent);
// No new line if last line already empty
lineContent = `Hello New Line${model.textEditorModel.getEOL()}`;
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), lineContent);
assert.equal(snapshotToString(model.createSnapshot()), lineContent);
// New empty line added (single line)
lineContent = 'Hello New Line';
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), `${lineContent}${model.textEditorModel.getEOL()}`);
assert.equal(snapshotToString(model.createSnapshot()), `${lineContent}${model.textEditorModel.getEOL()}`);
// New empty line added (multi line)
lineContent = `Hello New Line${model.textEditorModel.getEOL()}Hello New Line${model.textEditorModel.getEOL()}Hello New Line`;
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), `${lineContent}${model.textEditorModel.getEOL()}`);
assert.equal(snapshotToString(model.createSnapshot()), `${lineContent}${model.textEditorModel.getEOL()}`);
done();
});
@@ -89,27 +92,56 @@ suite('MainThreadSaveParticipant', function () {
let lineContent = `${textContent}`;
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), lineContent);
assert.equal(snapshotToString(model.createSnapshot()), lineContent);
// No new line removal if last line is single new line
lineContent = `${textContent}${eol}`;
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), lineContent);
assert.equal(snapshotToString(model.createSnapshot()), lineContent);
// Remove new line (single line with two new lines)
lineContent = `${textContent}${eol}${eol}`;
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), `${textContent}${eol}`);
assert.equal(snapshotToString(model.createSnapshot()), `${textContent}${eol}`);
// Remove new lines (multiple lines with multiple new lines)
lineContent = `${textContent}${eol}${textContent}${eol}${eol}${eol}`;
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), `${textContent}${eol}${textContent}${eol}`);
assert.equal(snapshotToString(model.createSnapshot()), `${textContent}${eol}${textContent}${eol}`);
done();
});
});
test('trim final new lines bug#39750', function (done) {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8');
model.load().then(() => {
const configService = new TestConfigurationService();
configService.setUserConfiguration('files', { 'trimFinalNewlines': true });
const participant = new TrimFinalNewLinesParticipant(configService, undefined);
const textContent = 'Trim New Line';
// single line
let lineContent = `${textContent}`;
model.textEditorModel.setValue(lineContent);
// apply edits and push to undo stack.
let textEdits = [{ identifier: null, range: new Range(1, 14, 1, 14), text: '.', forceMoveMarkers: false }];
model.textEditorModel.pushEditOperations([new Selection(1, 14, 1, 14)], textEdits, () => { return [new Selection(1, 15, 1, 15)]; });
// undo
model.textEditorModel.undo();
assert.equal(snapshotToString(model.createSnapshot()), `${textContent}`);
// trim final new lines should not mess the undo stack
participant.participate(model, { reason: SaveReason.EXPLICIT });
model.textEditorModel.redo();
assert.equal(snapshotToString(model.createSnapshot()), `${textContent}.`);
done();
});
});
});

View File

@@ -0,0 +1,135 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { CharCode } from 'vs/base/common/charCode';
export function SingleProxyRPCProtocol(thing: any): IRPCProtocol {
return {
getProxy<T>(): T {
return thing;
},
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
return value;
},
assertRegistered: undefined
};
}
declare var Proxy; // TODO@TypeScript
export class TestRPCProtocol implements IRPCProtocol {
private _callCountValue: number = 0;
private _idle: Promise<any>;
private _completeIdle: Function;
private readonly _locals: { [id: string]: any; };
private readonly _proxies: { [id: string]: any; };
constructor() {
this._locals = Object.create(null);
this._proxies = Object.create(null);
}
private get _callCount(): number {
return this._callCountValue;
}
private set _callCount(value: number) {
this._callCountValue = value;
if (this._callCountValue === 0) {
if (this._completeIdle) {
this._completeIdle();
}
this._idle = undefined;
}
}
sync(): Promise<any> {
return new Promise<any>((c) => {
setTimeout(c, 0);
}).then(() => {
if (this._callCount === 0) {
return undefined;
}
if (!this._idle) {
this._idle = new Promise<any>((c, e) => {
this._completeIdle = c;
});
}
return this._idle;
});
}
public getProxy<T>(identifier: ProxyIdentifier<T>): T {
if (!this._proxies[identifier.id]) {
this._proxies[identifier.id] = this._createProxy(identifier.id);
}
return this._proxies[identifier.id];
}
private _createProxy<T>(proxyId: string): T {
let handler = {
get: (target, name: string) => {
if (!target[name] && name.charCodeAt(0) === CharCode.DollarSign) {
target[name] = (...myArgs: any[]) => {
return this._remoteCall(proxyId, name, myArgs);
};
}
return target[name];
}
};
return new Proxy(Object.create(null), handler);
}
public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
this._locals[identifier.id] = value;
return value;
}
protected _remoteCall(proxyId: string, path: string, args: any[]): TPromise<any> {
this._callCount++;
return new TPromise<any>((c) => {
setTimeout(c, 0);
}).then(() => {
const instance = this._locals[proxyId];
// pretend the args went over the wire... (invoke .toJSON on objects...)
const wireArgs = simulateWireTransfer(args);
let p: Thenable<any>;
try {
let result = (<Function>instance[path]).apply(instance, wireArgs);
p = TPromise.is(result) ? result : TPromise.as(result);
} catch (err) {
p = TPromise.wrapError(err);
}
return p.then(result => {
this._callCount--;
// pretend the result went over the wire... (invoke .toJSON on objects...)
const wireResult = simulateWireTransfer(result);
return wireResult;
}, err => {
this._callCount--;
return TPromise.wrapError(err);
});
});
}
public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {
throw new Error('Not implemented!');
}
}
function simulateWireTransfer<T>(obj: T): T {
if (!obj) {
return obj;
}
return JSON.parse(JSON.stringify(obj));
}

View File

@@ -1,161 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { IThreadService, ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
export function OneGetThreadService(thing: any): IThreadService {
return {
get<T>(): T {
return thing;
},
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
return value;
},
assertRegistered: undefined
};
}
declare var Proxy; // TODO@TypeScript
export abstract class AbstractTestThreadService {
private _isMain: boolean;
protected _locals: { [id: string]: any; };
private _proxies: { [id: string]: any; } = Object.create(null);
constructor(isMain: boolean) {
this._isMain = isMain;
this._locals = Object.create(null);
this._proxies = Object.create(null);
}
public handle(rpcId: string, methodName: string, args: any[]): any {
if (!this._locals[rpcId]) {
throw new Error('Unknown actor ' + rpcId);
}
let actor = this._locals[rpcId];
let method = actor[methodName];
if (typeof method !== 'function') {
throw new Error('Unknown method ' + methodName + ' on actor ' + rpcId);
}
return method.apply(actor, args);
}
get<T>(identifier: ProxyIdentifier<T>): T {
if (!this._proxies[identifier.id]) {
this._proxies[identifier.id] = this._createProxy(identifier.id);
}
return this._proxies[identifier.id];
}
private _createProxy<T>(id: string): T {
let handler = {
get: (target, name) => {
return (...myArgs: any[]) => {
return this._callOnRemote(id, name, myArgs);
};
}
};
return new Proxy({}, handler);
}
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
if (identifier.isMain !== this._isMain) {
throw new Error('Mismatch in object registration!');
}
this._locals[identifier.id] = value;
return value;
}
protected abstract _callOnRemote(proxyId: string, path: string, args: any[]): TPromise<any>;
}
export class TestThreadService extends AbstractTestThreadService implements IThreadService {
constructor(isMainProcess: boolean = false) {
super(isMainProcess);
}
private _callCountValue: number = 0;
private _idle: TPromise<any>;
private _completeIdle: Function;
private get _callCount(): number {
return this._callCountValue;
}
private set _callCount(value: number) {
this._callCountValue = value;
if (this._callCountValue === 0) {
if (this._completeIdle) {
this._completeIdle();
}
this._idle = undefined;
}
}
sync(): TPromise<any> {
return new TPromise<any>((c) => {
setTimeout(c, 0);
}).then(() => {
if (this._callCount === 0) {
return undefined;
}
if (!this._idle) {
this._idle = new TPromise<any>((c, e) => {
this._completeIdle = c;
}, function () {
// no cancel
});
}
return this._idle;
});
}
private _testInstances: { [id: string]: any; } = Object.create(null);
setTestInstance<T>(identifier: ProxyIdentifier<T>, value: T): T {
this._testInstances[identifier.id] = value;
return value;
}
get<T>(identifier: ProxyIdentifier<T>): T {
let id = identifier.id;
if (this._locals[id]) {
return this._locals[id];
}
return super.get(identifier);
}
protected _callOnRemote(proxyId: string, path: string, args: any[]): TPromise<any> {
this._callCount++;
return new TPromise<any>((c) => {
setTimeout(c, 0);
}).then(() => {
const instance = this._testInstances[proxyId];
let p: Thenable<any>;
try {
let result = (<Function>instance[path]).apply(instance, args);
p = TPromise.is(result) ? result : TPromise.as(result);
} catch (err) {
p = TPromise.wrapError(err);
}
return p.then(result => {
this._callCount--;
return result;
}, err => {
this._callCount--;
return TPromise.wrapError(err);
});
});
}
public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {
throw new Error('Not implemented!');
}
}