SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,439 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
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 { MarkerService } from 'vs/platform/markers/common/markerService';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures';
import { MainThreadLanguageFeatures } from 'vs/workbench/api/electron-browser/mainThreadLanguageFeatures';
import { IHeapService } from 'vs/workbench/api/electron-browser/mainThreadHeapService';
import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { ExtHostHeapService } from 'vs/workbench/api/node/extHostHeapService';
import { MainThreadCommands } from 'vs/workbench/api/electron-browser/mainThreadCommands';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/node/extHostDocumentsAndEditors';
import { MainContext, ExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics';
import * as vscode from 'vscode';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
const defaultSelector = { scheme: 'far' };
const model: EditorCommon.IModel = EditorModel.createFromString(
[
'This is the first line',
'This is the second line',
'This is the third line',
].join('\n'),
undefined,
undefined,
URI.parse('far://testing/file.b'));
let threadService: TestThreadService;
let extHost: ExtHostLanguageFeatures;
let mainThread: MainThreadLanguageFeatures;
let commands: ExtHostCommands;
let disposables: vscode.Disposable[] = [];
let originalErrorHandler: (e: any) => any;
suite('ExtHostLanguageFeatureCommands', function () {
suiteSetup((done) => {
originalErrorHandler = errorHandler.getUnexpectedErrorHandler();
setUnexpectedErrorHandler(() => { });
// Use IInstantiationService to get typechecking when instantiating
let inst: IInstantiationService;
{
let instantiationService = new TestInstantiationService();
threadService = new TestThreadService();
instantiationService.stub(IHeapService, {
_serviceBrand: undefined,
trackRecursive(args) {
// nothing
return args;
}
});
instantiationService.stub(ICommandService, {
_serviceBrand: undefined,
executeCommand(id, args): any {
if (!CommandsRegistry.getCommands()[id]) {
return TPromise.wrapError(new Error(id + ' NOT known'));
}
let { handler } = CommandsRegistry.getCommands()[id];
return TPromise.as(instantiationService.invokeFunction(handler, args));
}
});
instantiationService.stub(IMarkerService, new MarkerService());
instantiationService.stub(IModelService, <IModelService>{
_serviceBrand: IModelService,
getModel(): any { return model; },
createModel(): any { throw new Error(); },
updateModel(): any { throw new Error(); },
setMode(): any { throw new Error(); },
destroyModel(): any { throw new Error(); },
getModels(): any { throw new Error(); },
onModelAdded: undefined,
onModelModeChanged: undefined,
onModelRemoved: undefined,
getCreationOptions(): any { throw new Error(); }
});
inst = instantiationService;
}
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(threadService);
extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
versionId: model.getVersionId(),
modeId: model.getLanguageIdentifier().language,
url: model.uri,
lines: model.getValue().split(model.getEOL()),
EOL: model.getEOL(),
}]
});
const extHostDocuments = new ExtHostDocuments(threadService, extHostDocumentsAndEditors);
threadService.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
const heapService = new ExtHostHeapService();
commands = new ExtHostCommands(threadService, heapService);
threadService.set(ExtHostContext.ExtHostCommands, commands);
threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService));
ExtHostApiCommands.register(commands);
const diagnostics = new ExtHostDiagnostics(threadService);
threadService.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
extHost = new ExtHostLanguageFeatures(threadService, extHostDocuments, commands, heapService, diagnostics);
threadService.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
mainThread = <MainThreadLanguageFeatures>threadService.setTestInstance(MainContext.MainThreadLanguageFeatures, inst.createInstance(MainThreadLanguageFeatures, threadService));
threadService.sync().then(done, done);
});
suiteTeardown(() => {
setUnexpectedErrorHandler(originalErrorHandler);
model.dispose();
});
teardown(function (done) {
while (disposables.length) {
disposables.pop().dispose();
}
threadService.sync()
.then(() => done(), err => done(err));
});
// --- workspace symbols
test('WorkspaceSymbols, invalid arguments', function (done) {
let promises = [
commands.executeCommand('vscode.executeWorkspaceSymbolProvider'),
commands.executeCommand('vscode.executeWorkspaceSymbolProvider', null),
commands.executeCommand('vscode.executeWorkspaceSymbolProvider', undefined),
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) {
disposables.push(extHost.registerWorkspaceSymbolProvider(<vscode.WorkspaceSymbolProvider>{
provideWorkspaceSymbols(query): any {
return [
new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first')),
new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/second'))
];
}
}));
disposables.push(extHost.registerWorkspaceSymbolProvider(<vscode.WorkspaceSymbolProvider>{
provideWorkspaceSymbols(query): any {
return [
new types.SymbolInformation(query, types.SymbolKind.Array, new types.Range(0, 0, 1, 1), URI.parse('far://testing/first'))
];
}
}));
threadService.sync().then(() => {
commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeWorkspaceSymbolProvider', 'testing').then(value => {
for (let info of value) {
assert.ok(info instanceof types.SymbolInformation);
assert.equal(info.name, 'testing');
assert.equal(info.kind, types.SymbolKind.Array);
}
assert.equal(value.length, 3);
done();
}, done);
}, done);
});
// --- definition
test('Definition, invalid arguments', function (done) {
let promises = [
commands.executeCommand('vscode.executeDefinitionProvider'),
commands.executeCommand('vscode.executeDefinitionProvider', null),
commands.executeCommand('vscode.executeDefinitionProvider', undefined),
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 () {
disposables.push(extHost.registerDefinitionProvider(defaultSelector, <vscode.DefinitionProvider>{
provideDefinition(doc: any): any {
return new types.Location(doc.uri, new types.Range(0, 0, 0, 0));
}
}));
disposables.push(extHost.registerDefinitionProvider(defaultSelector, <vscode.DefinitionProvider>{
provideDefinition(doc: any): any {
return [
new types.Location(doc.uri, new types.Range(0, 0, 0, 0)),
new types.Location(doc.uri, new types.Range(0, 0, 0, 0)),
new types.Location(doc.uri, new types.Range(0, 0, 0, 0)),
];
}
}));
return threadService.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) {
assert.ok(v.uri instanceof URI);
assert.ok(v.range instanceof types.Range);
}
});
});
});
// --- references
test('reference search, back and forth', function () {
disposables.push(extHost.registerReferenceProvider(defaultSelector, <vscode.ReferenceProvider>{
provideReferences(doc: any) {
return [
new types.Location(URI.parse('some:uri/path'), new types.Range(0, 1, 0, 5))
];
}
}));
return commands.executeCommand<vscode.Location[]>('vscode.executeReferenceProvider', model.uri, new types.Position(0, 0)).then(values => {
assert.equal(values.length, 1);
let [first] = values;
assert.equal(first.uri.toString(), 'some:uri/path');
assert.equal(first.range.start.line, 0);
assert.equal(first.range.start.character, 1);
assert.equal(first.range.end.line, 0);
assert.equal(first.range.end.character, 5);
});
});
// --- outline
test('Outline, back and forth', function (done) {
disposables.push(extHost.registerDocumentSymbolProvider(defaultSelector, <vscode.DocumentSymbolProvider>{
provideDocumentSymbols(): any {
return [
new types.SymbolInformation('testing1', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0)),
new types.SymbolInformation('testing2', types.SymbolKind.Enum, new types.Range(0, 1, 0, 3)),
];
}
}));
threadService.sync().then(() => {
commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeDocumentSymbolProvider', model.uri).then(values => {
assert.equal(values.length, 2);
let [first, second] = values;
assert.equal(first.name, 'testing2');
assert.equal(second.name, 'testing1');
done();
}, done);
}, done);
});
// --- suggest
test('Suggest, back and forth', function () {
disposables.push(extHost.registerCompletionItemProvider(defaultSelector, <vscode.CompletionItemProvider>{
provideCompletionItems(doc, pos): any {
let a = new types.CompletionItem('item1');
let b = new types.CompletionItem('item2');
b.textEdit = types.TextEdit.replace(new types.Range(0, 4, 0, 8), 'foo'); // overwite after
let c = new types.CompletionItem('item3');
c.textEdit = types.TextEdit.replace(new types.Range(0, 1, 0, 6), 'foobar'); // overwite before & after
// snippet string!
let d = new types.CompletionItem('item4');
d.range = new types.Range(0, 1, 0, 4);// overwite before
d.insertText = new types.SnippetString('foo$0bar');
return [a, b, c, d];
}
}, []));
return threadService.sync().then(() => {
return commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => {
assert.ok(list instanceof types.CompletionList);
let values = list.items;
assert.ok(Array.isArray(values));
assert.equal(values.length, 4);
let [first, second, third, fourth] = values;
assert.equal(first.label, 'item1');
assert.equal(first.textEdit.newText, 'item1');
assert.equal(first.textEdit.range.start.line, 0);
assert.equal(first.textEdit.range.start.character, 0);
assert.equal(first.textEdit.range.end.line, 0);
assert.equal(first.textEdit.range.end.character, 4);
assert.equal(second.label, 'item2');
assert.equal(second.textEdit.newText, 'foo');
assert.equal(second.textEdit.range.start.line, 0);
assert.equal(second.textEdit.range.start.character, 4);
assert.equal(second.textEdit.range.end.line, 0);
assert.equal(second.textEdit.range.end.character, 8);
assert.equal(third.label, 'item3');
assert.equal(third.textEdit.newText, 'foobar');
assert.equal(third.textEdit.range.start.line, 0);
assert.equal(third.textEdit.range.start.character, 1);
assert.equal(third.textEdit.range.end.line, 0);
assert.equal(third.textEdit.range.end.character, 6);
assert.equal(fourth.label, 'item4');
assert.equal(fourth.textEdit, undefined);
assert.equal(fourth.range.start.line, 0);
assert.equal(fourth.range.start.character, 1);
assert.equal(fourth.range.end.line, 0);
assert.equal(fourth.range.end.character, 4);
assert.ok(fourth.insertText instanceof types.SnippetString);
assert.equal((<types.SnippetString>fourth.insertText).value, 'foo$0bar');
});
});
});
test('Suggest, return CompletionList !array', function (done) {
disposables.push(extHost.registerCompletionItemProvider(defaultSelector, <vscode.CompletionItemProvider>{
provideCompletionItems(): any {
let a = new types.CompletionItem('item1');
let b = new types.CompletionItem('item2');
return new types.CompletionList(<any>[a, b], true);
}
}, []));
threadService.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);
done();
});
});
});
// --- quickfix
test('QuickFix, back and forth', function () {
disposables.push(extHost.registerCodeActionProvider(defaultSelector, <vscode.CodeActionProvider>{
provideCodeActions(): any {
return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }];
}
}));
return threadService.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;
assert.equal(first.title, 'Title');
assert.equal(first.command, 'testing');
assert.deepEqual(first.arguments, [1, 2, true]);
});
});
});
// --- code lens
test('CodeLens, back and forth', function () {
const complexArg = {
foo() { },
bar() { },
big: extHost
};
disposables.push(extHost.registerCodeLensProvider(defaultSelector, <vscode.CodeLensProvider>{
provideCodeLenses(): any {
return [new types.CodeLens(new types.Range(0, 0, 1, 1), { title: 'Title', command: 'cmd', arguments: [1, true, complexArg] })];
}
}));
return threadService.sync().then(() => {
return commands.executeCommand<vscode.CodeLens[]>('vscode.executeCodeLensProvider', model.uri).then(value => {
assert.equal(value.length, 1);
let [first] = value;
assert.equal(first.command.title, 'Title');
assert.equal(first.command.command, 'cmd');
assert.equal(first.command.arguments[0], 1);
assert.equal(first.command.arguments[1], true);
assert.equal(first.command.arguments[2], complexArg);
});
});
});
test('Links, back and forth', function () {
disposables.push(extHost.registerDocumentLinkProvider(defaultSelector, <vscode.DocumentLinkProvider>{
provideDocumentLinks(): any {
return [new types.DocumentLink(new types.Range(0, 0, 0, 20), URI.parse('foo:bar'))];
}
}));
return threadService.sync().then(() => {
return commands.executeCommand<vscode.DocumentLink[]>('vscode.executeLinkProvider', model.uri).then(value => {
assert.equal(value.length, 1);
let [first] = value;
assert.equal(first.target.toString(), 'foo:bar');
assert.equal(first.range.start.line, 0);
assert.equal(first.range.start.character, 0);
assert.equal(first.range.end.line, 0);
assert.equal(first.range.end.character, 20);
});
});
});
});

