mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 02:58:31 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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 */ });
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -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']
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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 }]);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
469
src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts
Normal file
469
src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts
Normal 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}');
|
||||
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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]);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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]);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
14
src/vs/workbench/test/electron-browser/api/mock.ts
Normal file
14
src/vs/workbench/test/electron-browser/api/mock.ts
Normal 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 () { };
|
||||
}
|
||||
161
src/vs/workbench/test/electron-browser/api/testThreadService.ts
Normal file
161
src/vs/workbench/test/electron-browser/api/testThreadService.ts
Normal 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!');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'vs/workbench/parts/search/browser/search.contribution'; // load contributions
|
||||
import * as assert from 'assert';
|
||||
import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ISearchService } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as minimist from 'minimist';
|
||||
import * as path from 'path';
|
||||
import { QuickOpenHandler, IQuickOpenRegistry, Extensions } from 'vs/workbench/browser/quickopen';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SearchService } from 'vs/workbench/services/search/node/searchService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { TestEnvironmentService, TestEditorService, TestEditorGroupService, TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { SimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
|
||||
namespace Timer {
|
||||
export interface ITimerEvent {
|
||||
id: number;
|
||||
topic: string;
|
||||
name: string;
|
||||
description: string;
|
||||
data: any;
|
||||
|
||||
startTime: Date;
|
||||
stopTime: Date;
|
||||
|
||||
stop(stopTime?: Date): void;
|
||||
timeTaken(): number;
|
||||
}
|
||||
}
|
||||
|
||||
declare var __dirname: string;
|
||||
|
||||
// Checkout sources to run against:
|
||||
// git clone --separate-git-dir=testGit --no-checkout --single-branch https://chromium.googlesource.com/chromium/src testWorkspace
|
||||
// cd testWorkspace; git checkout 39a7f93d67f7
|
||||
// Run from repository root folder with (test.bat on Windows): ./scripts/test.sh --grep QuickOpen.performance --timeout 180000 --testWorkspace <path>
|
||||
suite('QuickOpen performance (integration)', () => {
|
||||
|
||||
test('Measure', () => {
|
||||
if (process.env['VSCODE_PID']) {
|
||||
return; // TODO@Christoph find out why test fails when run from within VS Code
|
||||
}
|
||||
|
||||
const n = 3;
|
||||
const argv = minimist(process.argv);
|
||||
const testWorkspaceArg = argv['testWorkspace'];
|
||||
const verboseResults = argv['verboseResults'];
|
||||
const testWorkspacePath = testWorkspaceArg ? path.resolve(testWorkspaceArg) : __dirname;
|
||||
|
||||
const telemetryService = new TestTelemetryService();
|
||||
const configurationService = new SimpleConfigurationService();
|
||||
const instantiationService = new InstantiationService(new ServiceCollection(
|
||||
[ITelemetryService, telemetryService],
|
||||
[IConfigurationService, configurationService],
|
||||
[IModelService, new ModelServiceImpl(null, configurationService)],
|
||||
[IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))],
|
||||
[IWorkbenchEditorService, new TestEditorService()],
|
||||
[IEditorGroupService, new TestEditorGroupService()],
|
||||
[IEnvironmentService, TestEnvironmentService],
|
||||
[IUntitledEditorService, createSyncDescriptor(UntitledEditorService)],
|
||||
[ISearchService, createSyncDescriptor(SearchService)]
|
||||
));
|
||||
|
||||
const registry = Registry.as<IQuickOpenRegistry>(Extensions.Quickopen);
|
||||
const descriptor = registry.getDefaultQuickOpenHandler();
|
||||
assert.ok(descriptor);
|
||||
|
||||
function measure() {
|
||||
return instantiationService.createInstance(descriptor)
|
||||
.then((handler: QuickOpenHandler) => {
|
||||
handler.onOpen();
|
||||
return handler.getResults('a').then(result => {
|
||||
const uncachedEvent = popEvent();
|
||||
assert.strictEqual(uncachedEvent.data.symbols.fromCache, false, 'symbols.fromCache');
|
||||
assert.strictEqual(uncachedEvent.data.files.fromCache, true, 'files.fromCache');
|
||||
if (testWorkspaceArg) {
|
||||
assert.ok(!!uncachedEvent.data.files.joined, 'files.joined');
|
||||
}
|
||||
return uncachedEvent;
|
||||
}).then(uncachedEvent => {
|
||||
return handler.getResults('ab').then(result => {
|
||||
const cachedEvent = popEvent();
|
||||
assert.strictEqual(uncachedEvent.data.symbols.fromCache, false, 'symbols.fromCache');
|
||||
assert.ok(cachedEvent.data.files.fromCache, 'filesFromCache');
|
||||
handler.onClose(false);
|
||||
return [uncachedEvent, cachedEvent];
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function popEvent() {
|
||||
const events = telemetryService.events;
|
||||
assert.strictEqual(events.length, 1);
|
||||
const event = events[0];
|
||||
events.length = 0;
|
||||
assert.strictEqual(event.name, 'openAnything');
|
||||
return event;
|
||||
}
|
||||
|
||||
function printResult(data: any) {
|
||||
if (verboseResults) {
|
||||
console.log(JSON.stringify(data, null, ' ') + ',');
|
||||
} else {
|
||||
console.log(JSON.stringify({
|
||||
filesfromCacheNotJoined: data.files.fromCache && !data.files.joined,
|
||||
searchLength: data.searchLength,
|
||||
sortedResultDuration: data.sortedResultDuration,
|
||||
filesResultCount: data.files.resultCount,
|
||||
errorCount: data.files.errors && data.files.errors.length || undefined
|
||||
}) + ',');
|
||||
}
|
||||
}
|
||||
|
||||
return measure() // Warm-up first
|
||||
.then(() => {
|
||||
if (testWorkspaceArg || verboseResults) { // Don't measure by default
|
||||
const cachedEvents: Timer.ITimerEvent[] = [];
|
||||
let i = n;
|
||||
return (function iterate(): TPromise<Timer.ITimerEvent> {
|
||||
if (!i--) {
|
||||
return undefined;
|
||||
}
|
||||
return measure()
|
||||
.then(([uncachedEvent, cachedEvent]) => {
|
||||
printResult(uncachedEvent.data);
|
||||
cachedEvents.push(cachedEvent);
|
||||
return iterate();
|
||||
});
|
||||
})().then(() => {
|
||||
console.log();
|
||||
cachedEvents.forEach(cachedEvent => {
|
||||
printResult(cachedEvent.data);
|
||||
});
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestTelemetryService implements ITelemetryService {
|
||||
|
||||
public _serviceBrand: any;
|
||||
public isOptedIn = true;
|
||||
|
||||
public events: any[] = [];
|
||||
|
||||
public publicLog(eventName: string, data?: any): TPromise<void> {
|
||||
this.events.push({ name: eventName, data: data });
|
||||
return TPromise.as<void>(null);
|
||||
}
|
||||
|
||||
public getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return TPromise.as({
|
||||
instanceId: 'someValue.instanceId',
|
||||
sessionId: 'someValue.sessionId',
|
||||
machineId: 'someValue.machineId'
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 'vs/workbench/parts/search/browser/search.contribution'; // load contributions
|
||||
import * as assert from 'assert';
|
||||
import * as fs from 'fs';
|
||||
import { IWorkspaceContextService, LegacyWorkspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
|
||||
import { ISearchService, IQueryOptions } from 'vs/platform/search/common/search';
|
||||
import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IUntitledEditorService, UntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as minimist from 'minimist';
|
||||
import * as path from 'path';
|
||||
import { SearchService } from 'vs/workbench/services/search/node/searchService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { TestEnvironmentService, TestEditorService, TestEditorGroupService, TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { SimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
|
||||
import { SearchModel } from 'vs/workbench/parts/search/common/searchModel';
|
||||
import { QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder';
|
||||
|
||||
import Event, * as event from 'vs/base/common/event';
|
||||
|
||||
declare var __dirname: string;
|
||||
|
||||
// Checkout sources to run against:
|
||||
// git clone --separate-git-dir=testGit --no-checkout --single-branch https://chromium.googlesource.com/chromium/src testWorkspace
|
||||
// cd testWorkspace; git checkout 39a7f93d67f7
|
||||
// Run from repository root folder with (test.bat on Windows): ./scripts/test-int-mocha.sh --grep TextSearch.performance --timeout 500000 --testWorkspace <path>
|
||||
suite('TextSearch performance (integration)', () => {
|
||||
|
||||
test('Measure', () => {
|
||||
if (process.env['VSCODE_PID']) {
|
||||
return undefined; // TODO@Rob find out why test fails when run from within VS Code
|
||||
}
|
||||
|
||||
const n = 3;
|
||||
const argv = minimist(process.argv);
|
||||
const testWorkspaceArg = argv['testWorkspace'];
|
||||
const testWorkspacePath = testWorkspaceArg ? path.resolve(testWorkspaceArg) : __dirname;
|
||||
if (!fs.existsSync(testWorkspacePath)) {
|
||||
throw new Error(`--testWorkspace doesn't exist`);
|
||||
}
|
||||
|
||||
const telemetryService = new TestTelemetryService();
|
||||
const configurationService = new SimpleConfigurationService();
|
||||
const instantiationService = new InstantiationService(new ServiceCollection(
|
||||
[ITelemetryService, telemetryService],
|
||||
[IConfigurationService, configurationService],
|
||||
[IModelService, new ModelServiceImpl(null, configurationService)],
|
||||
[IWorkspaceContextService, new TestContextService(new LegacyWorkspace(URI.file(testWorkspacePath)))],
|
||||
[IWorkbenchEditorService, new TestEditorService()],
|
||||
[IEditorGroupService, new TestEditorGroupService()],
|
||||
[IEnvironmentService, TestEnvironmentService],
|
||||
[IUntitledEditorService, createSyncDescriptor(UntitledEditorService)],
|
||||
[ISearchService, createSyncDescriptor(SearchService)]
|
||||
));
|
||||
|
||||
const queryOptions: IQueryOptions = {
|
||||
maxResults: 2048
|
||||
};
|
||||
|
||||
const searchModel: SearchModel = instantiationService.createInstance(SearchModel);
|
||||
function runSearch(): TPromise<any> {
|
||||
const queryBuilder: QueryBuilder = instantiationService.createInstance(QueryBuilder);
|
||||
const query = queryBuilder.text({ pattern: 'static_library(' }, [URI.file(testWorkspacePath)], queryOptions);
|
||||
|
||||
// Wait for the 'searchResultsFinished' event, which is fired after the search() promise is resolved
|
||||
const onSearchResultsFinished = event.filterEvent(telemetryService.eventLogged, e => e.name === 'searchResultsFinished');
|
||||
event.once(onSearchResultsFinished)(onComplete);
|
||||
|
||||
function onComplete(): void {
|
||||
try {
|
||||
const allEvents = telemetryService.events.map(e => JSON.stringify(e)).join('\n');
|
||||
assert.equal(telemetryService.events.length, 3, 'Expected 3 telemetry events, got:\n' + allEvents);
|
||||
|
||||
const [firstRenderEvent, resultsShownEvent, resultsFinishedEvent] = telemetryService.events;
|
||||
assert.equal(firstRenderEvent.name, 'searchResultsFirstRender');
|
||||
assert.equal(resultsShownEvent.name, 'searchResultsShown');
|
||||
assert.equal(resultsFinishedEvent.name, 'searchResultsFinished');
|
||||
|
||||
telemetryService.events = [];
|
||||
|
||||
resolve(resultsFinishedEvent);
|
||||
} catch (e) {
|
||||
// Fail the runSearch() promise
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
|
||||
let resolve;
|
||||
let error;
|
||||
return new TPromise((_resolve, _error) => {
|
||||
resolve = _resolve;
|
||||
error = _error;
|
||||
|
||||
// Don't wait on this promise, we're waiting on the event fired above
|
||||
searchModel.search(query).then(
|
||||
null,
|
||||
_error);
|
||||
});
|
||||
}
|
||||
|
||||
const finishedEvents = [];
|
||||
return runSearch() // Warm-up first
|
||||
.then(() => {
|
||||
if (testWorkspaceArg) { // Don't measure by default
|
||||
let i = n;
|
||||
return (function iterate() {
|
||||
if (!i--) {
|
||||
return;
|
||||
}
|
||||
|
||||
return runSearch()
|
||||
.then((resultsFinishedEvent: any) => {
|
||||
console.log(`Iteration ${n - i}: ${resultsFinishedEvent.data.duration / 1000}s`);
|
||||
finishedEvents.push(resultsFinishedEvent);
|
||||
return iterate();
|
||||
});
|
||||
})().then(() => {
|
||||
const totalTime = finishedEvents.reduce((sum, e) => sum + e.data.duration, 0);
|
||||
console.log(`Avg duration: ${totalTime / n / 1000}s`);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestTelemetryService implements ITelemetryService {
|
||||
public _serviceBrand: any;
|
||||
public isOptedIn = true;
|
||||
|
||||
public events: any[] = [];
|
||||
|
||||
private emitter = new event.Emitter<any>();
|
||||
|
||||
public get eventLogged(): Event<any> {
|
||||
return this.emitter.event;
|
||||
}
|
||||
|
||||
public publicLog(eventName: string, data?: any): TPromise<void> {
|
||||
const event = { name: eventName, data: data };
|
||||
this.events.push(event);
|
||||
this.emitter.fire(event);
|
||||
return TPromise.as<void>(null);
|
||||
}
|
||||
|
||||
public getTelemetryInfo(): TPromise<ITelemetryInfo> {
|
||||
return TPromise.as({
|
||||
instanceId: 'someValue.instanceId',
|
||||
sessionId: 'someValue.sessionId',
|
||||
machineId: 'someValue.machineId'
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user