View File

@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* 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 * 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 { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostCommands', function () {
test('dispose calls unregister', function () {
let lastUnregister: string;
const shape = new class extends mock<MainThreadCommandsShape>() {
$registerCommand(id: string): TPromise<any> {
return undefined;
}
$unregisterCommand(id: string): TPromise<any> {
lastUnregister = id;
return undefined;
}
};
const commands = new ExtHostCommands(OneGetThreadService(shape), undefined);
commands.registerCommand('foo', (): any => { }).dispose();
assert.equal(lastUnregister, 'foo');
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
});
test('dispose bubbles only once', function () {
let unregisterCounter = 0;
const shape = new class extends mock<MainThreadCommandsShape>() {
$registerCommand(id: string): TPromise<any> {
return undefined;
}
$unregisterCommand(id: string): TPromise<any> {
unregisterCounter += 1;
return undefined;
}
};
const commands = new ExtHostCommands(OneGetThreadService(shape), undefined);
const reg = commands.registerCommand('foo', (): any => { });
reg.dispose();
reg.dispose();
reg.dispose();
assert.equal(unregisterCounter, 1);
});
});

View File

@@ -0,0 +1,405 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { MainThreadConfigurationShape } from 'vs/workbench/api/node/extHost.protocol';
import { TPromise } from 'vs/base/common/winjs.base';
import { ConfigurationTarget, ConfigurationEditingErrorCode, ConfigurationEditingError } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationModel } from 'vs/platform/configuration/common/configuration';
import { TestThreadService } from './testThreadService';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostConfiguration', function () {
class RecordingShape extends mock<MainThreadConfigurationShape>() {
lastArgs: [ConfigurationTarget, string, any];
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise<void> {
this.lastArgs = [target, key, value];
return TPromise.as(void 0);
}
};
function createExtHostConfiguration(contents: any = Object.create(null), shape?: MainThreadConfigurationShape) {
if (!shape) {
shape = new class extends mock<MainThreadConfigurationShape>() { };
}
return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestThreadService(), null), {
defaults: new ConfigurationModel(contents),
user: new ConfigurationModel(contents),
workspace: new ConfigurationModel(),
folders: Object.create(null)
});
}
test('getConfiguration fails regression test 1.7.1 -> 1.8 #15552', function () {
const extHostConfig = createExtHostConfiguration({
'search': {
'exclude': {
'**/node_modules': true
}
}
});
assert.equal(extHostConfig.getConfiguration('search.exclude')['**/node_modules'], true);
assert.equal(extHostConfig.getConfiguration('search.exclude').get('**/node_modules'), true);
assert.equal(extHostConfig.getConfiguration('search').get('exclude')['**/node_modules'], true);
assert.equal(extHostConfig.getConfiguration('search.exclude').has('**/node_modules'), true);
assert.equal(extHostConfig.getConfiguration('search').has('exclude.**/node_modules'), true);
});
test('has/get', function () {
const all = createExtHostConfiguration({
'farboo': {
'config0': true,
'nested': {
'config1': 42,
'config2': 'Das Pferd frisst kein Reis.'
},
'config4': ''
}
});
const config = all.getConfiguration('farboo');
assert.ok(config.has('config0'));
assert.equal(config.get('config0'), true);
assert.equal(config.get('config4'), '');
assert.equal(config['config0'], true);
assert.equal(config['config4'], '');
assert.ok(config.has('nested.config1'));
assert.equal(config.get('nested.config1'), 42);
assert.ok(config.has('nested.config2'));
assert.equal(config.get('nested.config2'), 'Das Pferd frisst kein Reis.');
assert.ok(config.has('nested'));
assert.deepEqual(config.get('nested'), { config1: 42, config2: 'Das Pferd frisst kein Reis.' });
});
test('inspect in no workspace context', function () {
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), null),
{
defaults: new ConfigurationModel({
'editor': {
'wordWrap': 'off'
}
}, ['editor.wordWrap']),
user: new ConfigurationModel({
'editor': {
'wordWrap': 'on'
}
}, ['editor.wordWrap']),
workspace: new ConfigurationModel({}, []),
folders: Object.create(null)
}
);
let actual = testObject.getConfiguration().inspect('editor.wordWrap');
assert.equal(actual.defaultValue, 'off');
assert.equal(actual.globalValue, 'on');
assert.equal(actual.workspaceValue, undefined);
assert.equal(actual.workspaceFolderValue, undefined);
actual = testObject.getConfiguration('editor').inspect('wordWrap');
assert.equal(actual.defaultValue, 'off');
assert.equal(actual.globalValue, 'on');
assert.equal(actual.workspaceValue, undefined);
assert.equal(actual.workspaceFolderValue, undefined);
});
test('inspect in single root context', function () {
const workspaceUri = URI.file('foo');
const folders = Object.create(null);
const workspace = new ConfigurationModel({
'editor': {
'wordWrap': 'bounded'
}
}, ['editor.wordWrap']);
folders[workspaceUri.toString()] = workspace;
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), {
'id': 'foo',
'roots': [URI.file('foo')],
'name': 'foo'
}),
{
defaults: new ConfigurationModel({
'editor': {
'wordWrap': 'off'
}
}, ['editor.wordWrap']),
user: new ConfigurationModel({
'editor': {
'wordWrap': 'on'
}
}, ['editor.wordWrap']),
workspace,
folders
}
);
let actual1 = testObject.getConfiguration().inspect('editor.wordWrap');
assert.equal(actual1.defaultValue, 'off');
assert.equal(actual1.globalValue, 'on');
assert.equal(actual1.workspaceValue, 'bounded');
assert.equal(actual1.workspaceFolderValue, undefined);
actual1 = testObject.getConfiguration('editor').inspect('wordWrap');
assert.equal(actual1.defaultValue, 'off');
assert.equal(actual1.globalValue, 'on');
assert.equal(actual1.workspaceValue, 'bounded');
assert.equal(actual1.workspaceFolderValue, undefined);
let actual2 = testObject.getConfiguration(null, workspaceUri).inspect('editor.wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.equal(actual2.workspaceFolderValue, 'bounded');
actual2 = testObject.getConfiguration('editor', workspaceUri).inspect('wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.equal(actual2.workspaceFolderValue, 'bounded');
});
test('inspect in multi root context', function () {
const workspace = new ConfigurationModel({
'editor': {
'wordWrap': 'bounded'
}
}, ['editor.wordWrap']);
const firstRoot = URI.file('foo1');
const secondRoot = URI.file('foo2');
const thirdRoot = URI.file('foo3');
const folders = Object.create(null);
folders[firstRoot.toString()] = new ConfigurationModel({
'editor': {
'wordWrap': 'off',
'lineNumbers': 'relative'
}
}, ['editor.wordWrap']);
folders[secondRoot.toString()] = new ConfigurationModel({
'editor': {
'wordWrap': 'on'
}
}, ['editor.wordWrap']);
folders[thirdRoot.toString()] = new ConfigurationModel({}, []);
const testObject = new ExtHostConfiguration(
new class extends mock<MainThreadConfigurationShape>() { },
new ExtHostWorkspace(new TestThreadService(), {
'id': 'foo',
'roots': [firstRoot, secondRoot],
'name': 'foo'
}),
{
defaults: new ConfigurationModel({
'editor': {
'wordWrap': 'off',
'lineNumbers': 'on'
}
}, ['editor.wordWrap']),
user: new ConfigurationModel({
'editor': {
'wordWrap': 'on'
}
}, ['editor.wordWrap']),
workspace,
folders
}
);
let actual1 = testObject.getConfiguration().inspect('editor.wordWrap');
assert.equal(actual1.defaultValue, 'off');
assert.equal(actual1.globalValue, 'on');
assert.equal(actual1.workspaceValue, 'bounded');
assert.equal(actual1.workspaceFolderValue, undefined);
actual1 = testObject.getConfiguration('editor').inspect('wordWrap');
assert.equal(actual1.defaultValue, 'off');
assert.equal(actual1.globalValue, 'on');
assert.equal(actual1.workspaceValue, 'bounded');
assert.equal(actual1.workspaceFolderValue, undefined);
actual1 = testObject.getConfiguration('editor').inspect('lineNumbers');
assert.equal(actual1.defaultValue, 'on');
assert.equal(actual1.globalValue, undefined);
assert.equal(actual1.workspaceValue, undefined);
assert.equal(actual1.workspaceFolderValue, undefined);
let actual2 = testObject.getConfiguration(null, firstRoot).inspect('editor.wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.equal(actual2.workspaceFolderValue, 'off');
actual2 = testObject.getConfiguration('editor', firstRoot).inspect('wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.equal(actual2.workspaceFolderValue, 'off');
actual2 = testObject.getConfiguration('editor', firstRoot).inspect('lineNumbers');
assert.equal(actual2.defaultValue, 'on');
assert.equal(actual2.globalValue, undefined);
assert.equal(actual2.workspaceValue, undefined);
assert.equal(actual2.workspaceFolderValue, 'relative');
actual2 = testObject.getConfiguration(null, secondRoot).inspect('editor.wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.equal(actual2.workspaceFolderValue, 'on');
actual2 = testObject.getConfiguration('editor', secondRoot).inspect('wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.equal(actual2.workspaceFolderValue, 'on');
actual2 = testObject.getConfiguration(null, thirdRoot).inspect('editor.wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1);
assert.equal(actual2.workspaceFolderValue, undefined);
actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap');
assert.equal(actual2.defaultValue, 'off');
assert.equal(actual2.globalValue, 'on');
assert.equal(actual2.workspaceValue, 'bounded');
assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1);
assert.equal(actual2.workspaceFolderValue, undefined);
});
test('getConfiguration vs get', function () {
const all = createExtHostConfiguration({
'farboo': {
'config0': true,
'config4': 38
}
});
let config = all.getConfiguration('farboo.config0');
assert.equal(config.get(''), undefined);
assert.equal(config.has(''), false);
config = all.getConfiguration('farboo');
assert.equal(config.get('config0'), true);
assert.equal(config.has('config0'), true);
});
test('getConfiguration vs get', function () {
const all = createExtHostConfiguration({
'farboo': {
'config0': true,
'config4': 38
}
});
let config = all.getConfiguration('farboo.config0');
assert.equal(config.get(''), undefined);
assert.equal(config.has(''), false);
config = all.getConfiguration('farboo');
assert.equal(config.get('config0'), true);
assert.equal(config.has('config0'), true);
});
test('name vs property', function () {
const all = createExtHostConfiguration({
'farboo': {
'get': 'get-prop'
}
});
const config = all.getConfiguration('farboo');
assert.ok(config.has('get'));
assert.equal(config.get('get'), 'get-prop');
assert.deepEqual(config['get'], config.get);
assert.throws(() => config['get'] = <any>'get-prop');
});
test('update: no target passes null', function () {
const shape = new RecordingShape();
const allConfig = createExtHostConfiguration({
'foo': {
'bar': 1,
'far': 1
}
}, shape);
let config = allConfig.getConfiguration('foo');
config.update('bar', 42);
assert.equal(shape.lastArgs[0], null);
});
test('update/section to key', function () {
const shape = new RecordingShape();
const allConfig = createExtHostConfiguration({
'foo': {
'bar': 1,
'far': 1
}
}, shape);
let config = allConfig.getConfiguration('foo');
config.update('bar', 42, true);
assert.equal(shape.lastArgs[0], ConfigurationTarget.USER);
assert.equal(shape.lastArgs[1], 'foo.bar');
assert.equal(shape.lastArgs[2], 42);
config = allConfig.getConfiguration('');
config.update('bar', 42, true);
assert.equal(shape.lastArgs[1], 'bar');
config.update('foo.bar', 42, true);
assert.equal(shape.lastArgs[1], 'foo.bar');
});
test('update, what is #15834', function () {
const shape = new RecordingShape();
const allConfig = createExtHostConfiguration({
'editor': {
'formatOnSave': true
}
}, shape);
allConfig.getConfiguration('editor').update('formatOnSave', { extensions: ['ts'] });
assert.equal(shape.lastArgs[1], 'editor.formatOnSave');
assert.deepEqual(shape.lastArgs[2], { extensions: ['ts'] });
});
test('update/error-state not OK', function () {
const shape = new class extends mock<MainThreadConfigurationShape>() {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise<any> {
return TPromise.wrapError(new ConfigurationEditingError('Unknown Key', ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY)); // something !== OK
}
};
return createExtHostConfiguration({}, shape)
.getConfiguration('')
.update('', true, false)
.then(() => assert.ok(false), err => { /* expecting rejection */ });
});
});

View File

@@ -0,0 +1,263 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import URI 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);
}
$clear(owner: string): TPromise<any> {
return TPromise.as(null);
}
};
test('disposeCheck', function () {
const collection = new DiagnosticCollection('test', new DiagnosticsShape());
collection.dispose();
collection.dispose(); // that's OK
assert.throws(() => collection.name);
assert.throws(() => collection.clear());
assert.throws(() => collection.delete(URI.parse('aa:bb')));
assert.throws(() => collection.forEach(() => { ; }));
assert.throws(() => collection.get(URI.parse('aa:bb')));
assert.throws(() => collection.has(URI.parse('aa:bb')));
assert.throws(() => collection.set(URI.parse('aa:bb'), []));
assert.throws(() => collection.set(URI.parse('aa:bb'), undefined));
});
test('diagnostic collection, forEach, clear, has', function () {
let collection = new DiagnosticCollection('test', new DiagnosticsShape());
assert.equal(collection.name, 'test');
collection.dispose();
assert.throws(() => collection.name);
let c = 0;
collection = new DiagnosticCollection('test', new DiagnosticsShape());
collection.forEach(() => c++);
assert.equal(c, 0);
collection.set(URI.parse('foo:bar'), [
new Diagnostic(new Range(0, 0, 1, 1), 'message-1'),
new Diagnostic(new Range(0, 0, 1, 1), 'message-2')
]);
collection.forEach(() => c++);
assert.equal(c, 1);
c = 0;
collection.clear();
collection.forEach(() => c++);
assert.equal(c, 0);
collection.set(URI.parse('foo:bar1'), [
new Diagnostic(new Range(0, 0, 1, 1), 'message-1'),
new Diagnostic(new Range(0, 0, 1, 1), 'message-2')
]);
collection.set(URI.parse('foo:bar2'), [
new Diagnostic(new Range(0, 0, 1, 1), 'message-1'),
new Diagnostic(new Range(0, 0, 1, 1), 'message-2')
]);
collection.forEach(() => c++);
assert.equal(c, 2);
assert.ok(collection.has(URI.parse('foo:bar1')));
assert.ok(collection.has(URI.parse('foo:bar2')));
assert.ok(!collection.has(URI.parse('foo:bar3')));
collection.delete(URI.parse('foo:bar1'));
assert.ok(!collection.has(URI.parse('foo:bar1')));
collection.dispose();
});
test('diagnostic collection, immutable read', function () {
let collection = new DiagnosticCollection('test', new DiagnosticsShape());
collection.set(URI.parse('foo:bar'), [
new Diagnostic(new Range(0, 0, 1, 1), 'message-1'),
new Diagnostic(new Range(0, 0, 1, 1), 'message-2')
]);
let array = collection.get(URI.parse('foo:bar'));
assert.throws(() => array.length = 0);
assert.throws(() => array.pop());
assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil'));
collection.forEach((uri, array) => {
assert.throws(() => array.length = 0);
assert.throws(() => array.pop());
assert.throws(() => array[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil'));
});
array = collection.get(URI.parse('foo:bar'));
assert.equal(array.length, 2);
collection.dispose();
});
test('diagnostics collection, set with dupliclated tuples', function () {
let collection = new DiagnosticCollection('test', new DiagnosticsShape());
let uri = URI.parse('sc:hightower');
collection.set([
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]],
[URI.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]],
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-2')]],
]);
let array = collection.get(uri);
assert.equal(array.length, 2);
let [first, second] = array;
assert.equal(first.message, 'message-1');
assert.equal(second.message, 'message-2');
// clear
collection.delete(uri);
assert.ok(!collection.has(uri));
// bad tuple clears 1/2
collection.set([
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]],
[URI.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]],
[uri, undefined]
]);
assert.ok(!collection.has(uri));
// clear
collection.delete(uri);
assert.ok(!collection.has(uri));
// bad tuple clears 2/2
collection.set([
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-1')]],
[URI.parse('some:thing'), [new Diagnostic(new Range(0, 0, 1, 1), 'something')]],
[uri, undefined],
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-2')]],
[uri, [new Diagnostic(new Range(0, 0, 0, 1), 'message-3')]],
]);
array = collection.get(uri);
assert.equal(array.length, 2);
[first, second] = array;
assert.equal(first.message, 'message-2');
assert.equal(second.message, 'message-3');
collection.dispose();
});
test('diagnostics collection, set tuple overrides, #11547', function () {
let lastEntries: [URI, IMarkerData[]][];
let collection = new DiagnosticCollection('test', new class extends DiagnosticsShape {
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
lastEntries = entries;
return super.$changeMany(owner, entries);
}
});
let uri = URI.parse('sc:hightower');
collection.set([[uri, [new Diagnostic(new Range(0, 0, 1, 1), 'error')]]]);
assert.equal(collection.get(uri).length, 1);
assert.equal(collection.get(uri)[0].message, 'error');
assert.equal(lastEntries.length, 1);
let [[, data1]] = lastEntries;
assert.equal(data1.length, 1);
assert.equal(data1[0].message, 'error');
lastEntries = undefined;
collection.set([[uri, [new Diagnostic(new Range(0, 0, 1, 1), 'warning')]]]);
assert.equal(collection.get(uri).length, 1);
assert.equal(collection.get(uri)[0].message, 'warning');
assert.equal(lastEntries.length, 1);
let [[, data2]] = lastEntries;
assert.equal(data2.length, 1);
assert.equal(data2[0].message, 'warning');
lastEntries = undefined;
});
test('diagnostics collection, tuples and undefined (small array), #15585', function () {
const collection = new DiagnosticCollection('test', new DiagnosticsShape());
let uri = URI.parse('sc:hightower');
let uri2 = URI.parse('sc:nomad');
let diag = new Diagnostic(new Range(0, 0, 0, 1), 'ffff');
collection.set([
[uri, [diag, diag, diag]],
[uri, undefined],
[uri, [diag]],
[uri2, [diag, diag]],
[uri2, undefined],
[uri2, [diag]],
]);
assert.equal(collection.get(uri).length, 1);
assert.equal(collection.get(uri2).length, 1);
});
test('diagnostics collection, tuples and undefined (large array), #15585', function () {
const collection = new DiagnosticCollection('test', new DiagnosticsShape());
const tuples: [URI, Diagnostic[]][] = [];
for (let i = 0; i < 500; i++) {
let uri = URI.parse('sc:hightower#' + i);
let diag = new Diagnostic(new Range(0, 0, 0, 1), i.toString());
tuples.push([uri, [diag, diag, diag]]);
tuples.push([uri, undefined]);
tuples.push([uri, [diag]]);
}
collection.set(tuples);
for (let i = 0; i < 500; i++) {
let uri = URI.parse('sc:hightower#' + i);
assert.equal(collection.has(uri), true);
assert.equal(collection.get(uri).length, 1);
}
});
test('diagnostic capping', function () {
let lastEntries: [URI, IMarkerData[]][];
let collection = new DiagnosticCollection('test', new class extends DiagnosticsShape {
$changeMany(owner: string, entries: [URI, IMarkerData[]][]): TPromise<any> {
lastEntries = entries;
return super.$changeMany(owner, entries);
}
});
let uri = URI.parse('aa:bb');
let diagnostics: Diagnostic[] = [];
for (let i = 0; i < 500; i++) {
diagnostics.push(new Diagnostic(new Range(i, 0, i + 1, 0), `error#${i}`, i < 300
? DiagnosticSeverity.Warning
: DiagnosticSeverity.Error));
}
collection.set(uri, diagnostics);
assert.equal(collection.get(uri).length, 500);
assert.equal(lastEntries.length, 1);
assert.equal(lastEntries[0][1].length, 251);
assert.equal(lastEntries[0][1][0].severity, Severity.Error);
assert.equal(lastEntries[0][1][200].severity, Severity.Warning);
assert.equal(lastEntries[0][1][250].severity, Severity.Error);
});
});

View File

@@ -0,0 +1,469 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
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 { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostDocumentData', () => {
let data: ExtHostDocumentData;
function assertPositionAt(offset: number, line: number, character: number) {
let position = data.document.positionAt(offset);
assert.equal(position.line, line);
assert.equal(position.character, character);
}
function assertOffsetAt(line: number, character: number, offset: number) {
let pos = new Position(line, character);
let actual = data.document.offsetAt(pos);
assert.equal(actual, offset);
}
setup(function () {
data = new ExtHostDocumentData(undefined, URI.file(''), [
'This is line one', //16
'and this is line number two', //27
'it is followed by #3', //20
'and finished with the fourth.', //29
], '\n', 'text', 1, false);
});
test('readonly-ness', function () {
assert.throws(() => (<any>data).document.uri = null);
assert.throws(() => (<any>data).document.fileName = 'foofile');
assert.throws(() => (<any>data).document.isDirty = false);
assert.throws(() => (<any>data).document.isUntitled = false);
assert.throws(() => (<any>data).document.languageId = 'dddd');
assert.throws(() => (<any>data).document.lineCount = 9);
});
test('save, when disposed', function () {
let saved: URI;
let data = new ExtHostDocumentData(new class extends mock<MainThreadDocumentsShape>() {
$trySaveDocument(uri) {
assert.ok(!saved);
saved = uri;
return TPromise.as(true);
}
}, URI.parse('foo:bar'), [], '\n', 'text', 1, true);
return data.document.save().then(() => {
assert.equal(saved.toString(), 'foo:bar');
data.dispose();
return data.document.save().then(() => {
assert.ok(false, 'expected failure');
}, err => {
assert.ok(err);
});
});
});
test('read, when disposed', function () {
data.dispose();
const { document } = data;
assert.equal(document.lineCount, 4);
assert.equal(document.lineAt(0).text, 'This is line one');
});
test('lines', function () {
assert.equal(data.document.lineCount, 4);
assert.throws(() => data.document.lineAt(-1));
assert.throws(() => data.document.lineAt(data.document.lineCount));
assert.throws(() => data.document.lineAt(Number.MAX_VALUE));
assert.throws(() => data.document.lineAt(Number.MIN_VALUE));
assert.throws(() => data.document.lineAt(0.8));
let line = data.document.lineAt(0);
assert.equal(line.lineNumber, 0);
assert.equal(line.text.length, 16);
assert.equal(line.text, 'This is line one');
assert.equal(line.isEmptyOrWhitespace, false);
assert.equal(line.firstNonWhitespaceCharacterIndex, 0);
data.onEvents({
changes: [{
range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 },
rangeLength: undefined,
text: '\t '
}],
eol: undefined,
versionId: undefined,
});
// line didn't change
assert.equal(line.text, 'This is line one');
assert.equal(line.firstNonWhitespaceCharacterIndex, 0);
// fetch line again
line = data.document.lineAt(0);
assert.equal(line.text, '\t This is line one');
assert.equal(line.firstNonWhitespaceCharacterIndex, 2);
});
test('line, issue #5704', function () {
let line = data.document.lineAt(0);
let { range, rangeIncludingLineBreak } = line;
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 16);
assert.equal(rangeIncludingLineBreak.end.line, 1);
assert.equal(rangeIncludingLineBreak.end.character, 0);
line = data.document.lineAt(data.document.lineCount - 1);
range = line.range;
rangeIncludingLineBreak = line.rangeIncludingLineBreak;
assert.equal(range.end.line, 3);
assert.equal(range.end.character, 29);
assert.equal(rangeIncludingLineBreak.end.line, 3);
assert.equal(rangeIncludingLineBreak.end.character, 29);
});
test('offsetAt', function () {
assertOffsetAt(0, 0, 0);
assertOffsetAt(0, 1, 1);
assertOffsetAt(0, 16, 16);
assertOffsetAt(1, 0, 17);
assertOffsetAt(1, 3, 20);
assertOffsetAt(2, 0, 45);
assertOffsetAt(4, 29, 95);
assertOffsetAt(4, 30, 95);
assertOffsetAt(4, Number.MAX_VALUE, 95);
assertOffsetAt(5, 29, 95);
assertOffsetAt(Number.MAX_VALUE, 29, 95);
assertOffsetAt(Number.MAX_VALUE, Number.MAX_VALUE, 95);
});
test('offsetAt, after remove', function () {
data.onEvents({
changes: [{
range: { startLineNumber: 1, startColumn: 3, endLineNumber: 1, endColumn: 6 },
rangeLength: undefined,
text: ''
}],
eol: undefined,
versionId: undefined,
});
assertOffsetAt(0, 1, 1);
assertOffsetAt(0, 13, 13);
assertOffsetAt(1, 0, 14);
});
test('offsetAt, after replace', function () {
data.onEvents({
changes: [{
range: { startLineNumber: 1, startColumn: 3, endLineNumber: 1, endColumn: 6 },
rangeLength: undefined,
text: 'is could be'
}],
eol: undefined,
versionId: undefined,
});
assertOffsetAt(0, 1, 1);
assertOffsetAt(0, 24, 24);
assertOffsetAt(1, 0, 25);
});
test('offsetAt, after insert line', function () {
data.onEvents({
changes: [{
range: { startLineNumber: 1, startColumn: 3, endLineNumber: 1, endColumn: 6 },
rangeLength: undefined,
text: 'is could be\na line with number'
}],
eol: undefined,
versionId: undefined,
});
assertOffsetAt(0, 1, 1);
assertOffsetAt(0, 13, 13);
assertOffsetAt(1, 0, 14);
assertOffsetAt(1, 18, 13 + 1 + 18);
assertOffsetAt(1, 29, 13 + 1 + 29);
assertOffsetAt(2, 0, 13 + 1 + 29 + 1);
});
test('offsetAt, after remove line', function () {
data.onEvents({
changes: [{
range: { startLineNumber: 1, startColumn: 3, endLineNumber: 2, endColumn: 6 },
rangeLength: undefined,
text: ''
}],
eol: undefined,
versionId: undefined,
});
assertOffsetAt(0, 1, 1);
assertOffsetAt(0, 2, 2);
assertOffsetAt(1, 0, 25);
});
test('positionAt', function () {
assertPositionAt(0, 0, 0);
assertPositionAt(Number.MIN_VALUE, 0, 0);
assertPositionAt(1, 0, 1);
assertPositionAt(16, 0, 16);
assertPositionAt(17, 1, 0);
assertPositionAt(20, 1, 3);
assertPositionAt(45, 2, 0);
assertPositionAt(95, 3, 29);
assertPositionAt(96, 3, 29);
assertPositionAt(99, 3, 29);
assertPositionAt(Number.MAX_VALUE, 3, 29);
});
test('getWordRangeAtPosition', function () {
data = new ExtHostDocumentData(undefined, URI.file(''), [
'aaaa bbbb+cccc abc'
], '\n', 'text', 1, false);
let range = data.document.getWordRangeAtPosition(new Position(0, 2));
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 0);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 4);
// ignore bad regular expresson /.*/
range = data.document.getWordRangeAtPosition(new Position(0, 2), /.*/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 0);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 4);
range = data.document.getWordRangeAtPosition(new Position(0, 5), /[a-z+]+/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 5);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 14);
range = data.document.getWordRangeAtPosition(new Position(0, 17), /[a-z+]+/);
assert.equal(range.start.line, 0);
assert.equal(range.start.character, 15);
assert.equal(range.end.line, 0);
assert.equal(range.end.character, 18);
range = data.document.getWordRangeAtPosition(new Position(0, 11), /yy/);
assert.equal(range, undefined);
});
test('getWordRangeAtPosition doesn\'t quite use the regex as expected, #29102', function () {
data = new ExtHostDocumentData(undefined, URI.file(''), [
'some text here',
'/** foo bar */',
'function() {',
' "far boo"',
'}'
], '\n', 'text', 1, false);
let range = data.document.getWordRangeAtPosition(new Position(0, 0), /\/\*.+\*\//);
assert.equal(range, undefined);
range = data.document.getWordRangeAtPosition(new Position(1, 0), /\/\*.+\*\//);
assert.equal(range.start.line, 1);
assert.equal(range.start.character, 0);
assert.equal(range.end.line, 1);
assert.equal(range.end.character, 14);
range = data.document.getWordRangeAtPosition(new Position(3, 0), /("|').*\1/);
assert.equal(range, undefined);
range = data.document.getWordRangeAtPosition(new Position(3, 1), /("|').*\1/);
assert.equal(range.start.line, 3);
assert.equal(range.start.character, 1);
assert.equal(range.end.line, 3);
assert.equal(range.end.character, 10);
});
});
enum AssertDocumentLineMappingDirection {
OffsetToPosition,
PositionToOffset
}
suite('ExtHostDocumentData updates line mapping', () => {
function positionToStr(position: { line: number; character: number; }): string {
return '(' + position.line + ',' + position.character + ')';
}
function assertDocumentLineMapping(doc: ExtHostDocumentData, direction: AssertDocumentLineMappingDirection): void {
let allText = doc.getText();
let line = 0, character = 0, previousIsCarriageReturn = false;
for (let offset = 0; offset <= allText.length; offset++) {
// The position coordinate system cannot express the position between \r and \n
let position = new Position(line, character + (previousIsCarriageReturn ? -1 : 0));
if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) {
let actualPosition = doc.document.positionAt(offset);
assert.equal(positionToStr(actualPosition), positionToStr(position), 'positionAt mismatch for offset ' + offset);
} else {
// The position coordinate system cannot express the position between \r and \n
let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0);
let actualOffset = doc.document.offsetAt(position);
assert.equal(actualOffset, expectedOffset, 'offsetAt mismatch for position ' + positionToStr(position));
}
if (allText.charAt(offset) === '\n') {
line++;
character = 0;
} else {
character++;
}
previousIsCarriageReturn = (allText.charAt(offset) === '\r');
}
}
function createChangeEvent(range: Range, text: string, eol?: string): IModelChangedEvent {
return {
changes: [{
range: range,
rangeLength: undefined,
text: text
}],
eol: eol,
versionId: undefined,
};
}
function testLineMappingDirectionAfterEvents(lines: string[], eol: string, direction: AssertDocumentLineMappingDirection, e: IModelChangedEvent): void {
let myDocument = new ExtHostDocumentData(undefined, URI.file(''), lines.slice(0), eol, 'text', 1, false);
assertDocumentLineMapping(myDocument, direction);
myDocument.onEvents(e);
assertDocumentLineMapping(myDocument, direction);
}
function testLineMappingAfterEvents(lines: string[], e: IModelChangedEvent): void {
testLineMappingDirectionAfterEvents(lines, '\n', AssertDocumentLineMappingDirection.PositionToOffset, e);
testLineMappingDirectionAfterEvents(lines, '\n', AssertDocumentLineMappingDirection.OffsetToPosition, e);
testLineMappingDirectionAfterEvents(lines, '\r\n', AssertDocumentLineMappingDirection.PositionToOffset, e);
testLineMappingDirectionAfterEvents(lines, '\r\n', AssertDocumentLineMappingDirection.OffsetToPosition, e);
}
test('line mapping', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], { changes: [], eol: undefined, versionId: 7 });
});
test('after remove', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 1, 6), ''));
});
test('after replace', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 1, 6), 'is could be'));
});
test('after insert line', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 1, 6), 'is could be\na line with number'));
});
test('after insert two lines', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 1, 6), 'is could be\na line with number\nyet another line'));
});
test('after remove line', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 2, 6), ''));
});
test('after remove two lines', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 3, 6), ''));
});
test('after deleting entire content', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 4, 30), ''));
});
test('after replacing entire content', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 3, 4, 30), 'some new text\nthat\nspans multiple lines'));
});
test('after changing EOL to CRLF', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 1, 1, 1), '', '\r\n'));
});
test('after changing EOL to LF', () => {
testLineMappingAfterEvents([
'This is line one',
'and this is line number two',
'it is followed by #3',
'and finished with the fourth.',
], createChangeEvent(new Range(1, 1, 1, 1), '', '\n'));
});
});

View File

@@ -0,0 +1,359 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import URI from 'vs/base/common/uri';
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 { MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/node/extHostDocumentSaveParticipant';
import { OneGetThreadService } from './testThreadService';
import { IResourceEdit } from 'vs/editor/common/services/bulkEdit';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostDocumentSaveParticipant', () => {
let resource = URI.parse('foo:bar');
let workspace = new class extends mock<MainThreadWorkspaceShape>() { };
let documents: ExtHostDocuments;
setup(() => {
const documentsAndEditors = new ExtHostDocumentsAndEditors(OneGetThreadService(null));
documentsAndEditors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
isDirty: false,
modeId: 'foo',
url: resource,
versionId: 1,
lines: ['foo'],
EOL: '\n',
}]
});
documents = new ExtHostDocuments(OneGetThreadService(null), documentsAndEditors);
});
test('no listeners, no problem', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true));
});
test('event delivery', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let event: vscode.TextDocumentWillSaveEvent;
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
event = e;
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.ok(event);
assert.equal(event.reason, TextDocumentSaveReason.Manual);
assert.equal(typeof event.waitUntil, 'function');
});
});
test('event delivery, immutable', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let event: vscode.TextDocumentWillSaveEvent;
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
event = e;
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.ok(event);
assert.throws(() => event.document = null);
});
});
test('event delivery, bad listener', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
throw new Error('💀');
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => {
sub.dispose();
const [first] = values;
assert.equal(first, false);
});
});
test('event delivery, bad listener doesn\'t prevent more events', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
throw new Error('💀');
});
let event: vscode.TextDocumentWillSaveEvent;
let sub2 = participant.onWillSaveTextDocumentEvent(function (e) {
event = e;
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub1.dispose();
sub2.dispose();
assert.ok(event);
});
});
test('event delivery, in subscriber order', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let counter = 0;
let sub1 = participant.onWillSaveTextDocumentEvent(function (event) {
assert.equal(counter++, 0);
});
let sub2 = participant.onWillSaveTextDocumentEvent(function (event) {
assert.equal(counter++, 1);
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub1.dispose();
sub2.dispose();
});
});
test('event delivery, ignore bad listeners', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 5, errors: 1 });
let callCount = 0;
let sub = participant.onWillSaveTextDocumentEvent(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)
]).then(values => {
sub.dispose();
assert.equal(callCount, 2);
});
});
test('event delivery, overall timeout', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 20, errors: 5 });
let callCount = 0;
let sub1 = participant.onWillSaveTextDocumentEvent(function (event) {
callCount += 1;
event.waitUntil(TPromise.timeout(17));
});
let sub2 = participant.onWillSaveTextDocumentEvent(function (event) {
callCount += 1;
event.waitUntil(TPromise.timeout(17));
});
let sub3 = participant.onWillSaveTextDocumentEvent(function (event) {
callCount += 1;
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => {
sub1.dispose();
sub2.dispose();
sub3.dispose();
assert.equal(callCount, 2);
assert.equal(values.length, 2);
});
});
test('event delivery, waitUntil', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
event.waitUntil(TPromise.timeout(10));
event.waitUntil(TPromise.timeout(10));
event.waitUntil(TPromise.timeout(10));
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
});
});
test('event delivery, waitUntil must be called sync', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
event.waitUntil(new TPromise((resolve, reject) => {
setTimeout(() => {
try {
assert.throws(() => event.waitUntil(TPromise.timeout(10)));
resolve(void 0);
} catch (e) {
reject(e);
}
}, 10);
}));
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
});
});
test('event delivery, waitUntil will timeout', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace, { timeout: 5, errors: 3 });
let sub = participant.onWillSaveTextDocumentEvent(function (event) {
event.waitUntil(TPromise.timeout(15));
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => {
sub.dispose();
const [first] = values;
assert.equal(first, false);
});
});
test('event delivery, waitUntil failure handling', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, workspace);
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
e.waitUntil(TPromise.wrapError(new Error('dddd')));
});
let event: vscode.TextDocumentWillSaveEvent;
let sub2 = participant.onWillSaveTextDocumentEvent(function (e) {
event = e;
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
assert.ok(event);
sub1.dispose();
sub2.dispose();
});
});
test('event delivery, pushEdits sync', () => {
let edits: IResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadWorkspaceShape>() {
$applyWorkspaceEdit(_edits) {
edits = _edits;
return TPromise.as(true);
}
});
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')]));
e.waitUntil(TPromise.as([TextEdit.setEndOfLine(EndOfLine.CRLF)]));
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.equal(edits.length, 2);
});
});
test('event delivery, concurrent change', () => {
let edits: IResourceEdit[];
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadWorkspaceShape>() {
$applyWorkspaceEdit(_edits) {
edits = _edits;
return TPromise.as(true);
}
});
let sub = participant.onWillSaveTextDocumentEvent(function (e) {
// concurrent change from somewhere
documents.$acceptModelChanged(resource.toString(), {
changes: [{
range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 },
rangeLength: undefined,
text: 'bar'
}],
eol: undefined,
versionId: 2
}, true);
e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')]));
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => {
sub.dispose();
assert.equal(edits, undefined);
assert.equal(values[0], false);
});
});
test('event delivery, two listeners -> two document states', () => {
const participant = new ExtHostDocumentSaveParticipant(documents, new class extends mock<MainThreadWorkspaceShape>() {
$applyWorkspaceEdit(_edits: IResourceEdit[]) {
for (const { resource, newText, range } of _edits) {
documents.$acceptModelChanged(resource.toString(), {
changes: [{
range,
rangeLength: undefined,
text: newText
}],
eol: undefined,
versionId: documents.getDocumentData(resource).version + 1
}, true);
}
return TPromise.as(true);
}
});
const document = documents.getDocumentData(resource).document;
let sub1 = participant.onWillSaveTextDocumentEvent(function (e) {
// the document state we started with
assert.equal(document.version, 1);
assert.equal(document.getText(), 'foo');
e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')]));
});
let sub2 = participant.onWillSaveTextDocumentEvent(function (e) {
// the document state AFTER the first listener kicked in
assert.equal(document.version, 2);
assert.equal(document.getText(), 'barfoo');
e.waitUntil(TPromise.as([TextEdit.insert(new Position(0, 0), 'bar')]));
});
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(values => {
sub1.dispose();
sub2.dispose();
// the document state AFTER eventing is done
assert.equal(document.version, 3);
assert.equal(document.getText(), 'barbarfoo');
});
});
});

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* 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 * 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', () => {
let editors: ExtHostDocumentsAndEditors;
setup(function () {
editors = new ExtHostDocumentsAndEditors({
get() { return undefined; }
});
});
test('The value of TextDocument.isClosed is incorrect when a text document is closed, #27949', () => {
editors.$acceptDocumentsAndEditorsDelta({
addedDocuments: [{
EOL: '\n',
isDirty: true,
modeId: 'fooLang',
url: URI.parse('foo:bar'),
versionId: 1,
lines: [
'first',
'second'
]
}]
});
return new TPromise((resolve, reject) => {
editors.onDidRemoveDocuments(e => {
try {
for (const data of e) {
assert.equal(data.document.isClosed, true);
}
resolve(undefined);
} catch (e) {
reject(e);
}
});
editors.$acceptDocumentsAndEditorsDelta({
removedDocuments: ['foo:bar']
});
});
});
});

View File

@@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { ExtHostFileSystemEventService } from 'vs/workbench/api/node/extHostFileSystemEventService';
suite('ExtHostFileSystemEventService', () => {
test('FileSystemWatcher ignore events properties are reversed #26851', function () {
const watcher1 = new ExtHostFileSystemEventService().createFileSystemWatcher('**/somethingInteresting', false, false, false);
assert.equal(watcher1.ignoreChangeEvents, false);
assert.equal(watcher1.ignoreCreateEvents, false);
assert.equal(watcher1.ignoreDeleteEvents, false);
const watcher2 = new ExtHostFileSystemEventService().createFileSystemWatcher('**/somethingBoring', true, true, true);
assert.equal(watcher2.ignoreChangeEvents, true);
assert.equal(watcher2.ignoreCreateEvents, true);
assert.equal(watcher2.ignoreDeleteEvents, true);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,140 @@
/*---------------------------------------------------------------------------------------------
* 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 * 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';
suite('ExtHostMessageService', function () {
test('propagte handle on select', function () {
let service = new MainThreadMessageService(null, null, <any>{
show(sev: number, m: { message; actions: Action[] }) {
assert.equal(m.actions.length, 1);
setImmediate(() => m.actions[0].run());
return () => { };
}
}, <any>{
choose() {
throw new Error('not implemented');
}
});
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, null, <any>{
show(sev: number, m: { message; actions: Action[] }) {
actions = m.actions;
}
}, <any>{
choose() {
throw new Error('not implemented');
}
});
// 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, null, <any>{
show(sev: number, m: { message; actions: Action[] }) {
c = 0;
actions = m.actions;
return () => {
c += 1;
};
}
}, <any>{
choose() {
throw new Error('not implemented');
}
});
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, null, <any>{
show(sev: number, m: { message; actions: Action[] }) {
throw new Error('not implemented');
}
}, <any>{
choose(severity, message, options, modal) {
assert.equal(severity, 1);
assert.equal(message, 'h');
assert.equal(options.length, 2);
assert.equal(options[1], 'Cancel');
return Promise.as(0);
}
});
return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]).then(handle => {
assert.equal(handle, 42);
});
});
test('returns undefined when cancelled', () => {
const service = new MainThreadMessageService(null, null, <any>{
show(sev: number, m: { message; actions: Action[] }) {
throw new Error('not implemented');
}
}, <any>{
choose(severity, message, options, modal) {
return Promise.as(1);
}
});
return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]).then(handle => {
assert.equal(handle, undefined);
});
});
test('hides Cancel button when not needed', () => {
const service = new MainThreadMessageService(null, null, <any>{
show(sev: number, m: { message; actions: Action[] }) {
throw new Error('not implemented');
}
}, <any>{
choose(severity, message, options, modal) {
assert.equal(options.length, 1);
return Promise.as(0);
}
});
return service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]).then(handle => {
assert.equal(handle, 42);
});
});
});
});

View File

@@ -0,0 +1,349 @@
/*---------------------------------------------------------------------------------------------
* 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 * 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 { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/node/extHostTextEditor';
import { ExtHostDocumentData } from 'vs/workbench/api/node/extHostDocumentData';
import URI from 'vs/base/common/uri';
suite('ExtHostTextEditor', () => {
let editor: ExtHostTextEditor;
setup(() => {
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);
});
test('disposed editor', () => {
assert.ok(editor.document);
editor._acceptViewColumn(3);
assert.equal(3, editor.viewColumn);
editor.dispose();
assert.throws(() => editor._acceptViewColumn(2));
assert.equal(3, editor.viewColumn);
assert.ok(editor.document);
assert.throws(() => editor._acceptOptions(null));
assert.throws(() => editor._acceptSelections([]));
});
});
suite('ExtHostTextEditorOptions', () => {
let opts: ExtHostTextEditorOptions;
let calls: ITextEditorConfigurationUpdate[] = [];
setup(() => {
calls = [];
let mockProxy: MainThreadEditorsShape = {
dispose: undefined,
$trySetOptions: (id: string, options: ITextEditorConfigurationUpdate) => {
assert.equal(id, '1');
calls.push(options);
return TPromise.as(void 0);
},
$tryShowTextDocument: undefined,
$registerTextEditorDecorationType: undefined,
$removeTextEditorDecorationType: undefined,
$tryShowEditor: undefined,
$tryHideEditor: undefined,
$trySetDecorations: undefined,
$tryRevealRange: undefined,
$trySetSelections: undefined,
$tryApplyEdits: undefined,
$tryInsertSnippet: undefined,
$getDiffInformation: undefined
};
opts = new ExtHostTextEditorOptions(mockProxy, '1', {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
});
teardown(() => {
opts = null;
calls = null;
});
function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void {
let actual = {
tabSize: opts.tabSize,
insertSpaces: opts.insertSpaces,
cursorStyle: opts.cursorStyle,
lineNumbers: opts.lineNumbers
};
assert.deepEqual(actual, expected);
}
test('can set tabSize to the same value', () => {
opts.tabSize = 4;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can change tabSize to positive integer', () => {
opts.tabSize = 1;
assertState(opts, {
tabSize: 1,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ tabSize: 1 }]);
});
test('can change tabSize to positive float', () => {
opts.tabSize = 2.3;
assertState(opts, {
tabSize: 2,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ tabSize: 2 }]);
});
test('can change tabSize to a string number', () => {
opts.tabSize = '2';
assertState(opts, {
tabSize: 2,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ tabSize: 2 }]);
});
test('tabSize can request indentation detection', () => {
opts.tabSize = 'auto';
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ tabSize: 'auto' }]);
});
test('ignores invalid tabSize 1', () => {
opts.tabSize = null;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('ignores invalid tabSize 2', () => {
opts.tabSize = -5;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('ignores invalid tabSize 3', () => {
opts.tabSize = 'hello';
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('ignores invalid tabSize 4', () => {
opts.tabSize = '-17';
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can set insertSpaces to the same value', () => {
opts.insertSpaces = false;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can set insertSpaces to boolean', () => {
opts.insertSpaces = true;
assertState(opts, {
tabSize: 4,
insertSpaces: true,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ insertSpaces: true }]);
});
test('can set insertSpaces to false string', () => {
opts.insertSpaces = 'false';
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can set insertSpaces to truey', () => {
opts.insertSpaces = 'hello';
assertState(opts, {
tabSize: 4,
insertSpaces: true,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ insertSpaces: true }]);
});
test('insertSpaces can request indentation detection', () => {
opts.insertSpaces = 'auto';
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ insertSpaces: 'auto' }]);
});
test('can set cursorStyle to same value', () => {
opts.cursorStyle = TextEditorCursorStyle.Line;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can change cursorStyle', () => {
opts.cursorStyle = TextEditorCursorStyle.Block;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Block,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block }]);
});
test('can set lineNumbers to same value', () => {
opts.lineNumbers = TextEditorLineNumbersStyle.On;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can change lineNumbers', () => {
opts.lineNumbers = TextEditorLineNumbersStyle.Off;
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.Off
});
assert.deepEqual(calls, [{ lineNumbers: TextEditorLineNumbersStyle.Off }]);
});
test('can do bulk updates 0', () => {
opts.assign({
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, []);
});
test('can do bulk updates 1', () => {
opts.assign({
tabSize: 'auto',
insertSpaces: true
});
assertState(opts, {
tabSize: 4,
insertSpaces: true,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ tabSize: 'auto', insertSpaces: true }]);
});
test('can do bulk updates 2', () => {
opts.assign({
tabSize: 3,
insertSpaces: 'auto'
});
assertState(opts, {
tabSize: 3,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Line,
lineNumbers: TextEditorLineNumbersStyle.On
});
assert.deepEqual(calls, [{ tabSize: 3, insertSpaces: 'auto' }]);
});
test('can do bulk updates 3', () => {
opts.assign({
cursorStyle: TextEditorCursorStyle.Block,
lineNumbers: TextEditorLineNumbersStyle.Relative
});
assertState(opts, {
tabSize: 4,
insertSpaces: false,
cursorStyle: TextEditorCursorStyle.Block,
lineNumbers: TextEditorLineNumbersStyle.Relative
});
assert.deepEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block, lineNumbers: TextEditorLineNumbersStyle.Relative }]);
});
});

View File

@@ -0,0 +1,144 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
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 { 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';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
suite('ExtHostConfiguration', function () {
class RecordingShape extends mock<MainThreadTreeViewsShape>() {
onRefresh = new Emitter<number[]>();
$registerView(treeViewId: string): void {
}
$refresh(viewId: string, itemHandles: number[]): void {
this.onRefresh.fire(itemHandles);
}
};
let testObject: ExtHostTreeViews;
let target: RecordingShape;
let onDidChangeTreeData: Emitter<string>;
setup(() => {
let threadService = new TestThreadService();
// Use IInstantiationService to get typechecking when instantiating
let inst: IInstantiationService;
{
let instantiationService = new TestInstantiationService();
inst = instantiationService;
}
threadService.setTestInstance(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, threadService));
target = new RecordingShape();
testObject = new ExtHostTreeViews(target, new ExtHostCommands(threadService, new ExtHostHeapService()));
onDidChangeTreeData = new Emitter<string>();
testObject.registerTreeDataProvider('testDataProvider', aTreeDataProvider());
testObject.$getElements('testDataProvider').then(elements => {
for (const element of elements) {
testObject.$getChildren('testDataProvider', element.handle);
}
});
});
test('refresh calls are throttled on roots', function (done) {
target.onRefresh.event(actuals => {
assert.equal(0, actuals.length);
done();
});
onDidChangeTreeData.fire();
onDidChangeTreeData.fire();
onDidChangeTreeData.fire();
onDidChangeTreeData.fire();
});
test('refresh calls are throttled on elements', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual([1, 2], actuals);
done();
});
onDidChangeTreeData.fire('a');
onDidChangeTreeData.fire('b');
onDidChangeTreeData.fire('b');
onDidChangeTreeData.fire('a');
});
test('refresh calls are throttled on unknown elements', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual([1, 2], actuals);
done();
});
onDidChangeTreeData.fire('a');
onDidChangeTreeData.fire('b');
onDidChangeTreeData.fire('g');
onDidChangeTreeData.fire('a');
});
test('refresh calls are throttled on unknown elements and root', function (done) {
target.onRefresh.event(actuals => {
assert.equal(0, actuals.length);
done();
});
onDidChangeTreeData.fire('a');
onDidChangeTreeData.fire('b');
onDidChangeTreeData.fire('g');
onDidChangeTreeData.fire('');
});
test('refresh calls are throttled on elements and root', function (done) {
target.onRefresh.event(actuals => {
assert.equal(0, actuals.length);
done();
});
onDidChangeTreeData.fire('a');
onDidChangeTreeData.fire('b');
onDidChangeTreeData.fire();
onDidChangeTreeData.fire('a');
});
function aTreeDataProvider(): TreeDataProvider<string> {
return <TreeDataProvider<string>>{
getChildren: (element: string): string[] => {
if (!element) {
return ['a', 'b'];
}
if (element === 'a') {
return ['aa', 'ab'];
}
if (element === 'b') {
return ['ba', 'bb'];
}
return [];
},
getTreeItem: (element: string): TreeItem => {
return <TreeItem>{
label: element
};
},
onDidChangeTreeData: onDidChangeTreeData.event
};
}
});

View File

@@ -0,0 +1,469 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import URI from 'vs/base/common/uri';
import * as types from 'vs/workbench/api/node/extHostTypes';
import { isWindows } from 'vs/base/common/platform';
function assertToJSON(a: any, expected: any) {
const raw = JSON.stringify(a);
const actual = JSON.parse(raw);
assert.deepEqual(actual, expected);
}
suite('ExtHostTypes', function () {
test('URI, toJSON', function () {
let uri = URI.parse('file:///path/test.file');
let data = uri.toJSON();
assert.deepEqual(data, {
$mid: 1,
scheme: 'file',
path: '/path/test.file',
fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'),
external: 'file:///path/test.file'
});
});
test('Disposable', function () {
let count = 0;
let d = new types.Disposable(() => {
count += 1;
return 12;
});
d.dispose();
assert.equal(count, 1);
d.dispose();
assert.equal(count, 1);
types.Disposable.from(undefined, { dispose() { count += 1; } }).dispose();
assert.equal(count, 2);
assert.throws(() => {
new types.Disposable(() => {
throw new Error();
}).dispose();
});
new types.Disposable(undefined).dispose();
});
test('Position', function () {
assert.throws(() => new types.Position(-1, 0));
assert.throws(() => new types.Position(0, -1));
let pos = new types.Position(0, 0);
assert.throws(() => (pos as any).line = -1);
assert.throws(() => (pos as any).character = -1);
assert.throws(() => (pos as any).line = 12);
let { line, character } = pos.toJSON();
assert.equal(line, 0);
assert.equal(character, 0);
});
test('Position, toJSON', function () {
let pos = new types.Position(4, 2);
assertToJSON(pos, { line: 4, character: 2 });
});
test('Position, isBefore(OrEqual)?', function () {
let p1 = new types.Position(1, 3);
let p2 = new types.Position(1, 2);
let p3 = new types.Position(0, 4);
assert.ok(p1.isBeforeOrEqual(p1));
assert.ok(!p1.isBefore(p1));
assert.ok(p2.isBefore(p1));
assert.ok(p3.isBefore(p2));
});
test('Position, isAfter(OrEqual)?', function () {
let p1 = new types.Position(1, 3);
let p2 = new types.Position(1, 2);
let p3 = new types.Position(0, 4);
assert.ok(p1.isAfterOrEqual(p1));
assert.ok(!p1.isAfter(p1));
assert.ok(p1.isAfter(p2));
assert.ok(p2.isAfter(p3));
assert.ok(p1.isAfter(p3));
});
test('Position, compareTo', function () {
let p1 = new types.Position(1, 3);
let p2 = new types.Position(1, 2);
let p3 = new types.Position(0, 4);
assert.equal(p1.compareTo(p1), 0);
assert.equal(p2.compareTo(p1), -1);
assert.equal(p1.compareTo(p2), 1);
assert.equal(p2.compareTo(p3), 1);
assert.equal(p1.compareTo(p3), 1);
});
test('Position, translate', function () {
let p1 = new types.Position(1, 3);
assert.ok(p1.translate() === p1);
assert.ok(p1.translate({}) === p1);
assert.ok(p1.translate(0, 0) === p1);
assert.ok(p1.translate(0) === p1);
assert.ok(p1.translate(undefined, 0) === p1);
assert.ok(p1.translate(undefined) === p1);
let res = p1.translate(-1);
assert.equal(res.line, 0);
assert.equal(res.character, 3);
res = p1.translate({ lineDelta: -1 });
assert.equal(res.line, 0);
assert.equal(res.character, 3);
res = p1.translate(undefined, -1);
assert.equal(res.line, 1);
assert.equal(res.character, 2);
res = p1.translate({ characterDelta: -1 });
assert.equal(res.line, 1);
assert.equal(res.character, 2);
res = p1.translate(11);
assert.equal(res.line, 12);
assert.equal(res.character, 3);
assert.throws(() => p1.translate(null));
assert.throws(() => p1.translate(null, null));
assert.throws(() => p1.translate(-2));
assert.throws(() => p1.translate({ lineDelta: -2 }));
assert.throws(() => p1.translate(-2, null));
assert.throws(() => p1.translate(0, -4));
});
test('Position, with', function () {
let p1 = new types.Position(1, 3);
assert.ok(p1.with() === p1);
assert.ok(p1.with(1) === p1);
assert.ok(p1.with(undefined, 3) === p1);
assert.ok(p1.with(1, 3) === p1);
assert.ok(p1.with(undefined) === p1);
assert.ok(p1.with({ line: 1 }) === p1);
assert.ok(p1.with({ character: 3 }) === p1);
assert.ok(p1.with({ line: 1, character: 3 }) === p1);
let p2 = p1.with({ line: 0, character: 11 });
assert.equal(p2.line, 0);
assert.equal(p2.character, 11);
assert.throws(() => p1.with(null));
assert.throws(() => p1.with(-9));
assert.throws(() => p1.with(0, -9));
assert.throws(() => p1.with({ line: -1 }));
assert.throws(() => p1.with({ character: -1 }));
});
test('Range', function () {
assert.throws(() => new types.Range(-1, 0, 0, 0));
assert.throws(() => new types.Range(0, -1, 0, 0));
assert.throws(() => new types.Range(new types.Position(0, 0), undefined));
assert.throws(() => new types.Range(new types.Position(0, 0), null));
assert.throws(() => new types.Range(undefined, new types.Position(0, 0)));
assert.throws(() => new types.Range(null, new types.Position(0, 0)));
let range = new types.Range(1, 0, 0, 0);
assert.throws(() => (range as any).start = null);
assert.throws(() => (range as any).start = new types.Position(0, 3));
});
test('Range, toJSON', function () {
let range = new types.Range(1, 2, 3, 4);
assertToJSON(range, [{ line: 1, character: 2 }, { line: 3, character: 4 }]);
});
test('Range, sorting', function () {
// sorts start/end
let range = new types.Range(1, 0, 0, 0);
assert.equal(range.start.line, 0);
assert.equal(range.end.line, 1);
range = new types.Range(0, 0, 1, 0);
assert.equal(range.start.line, 0);
assert.equal(range.end.line, 1);
});
test('Range, isEmpty|isSingleLine', function () {
let range = new types.Range(1, 0, 0, 0);
assert.ok(!range.isEmpty);
assert.ok(!range.isSingleLine);
range = new types.Range(1, 1, 1, 1);
assert.ok(range.isEmpty);
assert.ok(range.isSingleLine);
range = new types.Range(0, 1, 0, 11);
assert.ok(!range.isEmpty);
assert.ok(range.isSingleLine);
range = new types.Range(0, 0, 1, 1);
assert.ok(!range.isEmpty);
assert.ok(!range.isSingleLine);
});
test('Range, contains', function () {
let range = new types.Range(1, 1, 2, 11);
assert.ok(range.contains(range.start));
assert.ok(range.contains(range.end));
assert.ok(range.contains(range));
assert.ok(!range.contains(new types.Range(1, 0, 2, 11)));
assert.ok(!range.contains(new types.Range(0, 1, 2, 11)));
assert.ok(!range.contains(new types.Range(1, 1, 2, 12)));
assert.ok(!range.contains(new types.Range(1, 1, 3, 11)));
});
test('Range, intersection', function () {
let range = new types.Range(1, 1, 2, 11);
let res: types.Range;
res = range.intersection(range);
assert.equal(res.start.line, 1);
assert.equal(res.start.character, 1);
assert.equal(res.end.line, 2);
assert.equal(res.end.character, 11);
res = range.intersection(new types.Range(2, 12, 4, 0));
assert.equal(res, undefined);
res = range.intersection(new types.Range(0, 0, 1, 0));
assert.equal(res, undefined);
res = range.intersection(new types.Range(0, 0, 1, 1));
assert.ok(res.isEmpty);
assert.equal(res.start.line, 1);
assert.equal(res.start.character, 1);
res = range.intersection(new types.Range(2, 11, 61, 1));
assert.ok(res.isEmpty);
assert.equal(res.start.line, 2);
assert.equal(res.start.character, 11);
assert.throws(() => range.intersection(null));
assert.throws(() => range.intersection(undefined));
});
test('Range, union', function () {
let ran1 = new types.Range(0, 0, 5, 5);
assert.ok(ran1.union(new types.Range(0, 0, 1, 1)) === ran1);
let res: types.Range;
res = ran1.union(new types.Range(2, 2, 9, 9));
assert.ok(res.start === ran1.start);
assert.equal(res.end.line, 9);
assert.equal(res.end.character, 9);
ran1 = new types.Range(2, 1, 5, 3);
res = ran1.union(new types.Range(1, 0, 4, 2));
assert.ok(res.end === ran1.end);
assert.equal(res.start.line, 1);
assert.equal(res.start.character, 0);
});
test('Range, with', function () {
let range = new types.Range(1, 1, 2, 11);
assert.ok(range.with(range.start) === range);
assert.ok(range.with(undefined, range.end) === range);
assert.ok(range.with(range.start, range.end) === range);
assert.ok(range.with(new types.Position(1, 1)) === range);
assert.ok(range.with(undefined, new types.Position(2, 11)) === range);
assert.ok(range.with() === range);
assert.ok(range.with({ start: range.start }) === range);
assert.ok(range.with({ start: new types.Position(1, 1) }) === range);
assert.ok(range.with({ end: range.end }) === range);
assert.ok(range.with({ end: new types.Position(2, 11) }) === range);
let res = range.with(undefined, new types.Position(9, 8));
assert.equal(res.end.line, 9);
assert.equal(res.end.character, 8);
assert.equal(res.start.line, 1);
assert.equal(res.start.character, 1);
res = range.with({ end: new types.Position(9, 8) });
assert.equal(res.end.line, 9);
assert.equal(res.end.character, 8);
assert.equal(res.start.line, 1);
assert.equal(res.start.character, 1);
res = range.with({ end: new types.Position(9, 8), start: new types.Position(2, 3) });
assert.equal(res.end.line, 9);
assert.equal(res.end.character, 8);
assert.equal(res.start.line, 2);
assert.equal(res.start.character, 3);
assert.throws(() => range.with(null));
assert.throws(() => range.with(undefined, null));
});
test('TextEdit', function () {
let range = new types.Range(1, 1, 2, 11);
let edit = new types.TextEdit(range, undefined);
assert.equal(edit.newText, '');
assertToJSON(edit, { range: [{ line: 1, character: 1 }, { line: 2, character: 11 }], newText: '' });
edit = new types.TextEdit(range, null);
assert.equal(edit.newText, '');
edit = new types.TextEdit(range, '');
assert.equal(edit.newText, '');
});
test('WorkspaceEdit', function () {
let a = URI.file('a.ts');
let b = URI.file('b.ts');
let edit = new types.WorkspaceEdit();
assert.ok(!edit.has(a));
edit.set(a, [types.TextEdit.insert(new types.Position(0, 0), 'fff')]);
assert.ok(edit.has(a));
assert.equal(edit.size, 1);
assertToJSON(edit, [[URI.parse('file:///a.ts').toJSON(), [{ range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: 'fff' }]]]);
edit.insert(b, new types.Position(1, 1), 'fff');
edit.delete(b, new types.Range(0, 0, 0, 0));
assert.ok(edit.has(b));
assert.equal(edit.size, 2);
assertToJSON(edit, [
[URI.parse('file:///a.ts').toJSON(), [{ range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: 'fff' }]],
[URI.parse('file:///b.ts').toJSON(), [{ range: [{ line: 1, character: 1 }, { line: 1, character: 1 }], newText: 'fff' }, { range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: '' }]]
]);
edit.set(b, undefined);
assert.ok(edit.has(b));
assert.equal(edit.size, 2);
edit.set(b, [types.TextEdit.insert(new types.Position(0, 0), 'ffff')]);
assert.equal(edit.get(b).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));
});
test('toJSON & stringify', function () {
assertToJSON(new types.Selection(3, 4, 2, 1), { start: { line: 2, character: 1 }, end: { line: 3, character: 4 }, anchor: { line: 3, character: 4 }, active: { line: 2, character: 1 } });
assertToJSON(new types.Location(URI.file('u.ts'), new types.Position(3, 4)), { uri: URI.parse('file:///u.ts').toJSON(), range: [{ line: 3, character: 4 }, { line: 3, character: 4 }] });
assertToJSON(new types.Location(URI.file('u.ts'), new types.Range(1, 2, 3, 4)), { uri: URI.parse('file:///u.ts').toJSON(), range: [{ line: 1, character: 2 }, { line: 3, character: 4 }] });
let diag = new types.Diagnostic(new types.Range(0, 1, 2, 3), 'hello');
assertToJSON(diag, { severity: 'Error', message: 'hello', range: [{ line: 0, character: 1 }, { line: 2, character: 3 }] });
diag.source = 'me';
assertToJSON(diag, { severity: 'Error', message: 'hello', range: [{ line: 0, character: 1 }, { line: 2, character: 3 }], source: 'me' });
assertToJSON(new types.DocumentHighlight(new types.Range(2, 3, 4, 5)), { range: [{ line: 2, character: 3 }, { line: 4, character: 5 }], kind: 'Text' });
assertToJSON(new types.DocumentHighlight(new types.Range(2, 3, 4, 5), types.DocumentHighlightKind.Read), { range: [{ line: 2, character: 3 }, { line: 4, character: 5 }], kind: 'Read' });
assertToJSON(new types.SymbolInformation('test', types.SymbolKind.Boolean, new types.Range(0, 1, 2, 3)), {
name: 'test',
kind: 'Boolean',
location: {
range: [{ line: 0, character: 1 }, { line: 2, character: 3 }]
}
});
assertToJSON(new types.CodeLens(new types.Range(7, 8, 9, 10)), { range: [{ line: 7, character: 8 }, { line: 9, character: 10 }] });
assertToJSON(new types.CodeLens(new types.Range(7, 8, 9, 10), { command: 'id', title: 'title' }), {
range: [{ line: 7, character: 8 }, { line: 9, character: 10 }],
command: { command: 'id', title: 'title' }
});
assertToJSON(new types.CompletionItem('complete'), { label: 'complete' });
let item = new types.CompletionItem('complete');
item.kind = types.CompletionItemKind.Interface;
assertToJSON(item, { label: 'complete', kind: 'Interface' });
});
test('SymbolInformation, old ctor', function () {
let info = new types.SymbolInformation('foo', types.SymbolKind.Array, new types.Range(1, 1, 2, 3));
assert.ok(info.location instanceof types.Location);
assert.equal(info.location.uri, undefined);
});
test('SnippetString, builder-methods', function () {
let string: types.SnippetString;
string = new types.SnippetString();
assert.equal(string.appendText('I need $ and $').value, 'I need \\$ and \\$');
string = new types.SnippetString();
assert.equal(string.appendText('I need \\$').value, 'I need \\\\\\$');
string = new types.SnippetString();
string.appendPlaceholder('fo$o}');
assert.equal(string.value, '${1:fo\\$o\\}}');
string = new types.SnippetString();
string.appendText('foo').appendTabstop(0).appendText('bar');
assert.equal(string.value, 'foo$0bar');
string = new types.SnippetString();
string.appendText('foo').appendTabstop().appendText('bar');
assert.equal(string.value, 'foo$1bar');
string = new types.SnippetString();
string.appendText('foo').appendTabstop(42).appendText('bar');
assert.equal(string.value, 'foo$42bar');
string = new types.SnippetString();
string.appendText('foo').appendPlaceholder('farboo').appendText('bar');
assert.equal(string.value, 'foo${1:farboo}bar');
string = new types.SnippetString();
string.appendText('foo').appendPlaceholder('far$boo').appendText('bar');
assert.equal(string.value, 'foo${1:far\\$boo}bar');
string = new types.SnippetString();
string.appendText('foo').appendPlaceholder(b => b.appendText('abc').appendPlaceholder('nested')).appendText('bar');
assert.equal(string.value, 'foo${1:abc${2:nested}}bar');
string = new types.SnippetString();
string.appendVariable('foo');
assert.equal(string.value, '${foo}');
string = new types.SnippetString();
string.appendText('foo').appendVariable('TM_SELECTED_TEXT').appendText('bar');
assert.equal(string.value, 'foo${TM_SELECTED_TEXT}bar');
string = new types.SnippetString();
string.appendVariable('BAR', b => b.appendPlaceholder('ops'));
assert.equal(string.value, '${BAR:${1:ops}}');
string = new types.SnippetString();
string.appendVariable('BAR', b => { });
assert.equal(string.value, '${BAR}');
});
});

View File

@@ -0,0 +1,175 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import URI from 'vs/base/common/uri';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { TestThreadService } from './testThreadService';
suite('ExtHostWorkspace', function () {
test('asRelativePath', function () {
const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/Applications/NewsWoWBot')], name: 'Test' });
assert.equal(ws.getRelativePath('/Coding/Applications/NewsWoWBot/bernd/das/brot'), 'bernd/das/brot');
assert.equal(ws.getRelativePath('/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');
assert.equal(ws.getRelativePath(''), '');
assert.equal(ws.getRelativePath('/foo/bar'), '/foo/bar');
assert.equal(ws.getRelativePath('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 TestThreadService(), { id: 'foo', roots: [URI.file(root)], name: 'Test' });
assert.equal(ws.getRelativePath(input), input);
const input2 = '/home/aeschli/workspaces/samples/docker/a.file';
assert.equal(ws.getRelativePath(input2), 'a.file');
});
test('asRelativePath, no workspace', function () {
const ws = new ExtHostWorkspace(new TestThreadService(), null);
assert.equal(ws.getRelativePath(''), '');
assert.equal(ws.getRelativePath('/foo/bar'), '/foo/bar');
});
test('asRelativePath, multiple folders', function () {
const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' });
assert.equal(ws.getRelativePath('/Coding/One/file.txt'), 'One/file.txt');
assert.equal(ws.getRelativePath('/Coding/Two/files/out.txt'), 'Two/files/out.txt');
assert.equal(ws.getRelativePath('/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 TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' });
assert.equal(mrws.getRelativePath('/Coding/One/file.txt'), 'One/file.txt');
assert.equal(mrws.getRelativePath('/Coding/One/file.txt', true), 'One/file.txt');
assert.equal(mrws.getRelativePath('/Coding/One/file.txt', false), 'file.txt');
assert.equal(mrws.getRelativePath('/Coding/Two/files/out.txt'), 'Two/files/out.txt');
assert.equal(mrws.getRelativePath('/Coding/Two/files/out.txt', true), 'Two/files/out.txt');
assert.equal(mrws.getRelativePath('/Coding/Two/files/out.txt', false), 'files/out.txt');
assert.equal(mrws.getRelativePath('/Coding/Two2/files/out.txt'), '/Coding/Two2/files/out.txt');
assert.equal(mrws.getRelativePath('/Coding/Two2/files/out.txt', true), '/Coding/Two2/files/out.txt');
assert.equal(mrws.getRelativePath('/Coding/Two2/files/out.txt', false), '/Coding/Two2/files/out.txt');
const srws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One')], name: 'Test' });
assert.equal(srws.getRelativePath('/Coding/One/file.txt'), 'file.txt');
assert.equal(srws.getRelativePath('/Coding/One/file.txt', false), 'file.txt');
assert.equal(srws.getRelativePath('/Coding/One/file.txt', true), 'One/file.txt');
assert.equal(srws.getRelativePath('/Coding/Two2/files/out.txt'), '/Coding/Two2/files/out.txt');
assert.equal(srws.getRelativePath('/Coding/Two2/files/out.txt', true), '/Coding/Two2/files/out.txt');
assert.equal(srws.getRelativePath('/Coding/Two2/files/out.txt', false), '/Coding/Two2/files/out.txt');
});
test('getPath, legacy', function () {
let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] });
assert.equal(ws.getPath(), undefined);
ws = new ExtHostWorkspace(new TestThreadService(), null);
assert.equal(ws.getPath(), undefined);
ws = new ExtHostWorkspace(new TestThreadService(), undefined);
assert.equal(ws.getPath(), undefined);
ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('Folder'), URI.file('Another/Folder')] });
assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder');
ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('/Folder')] });
assert.equal(ws.getPath().replace(/\\/g, '/'), '/Folder');
});
test('WorkspaceFolder has name and index', function () {
const ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two')], name: 'Test' });
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 TestThreadService(), { id: 'foo', name: 'Test', roots: [URI.file('/Coding/One'), URI.file('/Coding/Two'), URI.file('/Coding/Two/Nested')] });
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'));
assert.equal(folder.name, 'Two');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/'));
assert.equal(folder.name, 'Two');
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two'));
assert.equal(folder, undefined);
});
test('Multiroot change event should have a delta, #29641', function () {
let ws = new ExtHostWorkspace(new TestThreadService(), { id: 'foo', name: 'Test', roots: [] });
let sub = ws.onDidChangeWorkspace(e => {
assert.deepEqual(e.added, []);
assert.deepEqual(e.removed, []);
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [] });
sub.dispose();
sub = ws.onDidChangeWorkspace(e => {
assert.deepEqual(e.removed, []);
assert.equal(e.added.length, 1);
assert.equal(e.added[0].uri.toString(), 'foo:bar');
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar')] });
sub.dispose();
sub = ws.onDidChangeWorkspace(e => {
assert.deepEqual(e.removed, []);
assert.equal(e.added.length, 1);
assert.equal(e.added[0].uri.toString(), 'foo:bar2');
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar'), URI.parse('foo:bar2')] });
sub.dispose();
sub = ws.onDidChangeWorkspace(e => {
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');
});
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', roots: [URI.parse('foo:bar3')] });
sub.dispose();
});
test('Multiroot change event is immutable', function () {
assert.equal(1, 1);
});
});

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* 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 * 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';
suite('MainThreadCommands', function () {
test('dispose on unregister', function () {
const commands = new MainThreadCommands(OneGetThreadService(null), undefined);
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
// register
commands.$registerCommand('foo');
assert.ok(CommandsRegistry.getCommand('foo'));
// unregister
commands.$unregisterCommand('foo');
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
});
test('unregister all on dispose', function () {
const commands = new MainThreadCommands(OneGetThreadService(null), undefined);
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
commands.$registerCommand('foo');
commands.$registerCommand('bar');
assert.ok(CommandsRegistry.getCommand('foo'));
assert.ok(CommandsRegistry.getCommand('bar'));
commands.dispose();
assert.equal(CommandsRegistry.getCommand('foo'), undefined);
assert.equal(CommandsRegistry.getCommand('bar'), undefined);
});
});

View File

@@ -0,0 +1,228 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import * as sinon from 'sinon';
import URI from 'vs/base/common/uri';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions, IConfigurationRegistry, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IWorkspaceContextService } 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 { ConfigurationTarget, IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { OneGetThreadService } from './testThreadService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
suite('ExtHostConfiguration', function () {
let instantiationService: TestInstantiationService;
let target: sinon.SinonSpy;
suiteSetup(() => {
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': 'extHostConfiguration',
'title': 'a',
'type': 'object',
'properties': {
'extHostConfiguration.resource': {
'description': 'extHostConfiguration.resource',
'type': 'boolean',
'default': true,
'scope': ConfigurationScope.RESOURCE
},
'extHostConfiguration.window': {
'description': 'extHostConfiguration.resource',
'type': 'boolean',
'default': true,
'scope': ConfigurationScope.WINDOW
}
}
});
});
setup(() => {
instantiationService = new TestInstantiationService();
instantiationService.stub(IConfigurationService, new TestConfigurationService());
target = sinon.spy();
instantiationService.stub(IConfigurationEditingService, ConfigurationEditingService);
instantiationService.stub(IConfigurationEditingService, 'writeConfiguration', target);
});
test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update resource configuration without configuration target defaults to folder', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]);
});
test('update configuration with user configuration target', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.USER, target.args[0][0]);
});
test('update configuration with workspace configuration target', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('update configuration with folder configuration target', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$updateConfigurationOption(ConfigurationTarget.FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc'));
assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]);
});
test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc'));
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => false });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null);
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][0]);
});
test('remove configuration without configuration target defaults to folder', function () {
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ hasMultiFolderWorkspace: () => true });
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, OneGetThreadService(null));
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc'));
assert.equal(ConfigurationTarget.FOLDER, target.args[0][0]);
});
});

View File

@@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { MainThreadDiagnostics } from 'vs/workbench/api/electron-browser/mainThreadDiagnostics';
import URI from 'vs/base/common/uri';
suite('MainThreadDiagnostics', function () {
let markerService: MarkerService;
setup(function () {
markerService = new MarkerService();
});
test('clear markers on dispose', function () {
let diag = new MainThreadDiagnostics(null, markerService);
diag.$changeMany('foo', [[URI.file('a'), [{
code: '666',
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: 1,
message: 'fffff',
severity: 1,
source: 'me'
}]]]);
assert.equal(markerService.read().length, 1);
diag.dispose();
assert.equal(markerService.read().length, 0);
});
});

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { BoundModelReferenceCollection } from 'vs/workbench/api/electron-browser/mainThreadDocuments';
import { Model } from 'vs/editor/common/model/model';
import { TPromise } from 'vs/base/common/winjs.base';
suite('BoundModelReferenceCollection', () => {
let col = new BoundModelReferenceCollection(15, 75);
teardown(() => {
col.dispose();
});
test('max age', () => {
let didDispose = false;
col.add({
object: <any>{ textEditorModel: Model.createFromString('farboo') },
dispose() {
didDispose = true;
}
});
return TPromise.timeout(30).then(() => {
assert.equal(didDispose, true);
});
});
test('max size', () => {
let disposed: number[] = [];
col.add({
object: <any>{ textEditorModel: Model.createFromString('farboo') },
dispose() {
disposed.push(0);
}
});
col.add({
object: <any>{ textEditorModel: Model.createFromString('boofar') },
dispose() {
disposed.push(1);
}
});
col.add({
object: <any>{ textEditorModel: Model.createFromString(new Array(71).join('x')) },
dispose() {
disposed.push(2);
}
});
assert.deepEqual(disposed, [0, 1]);
});
});

View File

@@ -0,0 +1,167 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/electron-browser/mainThreadDocumentsAndEditors';
import { OneGetThreadService } from './testThreadService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
import { MockCodeEditorService } from 'vs/editor/test/common/mocks/mockCodeEditorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta } from 'vs/workbench/api/node/extHost.protocol';
import { mockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
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';
suite('MainThreadDocumentsAndEditors', () => {
let modelService: ModelServiceImpl;
let codeEditorService: MockCodeEditorService;
let textFileService: ITextFileService;
let workbenchEditorService: IWorkbenchEditorService;
let documentAndEditor: MainThreadDocumentsAndEditors;
let deltas: IDocumentsAndEditorsDelta[] = [];
const hugeModelString = new Array(2 + (50 * 1024 * 1024)).join('-');
setup(() => {
deltas.length = 0;
const configService = new TestConfigurationService();
configService.setUserConfiguration('editor', { 'detectIndentation': false });
modelService = new ModelServiceImpl(null, configService);
codeEditorService = new MockCodeEditorService();
textFileService = new class extends mock<ITextFileService>() {
isDirty() { return false; };
models = <any>{
onModelSaved: Event.None,
onModelReverted: Event.None,
onModelDirty: Event.None,
};
};
workbenchEditorService = <IWorkbenchEditorService>{
getVisibleEditors() { return []; },
getActiveEditor() { return undefined; }
};
const editorGroupService = new class extends mock<IEditorGroupService>() {
onEditorsChanged = Event.None;
onEditorsMoved = Event.None;
};
documentAndEditor = new MainThreadDocumentsAndEditors(
OneGetThreadService(new class extends mock<ExtHostDocumentsAndEditorsShape>() {
$acceptDocumentsAndEditorsDelta(delta) { deltas.push(delta); }
}),
modelService,
textFileService,
workbenchEditorService,
codeEditorService,
null,
null,
null,
null,
editorGroupService,
null
);
});
test('Model#add', () => {
deltas.length = 0;
modelService.createModel('farboo', null, null);
assert.equal(deltas.length, 1);
const [delta] = deltas;
assert.equal(delta.addedDocuments.length, 1);
assert.equal(delta.removedDocuments, undefined);
assert.equal(delta.addedEditors, undefined);
assert.equal(delta.removedEditors, undefined);
assert.equal(delta.newActiveEditor, null);
});
test('ignore huge model', function () {
this.timeout(1000 * 60); // increase timeout for this one test
const model = modelService.createModel(hugeModelString, null, null);
assert.ok(model.isTooLargeForHavingARichMode());
assert.equal(deltas.length, 1);
const [delta] = deltas;
assert.equal(delta.newActiveEditor, null);
assert.equal(delta.addedDocuments, undefined);
assert.equal(delta.removedDocuments, undefined);
assert.equal(delta.addedEditors, undefined);
assert.equal(delta.removedEditors, undefined);
});
test('ignore huge model from editor', function () {
this.timeout(1000 * 60); // increase timeout for this one test
const model = modelService.createModel(hugeModelString, null, null);
const editor = mockCodeEditor(null, { model, wordWrap: 'off', wordWrapMinified: false });
assert.equal(deltas.length, 1);
deltas.length = 0;
codeEditorService.addCodeEditor(editor);
assert.equal(deltas.length, 0);
});
test('ignore editor w/o model', () => {
const editor = mockCodeEditor([], {});
editor.setModel(null);
codeEditorService.addCodeEditor(editor);
assert.equal(deltas.length, 1);
const [delta] = deltas;
assert.equal(delta.newActiveEditor, null);
assert.equal(delta.addedDocuments, undefined);
assert.equal(delta.removedDocuments, undefined);
assert.equal(delta.addedEditors, undefined);
assert.equal(delta.removedEditors, undefined);
});
test('editor with model', () => {
deltas.length = 0;
const model = modelService.createModel('farboo', null, null);
codeEditorService.addCodeEditor(mockCodeEditor(null, { model }));
assert.equal(deltas.length, 2);
const [first, second] = deltas;
assert.equal(first.addedDocuments.length, 1);
assert.equal(first.newActiveEditor, null);
assert.equal(first.removedDocuments, undefined);
assert.equal(first.addedEditors, undefined);
assert.equal(first.removedEditors, undefined);
assert.equal(second.addedEditors.length, 1);
assert.equal(second.addedDocuments, undefined);
assert.equal(second.removedDocuments, undefined);
assert.equal(second.removedEditors, undefined);
assert.equal(typeof second.newActiveEditor, 'string');
});
test('editor with dispos-ed/-ing model', () => {
modelService.createModel('foobar', null, null);
const model = modelService.createModel('farboo', null, null);
const editor = mockCodeEditor(null, { model });
codeEditorService.addCodeEditor(editor);
// ignore things until now
deltas.length = 0;
modelService.destroyModel(model.uri);
assert.equal(deltas.length, 1);
const [first] = deltas;
assert.equal(first.newActiveEditor, null);
assert.equal(first.removedEditors.length, 1);
assert.equal(first.removedDocuments.length, 1);
assert.equal(first.addedDocuments, undefined);
assert.equal(first.addedEditors, undefined);
});
});

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* 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 * as assert from 'assert';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FinalNewLineParticipant } from 'vs/workbench/api/electron-browser/mainThreadSaveParticipant';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
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 { 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';
class ServiceAccessor {
constructor( @ITextFileService public textFileService: TestTextFileService, @IModelService public modelService: IModelService) {
}
}
suite('MainThreadSaveParticipant', function () {
let instantiationService: IInstantiationService;
let accessor: ServiceAccessor;
setup(() => {
instantiationService = workbenchInstantiationService();
accessor = instantiationService.createInstance(ServiceAccessor);
});
teardown(() => {
(<TextFileEditorModelManager>accessor.textFileService.models).clear();
TextFileEditorModel.setSaveParticipant(null); // reset any set participant
});
test('insert final new line', function (done) {
const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8');
model.load().then(() => {
const configService = new TestConfigurationService();
configService.setUserConfiguration('files', { 'insertFinalNewline': true });
const participant = new FinalNewLineParticipant(configService, undefined);
// No new line for empty lines
let lineContent = '';
model.textEditorModel.setValue(lineContent);
participant.participate(model, { reason: SaveReason.EXPLICIT });
assert.equal(model.getValue(), 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);
// 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()}`);
// 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()}`);
done();
});
});
});

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
export interface Ctor<T> {
new(): T;
}
export function mock<T>(): Ctor<T> {
return <any>function () { };
}

View File

@@ -0,0 +1,161 @@
/*---------------------------------------------------------------------------------------------
* 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() {
super(false);
}
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!');
}
}