mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-10 10:12:34 -05:00
Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5
This commit is contained in:
21
src/vs/workbench/test/browser/api/extHost.api.impl.test.ts
Normal file
21
src/vs/workbench/test/browser/api/extHost.api.impl.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
|
||||
suite('ExtHost API', function () {
|
||||
test('issue #51387: originalFSPath', function () {
|
||||
if (isWindows) {
|
||||
assert.equal(originalFSPath(URI.file('C:\\test')).charAt(0), 'C');
|
||||
assert.equal(originalFSPath(URI.file('c:\\test')).charAt(0), 'c');
|
||||
|
||||
assert.equal(originalFSPath(URI.revive(JSON.parse(JSON.stringify(URI.file('C:\\test'))))).charAt(0), 'C');
|
||||
assert.equal(originalFSPath(URI.revive(JSON.parse(JSON.stringify(URI.file('c:\\test'))))).charAt(0), 'c');
|
||||
}
|
||||
});
|
||||
});
|
||||
934
src/vs/workbench/test/browser/api/extHostApiCommands.test.ts
Normal file
934
src/vs/workbench/test/browser/api/extHostApiCommands.test.ts
Normal file
@@ -0,0 +1,934 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { setUnexpectedErrorHandler, errorHandler } from 'vs/base/common/errors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { TextModel as EditorModel } from 'vs/editor/common/model/textModel';
|
||||
import { TestRPCProtocol } from './testRPCProtocol';
|
||||
import { MarkerService } from 'vs/platform/markers/common/markerService';
|
||||
import { IMarkerService } from 'vs/platform/markers/common/markers';
|
||||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
import { MainThreadLanguageFeatures } from 'vs/workbench/api/browser/mainThreadLanguageFeatures';
|
||||
import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { MainContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
|
||||
import type * as vscode from 'vscode';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import 'vs/workbench/contrib/search/browser/search.contribution';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { nullExtensionDescription, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
|
||||
import 'vs/editor/contrib/codeAction/codeAction';
|
||||
import 'vs/editor/contrib/codelens/codelens';
|
||||
import 'vs/editor/contrib/colorPicker/color';
|
||||
import 'vs/editor/contrib/format/format';
|
||||
import 'vs/editor/contrib/gotoSymbol/goToCommands';
|
||||
import 'vs/editor/contrib/hover/getHover';
|
||||
import 'vs/editor/contrib/links/getLinks';
|
||||
import 'vs/editor/contrib/parameterHints/provideSignatureHelp';
|
||||
import 'vs/editor/contrib/quickOpen/quickOpen';
|
||||
import 'vs/editor/contrib/smartSelect/smartSelect';
|
||||
import 'vs/editor/contrib/suggest/suggest';
|
||||
|
||||
const defaultSelector = { scheme: 'far' };
|
||||
const model: ITextModel = 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 rpcProtocol: TestRPCProtocol;
|
||||
let extHost: ExtHostLanguageFeatures;
|
||||
let mainThread: MainThreadLanguageFeatures;
|
||||
let commands: ExtHostCommands;
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
let originalErrorHandler: (e: any) => any;
|
||||
|
||||
function assertRejects(fn: () => Promise<any>, message: string = 'Expected rejection') {
|
||||
return fn().then(() => assert.ok(false, message), _err => assert.ok(true));
|
||||
}
|
||||
|
||||
suite('ExtHostLanguageFeatureCommands', function () {
|
||||
|
||||
suiteSetup(() => {
|
||||
|
||||
originalErrorHandler = errorHandler.getUnexpectedErrorHandler();
|
||||
setUnexpectedErrorHandler(() => { });
|
||||
|
||||
// Use IInstantiationService to get typechecking when instantiating
|
||||
let insta: IInstantiationService;
|
||||
rpcProtocol = new TestRPCProtocol();
|
||||
const services = new ServiceCollection();
|
||||
services.set(IExtensionService, new class extends mock<IExtensionService>() {
|
||||
async activateByEvent() {
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
services.set(ICommandService, new SyncDescriptor(class extends mock<ICommandService>() {
|
||||
|
||||
executeCommand(id: string, ...args: any): any {
|
||||
const command = CommandsRegistry.getCommands().get(id);
|
||||
if (!command) {
|
||||
return Promise.reject(new Error(id + ' NOT known'));
|
||||
}
|
||||
const { handler } = command;
|
||||
return Promise.resolve(insta.invokeFunction(handler, ...args));
|
||||
}
|
||||
}));
|
||||
services.set(IMarkerService, new MarkerService());
|
||||
services.set(IModelService, new class extends mock<IModelService>() {
|
||||
getModel() { return model; }
|
||||
});
|
||||
services.set(IEditorWorkerService, new class extends mock<IEditorWorkerService>() {
|
||||
async computeMoreMinimalEdits(_uri: any, edits: any) {
|
||||
return edits || undefined;
|
||||
}
|
||||
});
|
||||
|
||||
insta = new InstantiationService(services);
|
||||
|
||||
const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService());
|
||||
extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({
|
||||
addedDocuments: [{
|
||||
isDirty: false,
|
||||
versionId: model.getVersionId(),
|
||||
modeId: model.getLanguageIdentifier().language,
|
||||
uri: model.uri,
|
||||
lines: model.getValue().split(model.getEOL()),
|
||||
EOL: model.getEOL(),
|
||||
}]
|
||||
});
|
||||
const extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostDocuments, extHostDocuments);
|
||||
|
||||
commands = new ExtHostCommands(rpcProtocol, new NullLogService());
|
||||
rpcProtocol.set(ExtHostContext.ExtHostCommands, commands);
|
||||
rpcProtocol.set(MainContext.MainThreadCommands, insta.createInstance(MainThreadCommands, rpcProtocol));
|
||||
ExtHostApiCommands.register(commands);
|
||||
|
||||
const diagnostics = new ExtHostDiagnostics(rpcProtocol, new NullLogService());
|
||||
rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, diagnostics);
|
||||
|
||||
extHost = new ExtHostLanguageFeatures(rpcProtocol, null, extHostDocuments, commands, diagnostics, new NullLogService(), NullApiDeprecationService);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, extHost);
|
||||
|
||||
mainThread = rpcProtocol.set(MainContext.MainThreadLanguageFeatures, insta.createInstance(MainThreadLanguageFeatures, rpcProtocol));
|
||||
|
||||
return rpcProtocol.sync();
|
||||
});
|
||||
|
||||
suiteTeardown(() => {
|
||||
setUnexpectedErrorHandler(originalErrorHandler);
|
||||
model.dispose();
|
||||
mainThread.dispose();
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
disposables = dispose(disposables);
|
||||
return rpcProtocol.sync();
|
||||
});
|
||||
|
||||
// --- workspace symbols
|
||||
|
||||
test('WorkspaceSymbols, invalid arguments', function () {
|
||||
let promises = [
|
||||
assertRejects(() => commands.executeCommand('vscode.executeWorkspaceSymbolProvider')),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeWorkspaceSymbolProvider', null)),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeWorkspaceSymbolProvider', undefined)),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeWorkspaceSymbolProvider', true))
|
||||
];
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
test('WorkspaceSymbols, back and forth', function () {
|
||||
|
||||
disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, <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(nullExtensionDescription, <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'))
|
||||
];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('executeWorkspaceSymbolProvider should accept empty string, #39522', async function () {
|
||||
|
||||
disposables.push(extHost.registerWorkspaceSymbolProvider(nullExtensionDescription, {
|
||||
provideWorkspaceSymbols(): vscode.SymbolInformation[] {
|
||||
return [new types.SymbolInformation('hello', types.SymbolKind.Array, new types.Range(0, 0, 0, 0), URI.parse('foo:bar')) as vscode.SymbolInformation];
|
||||
}
|
||||
}));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
let symbols = await commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeWorkspaceSymbolProvider', '');
|
||||
assert.equal(symbols.length, 1);
|
||||
|
||||
await rpcProtocol.sync();
|
||||
symbols = await commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeWorkspaceSymbolProvider', '*');
|
||||
assert.equal(symbols.length, 1);
|
||||
});
|
||||
|
||||
// --- formatting
|
||||
test('executeFormatDocumentProvider, back and forth', async function () {
|
||||
|
||||
disposables.push(extHost.registerDocumentFormattingEditProvider(nullExtensionDescription, defaultSelector, new class implements vscode.DocumentFormattingEditProvider {
|
||||
provideDocumentFormattingEdits() {
|
||||
return [types.TextEdit.insert(new types.Position(0, 0), '42')];
|
||||
}
|
||||
}));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
let edits = await commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeFormatDocumentProvider', model.uri);
|
||||
assert.equal(edits.length, 1);
|
||||
});
|
||||
|
||||
|
||||
// --- definition
|
||||
|
||||
test('Definition, invalid arguments', function () {
|
||||
let promises = [
|
||||
assertRejects(() => commands.executeCommand('vscode.executeDefinitionProvider')),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeDefinitionProvider', null)),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeDefinitionProvider', undefined)),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeDefinitionProvider', true, false))
|
||||
];
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
test('Definition, back and forth', function () {
|
||||
|
||||
disposables.push(extHost.registerDefinitionProvider(nullExtensionDescription, defaultSelector, <vscode.DefinitionProvider>{
|
||||
provideDefinition(doc: any): any {
|
||||
return new types.Location(doc.uri, new types.Range(0, 0, 0, 0));
|
||||
}
|
||||
}));
|
||||
disposables.push(extHost.registerDefinitionProvider(nullExtensionDescription, 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 rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.Location[]>('vscode.executeDefinitionProvider', model.uri, new types.Position(0, 0)).then(values => {
|
||||
assert.equal(values.length, 4);
|
||||
for (let v of values) {
|
||||
assert.ok(v.uri instanceof URI);
|
||||
assert.ok(v.range instanceof types.Range);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --- declaration
|
||||
|
||||
test('Declaration, back and forth', function () {
|
||||
|
||||
disposables.push(extHost.registerDeclarationProvider(nullExtensionDescription, defaultSelector, <vscode.DeclarationProvider>{
|
||||
provideDeclaration(doc: any): any {
|
||||
return new types.Location(doc.uri, new types.Range(0, 0, 0, 0));
|
||||
}
|
||||
}));
|
||||
disposables.push(extHost.registerDeclarationProvider(nullExtensionDescription, defaultSelector, <vscode.DeclarationProvider>{
|
||||
provideDeclaration(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 rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.Location[]>('vscode.executeDeclarationProvider', 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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --- type definition
|
||||
|
||||
test('Type Definition, invalid arguments', function () {
|
||||
const promises = [
|
||||
assertRejects(() => commands.executeCommand('vscode.executeTypeDefinitionProvider')),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeTypeDefinitionProvider', null)),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeTypeDefinitionProvider', undefined)),
|
||||
assertRejects(() => commands.executeCommand('vscode.executeTypeDefinitionProvider', true, false))
|
||||
];
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
|
||||
test('Type Definition, back and forth', function () {
|
||||
|
||||
disposables.push(extHost.registerTypeDefinitionProvider(nullExtensionDescription, defaultSelector, <vscode.TypeDefinitionProvider>{
|
||||
provideTypeDefinition(doc: any): any {
|
||||
return new types.Location(doc.uri, new types.Range(0, 0, 0, 0));
|
||||
}
|
||||
}));
|
||||
disposables.push(extHost.registerTypeDefinitionProvider(nullExtensionDescription, defaultSelector, <vscode.TypeDefinitionProvider>{
|
||||
provideTypeDefinition(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 rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.Location[]>('vscode.executeTypeDefinitionProvider', model.uri, new types.Position(0, 0)).then(values => {
|
||||
assert.equal(values.length, 4);
|
||||
for (const 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(nullExtensionDescription, defaultSelector, <vscode.ReferenceProvider>{
|
||||
provideReferences() {
|
||||
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 () {
|
||||
disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, 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)),
|
||||
];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.SymbolInformation[]>('vscode.executeDocumentSymbolProvider', model.uri).then(values => {
|
||||
assert.equal(values.length, 2);
|
||||
let [first, second] = values;
|
||||
assert.ok(first instanceof types.SymbolInformation);
|
||||
assert.ok(second instanceof types.SymbolInformation);
|
||||
assert.equal(first.name, 'testing2');
|
||||
assert.equal(second.name, 'testing1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('vscode.executeDocumentSymbolProvider command only returns SymbolInformation[] rather than DocumentSymbol[] #57984', function () {
|
||||
disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, <vscode.DocumentSymbolProvider>{
|
||||
provideDocumentSymbols(): any {
|
||||
return [
|
||||
new types.SymbolInformation('SymbolInformation', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0))
|
||||
];
|
||||
}
|
||||
}));
|
||||
disposables.push(extHost.registerDocumentSymbolProvider(nullExtensionDescription, defaultSelector, <vscode.DocumentSymbolProvider>{
|
||||
provideDocumentSymbols(): any {
|
||||
let root = new types.DocumentSymbol('DocumentSymbol', 'DocumentSymbol#detail', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0), new types.Range(1, 0, 1, 0));
|
||||
root.children = [new types.DocumentSymbol('DocumentSymbol#child', 'DocumentSymbol#detail#child', types.SymbolKind.Enum, new types.Range(1, 0, 1, 0), new types.Range(1, 0, 1, 0))];
|
||||
return [root];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<(vscode.SymbolInformation & vscode.DocumentSymbol)[]>('vscode.executeDocumentSymbolProvider', model.uri).then(values => {
|
||||
assert.equal(values.length, 2);
|
||||
let [first, second] = values;
|
||||
assert.ok(first instanceof types.SymbolInformation);
|
||||
assert.ok(!(first instanceof types.DocumentSymbol));
|
||||
assert.ok(second instanceof types.SymbolInformation);
|
||||
assert.equal(first.name, 'DocumentSymbol');
|
||||
assert.equal(first.children.length, 1);
|
||||
assert.equal(second.name, 'SymbolInformation');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --- suggest
|
||||
|
||||
test('Suggest, back and forth', function () {
|
||||
disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, <vscode.CompletionItemProvider>{
|
||||
provideCompletionItems(): 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 rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => {
|
||||
|
||||
assert.ok(list instanceof types.CompletionList);
|
||||
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, undefined);// no text edit, default ranges
|
||||
assert.ok(!types.Range.isRange(first.range));
|
||||
|
||||
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);
|
||||
|
||||
const range: any = fourth.range!;
|
||||
assert.ok(types.Range.isRange(range));
|
||||
assert.equal(range.start.line, 0);
|
||||
assert.equal(range.start.character, 1);
|
||||
assert.equal(range.end.line, 0);
|
||||
assert.equal(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 () {
|
||||
disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, 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);
|
||||
}
|
||||
}, []));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.CompletionList>('vscode.executeCompletionItemProvider', model.uri, new types.Position(0, 4)).then(list => {
|
||||
assert.ok(list instanceof types.CompletionList);
|
||||
assert.equal(list.isIncomplete, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Suggest, resolve completion items', async function () {
|
||||
|
||||
let resolveCount = 0;
|
||||
|
||||
disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, <vscode.CompletionItemProvider>{
|
||||
provideCompletionItems(): any {
|
||||
let a = new types.CompletionItem('item1');
|
||||
let b = new types.CompletionItem('item2');
|
||||
let c = new types.CompletionItem('item3');
|
||||
let d = new types.CompletionItem('item4');
|
||||
return new types.CompletionList([a, b, c, d], false);
|
||||
},
|
||||
resolveCompletionItem(item) {
|
||||
resolveCount += 1;
|
||||
return item;
|
||||
}
|
||||
}, []));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
|
||||
let list = await commands.executeCommand<vscode.CompletionList>(
|
||||
'vscode.executeCompletionItemProvider',
|
||||
model.uri,
|
||||
new types.Position(0, 4),
|
||||
undefined,
|
||||
2 // maxItemsToResolve
|
||||
);
|
||||
|
||||
assert.ok(list instanceof types.CompletionList);
|
||||
assert.equal(resolveCount, 2);
|
||||
|
||||
});
|
||||
|
||||
test('"vscode.executeCompletionItemProvider" doesnot return a preselect field #53749', async function () {
|
||||
disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, <vscode.CompletionItemProvider>{
|
||||
provideCompletionItems(): any {
|
||||
let a = new types.CompletionItem('item1');
|
||||
a.preselect = true;
|
||||
let b = new types.CompletionItem('item2');
|
||||
let c = new types.CompletionItem('item3');
|
||||
c.preselect = true;
|
||||
let d = new types.CompletionItem('item4');
|
||||
return new types.CompletionList([a, b, c, d], false);
|
||||
}
|
||||
}, []));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
|
||||
let list = await commands.executeCommand<vscode.CompletionList>(
|
||||
'vscode.executeCompletionItemProvider',
|
||||
model.uri,
|
||||
new types.Position(0, 4),
|
||||
undefined
|
||||
);
|
||||
|
||||
assert.ok(list instanceof types.CompletionList);
|
||||
assert.equal(list.items.length, 4);
|
||||
|
||||
let [a, b, c, d] = list.items;
|
||||
assert.equal(a.preselect, true);
|
||||
assert.equal(b.preselect, undefined);
|
||||
assert.equal(c.preselect, true);
|
||||
assert.equal(d.preselect, undefined);
|
||||
});
|
||||
|
||||
test('executeCompletionItemProvider doesn\'t capture commitCharacters #58228', async function () {
|
||||
disposables.push(extHost.registerCompletionItemProvider(nullExtensionDescription, defaultSelector, <vscode.CompletionItemProvider>{
|
||||
provideCompletionItems(): any {
|
||||
let a = new types.CompletionItem('item1');
|
||||
a.commitCharacters = ['a', 'b'];
|
||||
let b = new types.CompletionItem('item2');
|
||||
return new types.CompletionList([a, b], false);
|
||||
}
|
||||
}, []));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
|
||||
let list = await commands.executeCommand<vscode.CompletionList>(
|
||||
'vscode.executeCompletionItemProvider',
|
||||
model.uri,
|
||||
new types.Position(0, 4),
|
||||
undefined
|
||||
);
|
||||
|
||||
assert.ok(list instanceof types.CompletionList);
|
||||
assert.equal(list.items.length, 2);
|
||||
|
||||
let [a, b] = list.items;
|
||||
assert.deepEqual(a.commitCharacters, ['a', 'b']);
|
||||
assert.equal(b.commitCharacters, undefined);
|
||||
});
|
||||
|
||||
// --- signatureHelp
|
||||
|
||||
test('Parameter Hints, back and forth', async () => {
|
||||
disposables.push(extHost.registerSignatureHelpProvider(nullExtensionDescription, defaultSelector, new class implements vscode.SignatureHelpProvider {
|
||||
provideSignatureHelp(_document: vscode.TextDocument, _position: vscode.Position, _token: vscode.CancellationToken, context: vscode.SignatureHelpContext): vscode.SignatureHelp {
|
||||
return {
|
||||
activeSignature: 0,
|
||||
activeParameter: 1,
|
||||
signatures: [
|
||||
{
|
||||
label: 'abc',
|
||||
documentation: `${context.triggerKind === 1 /* vscode.SignatureHelpTriggerKind.Invoke */ ? 'invoked' : 'unknown'} ${context.triggerCharacter}`,
|
||||
parameters: []
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}, []));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
|
||||
const firstValue = await commands.executeCommand<vscode.SignatureHelp>('vscode.executeSignatureHelpProvider', model.uri, new types.Position(0, 1), ',');
|
||||
assert.strictEqual(firstValue.activeSignature, 0);
|
||||
assert.strictEqual(firstValue.activeParameter, 1);
|
||||
assert.strictEqual(firstValue.signatures.length, 1);
|
||||
assert.strictEqual(firstValue.signatures[0].label, 'abc');
|
||||
assert.strictEqual(firstValue.signatures[0].documentation, 'invoked ,');
|
||||
});
|
||||
|
||||
// --- quickfix
|
||||
|
||||
test('QuickFix, back and forth', function () {
|
||||
disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, {
|
||||
provideCodeActions(): vscode.Command[] {
|
||||
return [{ command: 'testing', title: 'Title', arguments: [1, 2, true] }];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.Command[]>('vscode.executeCodeActionProvider', model.uri, new types.Range(0, 0, 1, 1)).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
let [first] = value;
|
||||
assert.equal(first.title, 'Title');
|
||||
assert.equal(first.command, 'testing');
|
||||
assert.deepEqual(first.arguments, [1, 2, true]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('vscode.executeCodeActionProvider results seem to be missing their `command` property #45124', function () {
|
||||
disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, {
|
||||
provideCodeActions(document, range): vscode.CodeAction[] {
|
||||
return [{
|
||||
command: {
|
||||
arguments: [document, range],
|
||||
command: 'command',
|
||||
title: 'command_title',
|
||||
},
|
||||
kind: types.CodeActionKind.Empty.append('foo'),
|
||||
title: 'title',
|
||||
}];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', model.uri, new types.Range(0, 0, 1, 1)).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
const [first] = value;
|
||||
assert.ok(first.command);
|
||||
assert.equal(first.command!.command, 'command');
|
||||
assert.equal(first.command!.title, 'command_title');
|
||||
assert.equal(first.kind!.value, 'foo');
|
||||
assert.equal(first.title, 'title');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('vscode.executeCodeActionProvider passes Range to provider although Selection is passed in #77997', function () {
|
||||
disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, {
|
||||
provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] {
|
||||
return [{
|
||||
command: {
|
||||
arguments: [document, rangeOrSelection],
|
||||
command: 'command',
|
||||
title: 'command_title',
|
||||
},
|
||||
kind: types.CodeActionKind.Empty.append('foo'),
|
||||
title: 'title',
|
||||
}];
|
||||
}
|
||||
}));
|
||||
|
||||
const selection = new types.Selection(0, 0, 1, 1);
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', model.uri, selection).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
const [first] = value;
|
||||
assert.ok(first.command);
|
||||
assert.ok(first.command!.arguments![1] instanceof types.Selection);
|
||||
assert.ok(first.command!.arguments![1].isEqual(selection));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('vscode.executeCodeActionProvider results seem to be missing their `isPreferred` property #78098', function () {
|
||||
disposables.push(extHost.registerCodeActionProvider(nullExtensionDescription, defaultSelector, {
|
||||
provideCodeActions(document, rangeOrSelection): vscode.CodeAction[] {
|
||||
return [{
|
||||
command: {
|
||||
arguments: [document, rangeOrSelection],
|
||||
command: 'command',
|
||||
title: 'command_title',
|
||||
},
|
||||
kind: types.CodeActionKind.Empty.append('foo'),
|
||||
title: 'title',
|
||||
isPreferred: true
|
||||
}];
|
||||
}
|
||||
}));
|
||||
|
||||
const selection = new types.Selection(0, 0, 1, 1);
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.CodeAction[]>('vscode.executeCodeActionProvider', model.uri, selection).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
const [first] = value;
|
||||
assert.equal(first.isPreferred, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --- code lens
|
||||
|
||||
test('CodeLens, back and forth', function () {
|
||||
|
||||
const complexArg = {
|
||||
foo() { },
|
||||
bar() { },
|
||||
big: extHost
|
||||
};
|
||||
|
||||
disposables.push(extHost.registerCodeLensProvider(nullExtensionDescription, 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 rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.CodeLens[]>('vscode.executeCodeLensProvider', model.uri).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
const [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('CodeLens, resolve', async function () {
|
||||
|
||||
let resolveCount = 0;
|
||||
|
||||
disposables.push(extHost.registerCodeLensProvider(nullExtensionDescription, defaultSelector, <vscode.CodeLensProvider>{
|
||||
provideCodeLenses(): any {
|
||||
return [
|
||||
new types.CodeLens(new types.Range(0, 0, 1, 1)),
|
||||
new types.CodeLens(new types.Range(0, 0, 1, 1)),
|
||||
new types.CodeLens(new types.Range(0, 0, 1, 1)),
|
||||
new types.CodeLens(new types.Range(0, 0, 1, 1), { title: 'Already resolved', command: 'fff' })
|
||||
];
|
||||
},
|
||||
resolveCodeLens(codeLens: types.CodeLens) {
|
||||
codeLens.command = { title: resolveCount.toString(), command: 'resolved' };
|
||||
resolveCount += 1;
|
||||
return codeLens;
|
||||
}
|
||||
}));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
|
||||
let value = await commands.executeCommand<vscode.CodeLens[]>('vscode.executeCodeLensProvider', model.uri, 2);
|
||||
|
||||
assert.equal(value.length, 3); // the resolve argument defines the number of results being returned
|
||||
assert.equal(resolveCount, 2);
|
||||
|
||||
resolveCount = 0;
|
||||
value = await commands.executeCommand<vscode.CodeLens[]>('vscode.executeCodeLensProvider', model.uri);
|
||||
|
||||
assert.equal(value.length, 4);
|
||||
assert.equal(resolveCount, 0);
|
||||
});
|
||||
|
||||
test('Links, back and forth', function () {
|
||||
|
||||
disposables.push(extHost.registerDocumentLinkProvider(nullExtensionDescription, defaultSelector, <vscode.DocumentLinkProvider>{
|
||||
provideDocumentLinks(): any {
|
||||
return [new types.DocumentLink(new types.Range(0, 0, 0, 20), URI.parse('foo:bar'))];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.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 + '', '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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('Color provider', function () {
|
||||
|
||||
disposables.push(extHost.registerColorProvider(nullExtensionDescription, defaultSelector, <vscode.DocumentColorProvider>{
|
||||
provideDocumentColors(): vscode.ColorInformation[] {
|
||||
return [new types.ColorInformation(new types.Range(0, 0, 0, 20), new types.Color(0.1, 0.2, 0.3, 0.4))];
|
||||
},
|
||||
provideColorPresentations(): vscode.ColorPresentation[] {
|
||||
const cp = new types.ColorPresentation('#ABC');
|
||||
cp.textEdit = types.TextEdit.replace(new types.Range(1, 0, 1, 20), '#ABC');
|
||||
cp.additionalTextEdits = [types.TextEdit.insert(new types.Position(2, 20), '*')];
|
||||
return [cp];
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.ColorInformation[]>('vscode.executeDocumentColorProvider', model.uri).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
let [first] = value;
|
||||
|
||||
assert.equal(first.color.red, 0.1);
|
||||
assert.equal(first.color.green, 0.2);
|
||||
assert.equal(first.color.blue, 0.3);
|
||||
assert.equal(first.color.alpha, 0.4);
|
||||
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);
|
||||
});
|
||||
}).then(() => {
|
||||
const color = new types.Color(0.5, 0.6, 0.7, 0.8);
|
||||
const range = new types.Range(0, 0, 0, 20);
|
||||
return commands.executeCommand<vscode.ColorPresentation[]>('vscode.executeColorPresentationProvider', color, { uri: model.uri, range }).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
let [first] = value;
|
||||
|
||||
assert.equal(first.label, '#ABC');
|
||||
assert.equal(first.textEdit!.newText, '#ABC');
|
||||
assert.equal(first.textEdit!.range.start.line, 1);
|
||||
assert.equal(first.textEdit!.range.start.character, 0);
|
||||
assert.equal(first.textEdit!.range.end.line, 1);
|
||||
assert.equal(first.textEdit!.range.end.character, 20);
|
||||
assert.equal(first.additionalTextEdits!.length, 1);
|
||||
assert.equal(first.additionalTextEdits![0].range.start.line, 2);
|
||||
assert.equal(first.additionalTextEdits![0].range.start.character, 20);
|
||||
assert.equal(first.additionalTextEdits![0].range.end.line, 2);
|
||||
assert.equal(first.additionalTextEdits![0].range.end.character, 20);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('"TypeError: e.onCancellationRequested is not a function" calling hover provider in Insiders #54174', function () {
|
||||
|
||||
disposables.push(extHost.registerHoverProvider(nullExtensionDescription, defaultSelector, <vscode.HoverProvider>{
|
||||
provideHover(): any {
|
||||
return new types.Hover('fofofofo');
|
||||
}
|
||||
}));
|
||||
|
||||
return rpcProtocol.sync().then(() => {
|
||||
return commands.executeCommand<vscode.Hover[]>('vscode.executeHoverProvider', model.uri, new types.Position(1, 1)).then(value => {
|
||||
assert.equal(value.length, 1);
|
||||
assert.equal(value[0].contents.length, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// --- selection ranges
|
||||
|
||||
test('Selection Range, back and forth', async function () {
|
||||
|
||||
disposables.push(extHost.registerSelectionRangeProvider(nullExtensionDescription, defaultSelector, <vscode.SelectionRangeProvider>{
|
||||
provideSelectionRanges() {
|
||||
return [
|
||||
new types.SelectionRange(new types.Range(0, 10, 0, 18), new types.SelectionRange(new types.Range(0, 2, 0, 20))),
|
||||
];
|
||||
}
|
||||
}));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
let value = await commands.executeCommand<vscode.SelectionRange[]>('vscode.executeSelectionRangeProvider', model.uri, [new types.Position(0, 10)]);
|
||||
assert.equal(value.length, 1);
|
||||
assert.ok(value[0].parent);
|
||||
});
|
||||
|
||||
// --- call hierarcht
|
||||
|
||||
test('CallHierarchy, back and forth', async function () {
|
||||
|
||||
disposables.push(extHost.registerCallHierarchyProvider(nullExtensionDescription, defaultSelector, new class implements vscode.CallHierarchyProvider {
|
||||
|
||||
prepareCallHierarchy(document: vscode.TextDocument, position: vscode.Position, ): vscode.ProviderResult<vscode.CallHierarchyItem> {
|
||||
return new types.CallHierarchyItem(types.SymbolKind.Constant, 'ROOT', 'ROOT', document.uri, new types.Range(0, 0, 0, 0), new types.Range(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
provideCallHierarchyIncomingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CallHierarchyIncomingCall[]> {
|
||||
|
||||
return [new types.CallHierarchyIncomingCall(
|
||||
new types.CallHierarchyItem(types.SymbolKind.Constant, 'INCOMING', 'INCOMING', item.uri, new types.Range(0, 0, 0, 0), new types.Range(0, 0, 0, 0)),
|
||||
[new types.Range(0, 0, 0, 0)]
|
||||
)];
|
||||
}
|
||||
|
||||
provideCallHierarchyOutgoingCalls(item: vscode.CallHierarchyItem, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CallHierarchyOutgoingCall[]> {
|
||||
return [new types.CallHierarchyOutgoingCall(
|
||||
new types.CallHierarchyItem(types.SymbolKind.Constant, 'OUTGOING', 'OUTGOING', item.uri, new types.Range(0, 0, 0, 0), new types.Range(0, 0, 0, 0)),
|
||||
[new types.Range(0, 0, 0, 0)]
|
||||
)];
|
||||
}
|
||||
}));
|
||||
|
||||
await rpcProtocol.sync();
|
||||
|
||||
const root = await commands.executeCommand<vscode.CallHierarchyItem[]>('vscode.prepareCallHierarchy', model.uri, new types.Position(0, 0));
|
||||
|
||||
assert.ok(Array.isArray(root));
|
||||
assert.equal(root.length, 1);
|
||||
assert.equal(root[0].name, 'ROOT');
|
||||
|
||||
const incoming = await commands.executeCommand<vscode.CallHierarchyIncomingCall[]>('vscode.provideIncomingCalls', root[0]);
|
||||
assert.equal(incoming.length, 1);
|
||||
assert.equal(incoming[0].from.name, 'INCOMING');
|
||||
|
||||
const outgoing = await commands.executeCommand<vscode.CallHierarchyOutgoingCall[]>('vscode.provideOutgoingCalls', root[0]);
|
||||
assert.equal(outgoing.length, 1);
|
||||
assert.equal(outgoing[0].to.name, 'OUTGOING');
|
||||
});
|
||||
});
|
||||
93
src/vs/workbench/test/browser/api/extHostCommands.test.ts
Normal file
93
src/vs/workbench/test/browser/api/extHostCommands.test.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainThreadCommandsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('ExtHostCommands', function () {
|
||||
|
||||
test('dispose calls unregister', function () {
|
||||
|
||||
let lastUnregister: string;
|
||||
|
||||
const shape = new class extends mock<MainThreadCommandsShape>() {
|
||||
$registerCommand(id: string): void {
|
||||
//
|
||||
}
|
||||
$unregisterCommand(id: string): void {
|
||||
lastUnregister = id;
|
||||
}
|
||||
};
|
||||
|
||||
const commands = new ExtHostCommands(
|
||||
SingleProxyRPCProtocol(shape),
|
||||
new NullLogService()
|
||||
);
|
||||
commands.registerCommand(true, '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): void {
|
||||
//
|
||||
}
|
||||
$unregisterCommand(id: string): void {
|
||||
unregisterCounter += 1;
|
||||
}
|
||||
};
|
||||
|
||||
const commands = new ExtHostCommands(
|
||||
SingleProxyRPCProtocol(shape),
|
||||
new NullLogService()
|
||||
);
|
||||
const reg = commands.registerCommand(true, 'foo', (): any => { });
|
||||
reg.dispose();
|
||||
reg.dispose();
|
||||
reg.dispose();
|
||||
assert.equal(unregisterCounter, 1);
|
||||
});
|
||||
|
||||
test('execute with retry', async function () {
|
||||
|
||||
let count = 0;
|
||||
|
||||
const shape = new class extends mock<MainThreadCommandsShape>() {
|
||||
$registerCommand(id: string): void {
|
||||
//
|
||||
}
|
||||
async $executeCommand<T>(id: string, args: any[], retry: boolean): Promise<T | undefined> {
|
||||
count++;
|
||||
assert.equal(retry, count === 1);
|
||||
if (count === 1) {
|
||||
assert.equal(retry, true);
|
||||
throw new Error('$executeCommand:retry');
|
||||
} else {
|
||||
assert.equal(retry, false);
|
||||
return <any>17;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const commands = new ExtHostCommands(
|
||||
SingleProxyRPCProtocol(shape),
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
const result = await commands.executeCommand('fooo', [this, true]);
|
||||
assert.equal(result, 17);
|
||||
assert.equal(count, 2);
|
||||
});
|
||||
});
|
||||
734
src/vs/workbench/test/browser/api/extHostConfiguration.test.ts
Normal file
734
src/vs/workbench/test/browser/api/extHostConfiguration.test.ts
Normal file
@@ -0,0 +1,734 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
|
||||
import { TestRPCProtocol } from './testRPCProtocol';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { IWorkspaceFolder, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ConfigurationTarget, IConfigurationModel, IConfigurationChange } from 'vs/platform/configuration/common/configuration';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
|
||||
suite('ExtHostConfiguration', function () {
|
||||
|
||||
class RecordingShape extends mock<MainThreadConfigurationShape>() {
|
||||
lastArgs!: [ConfigurationTarget, string, any];
|
||||
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): Promise<void> {
|
||||
this.lastArgs = [target, key, value];
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function createExtHostWorkspace(): ExtHostWorkspace {
|
||||
return new ExtHostWorkspace(new TestRPCProtocol(), new class extends mock<IExtHostInitDataService>() { }, new NullLogService());
|
||||
}
|
||||
|
||||
function createExtHostConfiguration(contents: any = Object.create(null), shape?: MainThreadConfigurationShape) {
|
||||
if (!shape) {
|
||||
shape = new class extends mock<MainThreadConfigurationShape>() { };
|
||||
}
|
||||
return new ExtHostConfigProvider(shape, createExtHostWorkspace(), createConfigurationData(contents), new NullLogService());
|
||||
}
|
||||
|
||||
function createConfigurationData(contents: any): IConfigurationInitData {
|
||||
return {
|
||||
defaults: new ConfigurationModel(contents),
|
||||
user: new ConfigurationModel(contents),
|
||||
workspace: new ConfigurationModel(),
|
||||
folders: [],
|
||||
configurationScopes: []
|
||||
};
|
||||
}
|
||||
|
||||
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<any>('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', () => {
|
||||
|
||||
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('can modify the returned configuration', function () {
|
||||
|
||||
const all = createExtHostConfiguration({
|
||||
'farboo': {
|
||||
'config0': true,
|
||||
'nested': {
|
||||
'config1': 42,
|
||||
'config2': 'Das Pferd frisst kein Reis.'
|
||||
},
|
||||
'config4': ''
|
||||
},
|
||||
'workbench': {
|
||||
'colorCustomizations': {
|
||||
'statusBar.foreground': 'somevalue'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let testObject = all.getConfiguration();
|
||||
let actual = testObject.get<any>('farboo')!;
|
||||
actual['nested']['config1'] = 41;
|
||||
assert.equal(41, actual['nested']['config1']);
|
||||
actual['farboo1'] = 'newValue';
|
||||
assert.equal('newValue', actual['farboo1']);
|
||||
|
||||
testObject = all.getConfiguration();
|
||||
actual = testObject.get('farboo')!;
|
||||
assert.equal(actual['nested']['config1'], 42);
|
||||
assert.equal(actual['farboo1'], undefined);
|
||||
|
||||
testObject = all.getConfiguration();
|
||||
actual = testObject.get('farboo')!;
|
||||
assert.equal(actual['config0'], true);
|
||||
actual['config0'] = false;
|
||||
assert.equal(actual['config0'], false);
|
||||
|
||||
testObject = all.getConfiguration();
|
||||
actual = testObject.get('farboo')!;
|
||||
assert.equal(actual['config0'], true);
|
||||
|
||||
testObject = all.getConfiguration();
|
||||
actual = testObject.inspect('farboo')!;
|
||||
actual['value'] = 'effectiveValue';
|
||||
assert.equal('effectiveValue', actual['value']);
|
||||
|
||||
testObject = all.getConfiguration('workbench');
|
||||
actual = testObject.get('colorCustomizations')!;
|
||||
actual['statusBar.foreground'] = undefined;
|
||||
assert.equal(actual['statusBar.foreground'], undefined);
|
||||
testObject = all.getConfiguration('workbench');
|
||||
actual = testObject.get('colorCustomizations')!;
|
||||
assert.equal(actual['statusBar.foreground'], 'somevalue');
|
||||
});
|
||||
|
||||
test('Stringify returned configuration', function () {
|
||||
|
||||
const all = createExtHostConfiguration({
|
||||
'farboo': {
|
||||
'config0': true,
|
||||
'nested': {
|
||||
'config1': 42,
|
||||
'config2': 'Das Pferd frisst kein Reis.'
|
||||
},
|
||||
'config4': ''
|
||||
},
|
||||
'workbench': {
|
||||
'colorCustomizations': {
|
||||
'statusBar.foreground': 'somevalue'
|
||||
},
|
||||
'emptyobjectkey': {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const testObject = all.getConfiguration();
|
||||
let actual: any = testObject.get('farboo');
|
||||
assert.deepEqual(JSON.stringify({
|
||||
'config0': true,
|
||||
'nested': {
|
||||
'config1': 42,
|
||||
'config2': 'Das Pferd frisst kein Reis.'
|
||||
},
|
||||
'config4': ''
|
||||
}), JSON.stringify(actual));
|
||||
|
||||
assert.deepEqual(undefined, JSON.stringify(testObject.get('unknownkey')));
|
||||
|
||||
actual = testObject.get('farboo')!;
|
||||
actual['config0'] = false;
|
||||
assert.deepEqual(JSON.stringify({
|
||||
'config0': false,
|
||||
'nested': {
|
||||
'config1': 42,
|
||||
'config2': 'Das Pferd frisst kein Reis.'
|
||||
},
|
||||
'config4': ''
|
||||
}), JSON.stringify(actual));
|
||||
|
||||
actual = testObject.get<any>('workbench')!['colorCustomizations']!;
|
||||
actual['statusBar.background'] = 'anothervalue';
|
||||
assert.deepEqual(JSON.stringify({
|
||||
'statusBar.foreground': 'somevalue',
|
||||
'statusBar.background': 'anothervalue'
|
||||
}), JSON.stringify(actual));
|
||||
|
||||
actual = testObject.get('workbench');
|
||||
actual['unknownkey'] = 'somevalue';
|
||||
assert.deepEqual(JSON.stringify({
|
||||
'colorCustomizations': {
|
||||
'statusBar.foreground': 'somevalue'
|
||||
},
|
||||
'emptyobjectkey': {},
|
||||
'unknownkey': 'somevalue'
|
||||
}), JSON.stringify(actual));
|
||||
|
||||
actual = all.getConfiguration('workbench').get('emptyobjectkey');
|
||||
actual = assign(actual || {}, {
|
||||
'statusBar.background': `#0ff`,
|
||||
'statusBar.foreground': `#ff0`,
|
||||
});
|
||||
assert.deepEqual(JSON.stringify({
|
||||
'statusBar.background': `#0ff`,
|
||||
'statusBar.foreground': `#ff0`,
|
||||
}), JSON.stringify(actual));
|
||||
|
||||
actual = all.getConfiguration('workbench').get('unknownkey');
|
||||
actual = assign(actual || {}, {
|
||||
'statusBar.background': `#0ff`,
|
||||
'statusBar.foreground': `#ff0`,
|
||||
});
|
||||
assert.deepEqual(JSON.stringify({
|
||||
'statusBar.background': `#0ff`,
|
||||
'statusBar.foreground': `#ff0`,
|
||||
}), JSON.stringify(actual));
|
||||
});
|
||||
|
||||
test('cannot modify returned configuration', function () {
|
||||
|
||||
const all = createExtHostConfiguration({
|
||||
'farboo': {
|
||||
'config0': true,
|
||||
'nested': {
|
||||
'config1': 42,
|
||||
'config2': 'Das Pferd frisst kein Reis.'
|
||||
},
|
||||
'config4': ''
|
||||
}
|
||||
});
|
||||
|
||||
let testObject: any = all.getConfiguration();
|
||||
|
||||
try {
|
||||
testObject['get'] = null;
|
||||
assert.fail('This should be readonly');
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
try {
|
||||
testObject['farboo']['config0'] = false;
|
||||
assert.fail('This should be readonly');
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
try {
|
||||
testObject['farboo']['farboo1'] = 'hello';
|
||||
assert.fail('This should be readonly');
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
|
||||
test('inspect in no workspace context', function () {
|
||||
const testObject = new ExtHostConfigProvider(
|
||||
new class extends mock<MainThreadConfigurationShape>() { },
|
||||
createExtHostWorkspace(),
|
||||
{
|
||||
defaults: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'off'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
user: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
workspace: new ConfigurationModel({}, []),
|
||||
folders: [],
|
||||
configurationScopes: []
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
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: [UriComponents, IConfigurationModel][] = [];
|
||||
const workspace = new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'bounded'
|
||||
}
|
||||
}, ['editor.wordWrap']);
|
||||
folders.push([workspaceUri, workspace]);
|
||||
const extHostWorkspace = createExtHostWorkspace();
|
||||
extHostWorkspace.$initializeWorkspace({
|
||||
'id': 'foo',
|
||||
'folders': [aWorkspaceFolder(URI.file('foo'), 0)],
|
||||
'name': 'foo'
|
||||
});
|
||||
const testObject = new ExtHostConfigProvider(
|
||||
new class extends mock<MainThreadConfigurationShape>() { },
|
||||
extHostWorkspace,
|
||||
{
|
||||
defaults: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'off'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
user: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
workspace,
|
||||
folders,
|
||||
configurationScopes: []
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
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(undefined, 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: [UriComponents, IConfigurationModel][] = [];
|
||||
folders.push([firstRoot, new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'off',
|
||||
'lineNumbers': 'relative'
|
||||
}
|
||||
}, ['editor.wordWrap'])]);
|
||||
folders.push([secondRoot, new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
}
|
||||
}, ['editor.wordWrap'])]);
|
||||
folders.push([thirdRoot, new ConfigurationModel({}, [])]);
|
||||
|
||||
const extHostWorkspace = createExtHostWorkspace();
|
||||
extHostWorkspace.$initializeWorkspace({
|
||||
'id': 'foo',
|
||||
'folders': [aWorkspaceFolder(firstRoot, 0), aWorkspaceFolder(secondRoot, 1)],
|
||||
'name': 'foo'
|
||||
});
|
||||
const testObject = new ExtHostConfigProvider(
|
||||
new class extends mock<MainThreadConfigurationShape>() { },
|
||||
extHostWorkspace,
|
||||
{
|
||||
defaults: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'off',
|
||||
'lineNumbers': 'on'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
user: new ConfigurationModel({
|
||||
'editor': {
|
||||
'wordWrap': 'on'
|
||||
}
|
||||
}, ['editor.wordWrap']),
|
||||
workspace,
|
||||
folders,
|
||||
configurationScopes: []
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
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(undefined, 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(undefined, 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(undefined, 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('inspect with language overrides', function () {
|
||||
const firstRoot = URI.file('foo1');
|
||||
const secondRoot = URI.file('foo2');
|
||||
const folders: [UriComponents, IConfigurationModel][] = [];
|
||||
folders.push([firstRoot, toConfigurationModel({
|
||||
'editor.wordWrap': 'bounded',
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'unbounded',
|
||||
}
|
||||
})]);
|
||||
folders.push([secondRoot, toConfigurationModel({})]);
|
||||
|
||||
const extHostWorkspace = createExtHostWorkspace();
|
||||
extHostWorkspace.$initializeWorkspace({
|
||||
'id': 'foo',
|
||||
'folders': [aWorkspaceFolder(firstRoot, 0), aWorkspaceFolder(secondRoot, 1)],
|
||||
'name': 'foo'
|
||||
});
|
||||
const testObject = new ExtHostConfigProvider(
|
||||
new class extends mock<MainThreadConfigurationShape>() { },
|
||||
extHostWorkspace,
|
||||
{
|
||||
defaults: toConfigurationModel({
|
||||
'editor.wordWrap': 'off',
|
||||
'[markdown]': {
|
||||
'editor.wordWrap': 'bounded',
|
||||
}
|
||||
}),
|
||||
user: toConfigurationModel({
|
||||
'editor.wordWrap': 'bounded',
|
||||
'[typescript]': {
|
||||
'editor.lineNumbers': 'off',
|
||||
}
|
||||
}),
|
||||
workspace: toConfigurationModel({
|
||||
'[typescript]': {
|
||||
'editor.wordWrap': 'unbounded',
|
||||
'editor.lineNumbers': 'off',
|
||||
}
|
||||
}),
|
||||
folders,
|
||||
configurationScopes: []
|
||||
},
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
let actual = testObject.getConfiguration(undefined, { uri: firstRoot, languageId: 'typescript' }).inspect('editor.wordWrap')!;
|
||||
assert.equal(actual.defaultValue, 'off');
|
||||
assert.equal(actual.globalValue, 'bounded');
|
||||
assert.equal(actual.workspaceValue, undefined);
|
||||
assert.equal(actual.workspaceFolderValue, 'bounded');
|
||||
assert.equal(actual.defaultLanguageValue, undefined);
|
||||
assert.equal(actual.globalLanguageValue, undefined);
|
||||
assert.equal(actual.workspaceLanguageValue, 'unbounded');
|
||||
assert.equal(actual.workspaceFolderLanguageValue, 'unbounded');
|
||||
assert.deepEqual(actual.languageIds, ['markdown', 'typescript']);
|
||||
|
||||
actual = testObject.getConfiguration(undefined, { uri: secondRoot, languageId: 'typescript' }).inspect('editor.wordWrap')!;
|
||||
assert.equal(actual.defaultValue, 'off');
|
||||
assert.equal(actual.globalValue, 'bounded');
|
||||
assert.equal(actual.workspaceValue, undefined);
|
||||
assert.equal(actual.workspaceFolderValue, undefined);
|
||||
assert.equal(actual.defaultLanguageValue, undefined);
|
||||
assert.equal(actual.globalLanguageValue, undefined);
|
||||
assert.equal(actual.workspaceLanguageValue, 'unbounded');
|
||||
assert.equal(actual.workspaceFolderLanguageValue, undefined);
|
||||
assert.deepEqual(actual.languageIds, ['markdown', 'typescript']);
|
||||
});
|
||||
|
||||
|
||||
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): Promise<any> {
|
||||
return Promise.reject(new Error('Unknown Key')); // something !== OK
|
||||
}
|
||||
};
|
||||
|
||||
return createExtHostConfiguration({}, shape)
|
||||
.getConfiguration('')
|
||||
.update('', true, false)
|
||||
.then(() => assert.ok(false), err => { /* expecting rejection */ });
|
||||
});
|
||||
|
||||
test('configuration change event', (done) => {
|
||||
|
||||
const workspaceFolder = aWorkspaceFolder(URI.file('folder1'), 0);
|
||||
const extHostWorkspace = createExtHostWorkspace();
|
||||
extHostWorkspace.$initializeWorkspace({
|
||||
'id': 'foo',
|
||||
'folders': [workspaceFolder],
|
||||
'name': 'foo'
|
||||
});
|
||||
const testObject = new ExtHostConfigProvider(
|
||||
new class extends mock<MainThreadConfigurationShape>() { },
|
||||
extHostWorkspace,
|
||||
createConfigurationData({
|
||||
'farboo': {
|
||||
'config': false,
|
||||
'updatedConfig': false
|
||||
}
|
||||
}),
|
||||
new NullLogService()
|
||||
);
|
||||
|
||||
const newConfigData = createConfigurationData({
|
||||
'farboo': {
|
||||
'config': false,
|
||||
'updatedConfig': true,
|
||||
'newConfig': true,
|
||||
}
|
||||
});
|
||||
const configEventData: IConfigurationChange = { keys: ['farboo.updatedConfig', 'farboo.newConfig'], overrides: [] };
|
||||
testObject.onDidChangeConfiguration(e => {
|
||||
|
||||
assert.deepEqual(testObject.getConfiguration().get('farboo'), {
|
||||
'config': false,
|
||||
'updatedConfig': true,
|
||||
'newConfig': true,
|
||||
});
|
||||
|
||||
assert.ok(e.affectsConfiguration('farboo'));
|
||||
assert.ok(e.affectsConfiguration('farboo', workspaceFolder.uri));
|
||||
assert.ok(e.affectsConfiguration('farboo', URI.file('any')));
|
||||
|
||||
assert.ok(e.affectsConfiguration('farboo.updatedConfig'));
|
||||
assert.ok(e.affectsConfiguration('farboo.updatedConfig', workspaceFolder.uri));
|
||||
assert.ok(e.affectsConfiguration('farboo.updatedConfig', URI.file('any')));
|
||||
|
||||
assert.ok(e.affectsConfiguration('farboo.newConfig'));
|
||||
assert.ok(e.affectsConfiguration('farboo.newConfig', workspaceFolder.uri));
|
||||
assert.ok(e.affectsConfiguration('farboo.newConfig', URI.file('any')));
|
||||
|
||||
assert.ok(!e.affectsConfiguration('farboo.config'));
|
||||
assert.ok(!e.affectsConfiguration('farboo.config', workspaceFolder.uri));
|
||||
assert.ok(!e.affectsConfiguration('farboo.config', URI.file('any')));
|
||||
done();
|
||||
});
|
||||
|
||||
testObject.$acceptConfigurationChanged(newConfigData, configEventData);
|
||||
});
|
||||
|
||||
function aWorkspaceFolder(uri: URI, index: number, name: string = ''): IWorkspaceFolder {
|
||||
return new WorkspaceFolder({ uri, name, index });
|
||||
}
|
||||
|
||||
function toConfigurationModel(obj: any): ConfigurationModel {
|
||||
const parser = new ConfigurationModelParser('test');
|
||||
parser.parseContent(JSON.stringify(obj));
|
||||
return parser.configurationModel;
|
||||
}
|
||||
|
||||
});
|
||||
463
src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts
Normal file
463
src/vs/workbench/test/browser/api/extHostDiagnostics.test.ts
Normal file
@@ -0,0 +1,463 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { DiagnosticCollection, ExtHostDiagnostics } from 'vs/workbench/api/common/extHostDiagnostics';
|
||||
import { Diagnostic, DiagnosticSeverity, Range, DiagnosticRelatedInformation, Location } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { MainThreadDiagnosticsShape, IMainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('ExtHostDiagnostics', () => {
|
||||
|
||||
class DiagnosticsShape extends mock<MainThreadDiagnosticsShape>() {
|
||||
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
|
||||
//
|
||||
}
|
||||
$clear(owner: string): void {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
test('disposeCheck', () => {
|
||||
|
||||
const collection = new DiagnosticCollection('test', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
|
||||
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', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
assert.equal(collection.name, 'test');
|
||||
collection.dispose();
|
||||
assert.throws(() => collection.name);
|
||||
|
||||
let c = 0;
|
||||
collection = new DiagnosticCollection('test', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
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', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
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')) as Diagnostic[];
|
||||
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: readonly Diagnostic[]) => {
|
||||
assert.throws(() => (array as Diagnostic[]).length = 0);
|
||||
assert.throws(() => (array as Diagnostic[]).pop());
|
||||
assert.throws(() => (array as Diagnostic[])[0] = new Diagnostic(new Range(0, 0, 0, 0), 'evil'));
|
||||
});
|
||||
|
||||
array = collection.get(URI.parse('foo:bar')) as Diagnostic[];
|
||||
assert.equal(array.length, 2);
|
||||
|
||||
collection.dispose();
|
||||
});
|
||||
|
||||
|
||||
test('diagnostics collection, set with dupliclated tuples', function () {
|
||||
let collection = new DiagnosticCollection('test', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
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!: [UriComponents, IMarkerData[]][];
|
||||
let collection = new DiagnosticCollection('test', 'test', 100, new class extends DiagnosticsShape {
|
||||
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
|
||||
lastEntries = entries;
|
||||
return super.$changeMany(owner, entries);
|
||||
}
|
||||
}, new Emitter());
|
||||
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('do send message when not making a change', function () {
|
||||
|
||||
let changeCount = 0;
|
||||
let eventCount = 0;
|
||||
|
||||
const emitter = new Emitter<any>();
|
||||
emitter.event(_ => eventCount += 1);
|
||||
const collection = new DiagnosticCollection('test', 'test', 100, new class extends DiagnosticsShape {
|
||||
$changeMany() {
|
||||
changeCount += 1;
|
||||
}
|
||||
}, emitter);
|
||||
|
||||
let uri = URI.parse('sc:hightower');
|
||||
let diag = new Diagnostic(new Range(0, 0, 0, 1), 'ffff');
|
||||
|
||||
collection.set(uri, [diag]);
|
||||
assert.equal(changeCount, 1);
|
||||
assert.equal(eventCount, 1);
|
||||
|
||||
collection.set(uri, [diag]);
|
||||
assert.equal(changeCount, 2);
|
||||
assert.equal(eventCount, 2);
|
||||
});
|
||||
|
||||
test('diagnostics collection, tuples and undefined (small array), #15585', function () {
|
||||
|
||||
const collection = new DiagnosticCollection('test', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
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', 'test', 100, new DiagnosticsShape(), new Emitter());
|
||||
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!: [UriComponents, IMarkerData[]][];
|
||||
let collection = new DiagnosticCollection('test', 'test', 250, new class extends DiagnosticsShape {
|
||||
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]): void {
|
||||
lastEntries = entries;
|
||||
return super.$changeMany(owner, entries);
|
||||
}
|
||||
}, new Emitter());
|
||||
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, MarkerSeverity.Error);
|
||||
assert.equal(lastEntries[0][1][200].severity, MarkerSeverity.Warning);
|
||||
assert.equal(lastEntries[0][1][250].severity, MarkerSeverity.Info);
|
||||
});
|
||||
|
||||
test('diagnostic eventing', async function () {
|
||||
let emitter = new Emitter<Array<string | URI>>();
|
||||
let collection = new DiagnosticCollection('ddd', 'test', 100, new DiagnosticsShape(), emitter);
|
||||
|
||||
let diag1 = new Diagnostic(new Range(1, 1, 2, 3), 'diag1');
|
||||
let diag2 = new Diagnostic(new Range(1, 1, 2, 3), 'diag2');
|
||||
let diag3 = new Diagnostic(new Range(1, 1, 2, 3), 'diag3');
|
||||
|
||||
let p = Event.toPromise(emitter.event).then(a => {
|
||||
assert.equal(a.length, 1);
|
||||
assert.equal(a[0].toString(), 'aa:bb');
|
||||
assert.ok(URI.isUri(a[0]));
|
||||
});
|
||||
collection.set(URI.parse('aa:bb'), []);
|
||||
await p;
|
||||
|
||||
p = Event.toPromise(emitter.event).then(e => {
|
||||
assert.equal(e.length, 2);
|
||||
assert.ok(URI.isUri(e[0]));
|
||||
assert.ok(URI.isUri(e[1]));
|
||||
assert.equal(e[0].toString(), 'aa:bb');
|
||||
assert.equal(e[1].toString(), 'aa:cc');
|
||||
});
|
||||
collection.set([
|
||||
[URI.parse('aa:bb'), [diag1]],
|
||||
[URI.parse('aa:cc'), [diag2, diag3]],
|
||||
]);
|
||||
await p;
|
||||
|
||||
p = Event.toPromise(emitter.event).then(e => {
|
||||
assert.equal(e.length, 2);
|
||||
assert.ok(typeof e[0] === 'string');
|
||||
assert.ok(typeof e[1] === 'string');
|
||||
});
|
||||
collection.clear();
|
||||
await p;
|
||||
});
|
||||
|
||||
test('vscode.languages.onDidChangeDiagnostics Does Not Provide Document URI #49582', async function () {
|
||||
let emitter = new Emitter<Array<string | URI>>();
|
||||
let collection = new DiagnosticCollection('ddd', 'test', 100, new DiagnosticsShape(), emitter);
|
||||
|
||||
let diag1 = new Diagnostic(new Range(1, 1, 2, 3), 'diag1');
|
||||
|
||||
// delete
|
||||
collection.set(URI.parse('aa:bb'), [diag1]);
|
||||
let p = Event.toPromise(emitter.event).then(e => {
|
||||
assert.equal(e[0].toString(), 'aa:bb');
|
||||
});
|
||||
collection.delete(URI.parse('aa:bb'));
|
||||
await p;
|
||||
|
||||
// set->undefined (as delete)
|
||||
collection.set(URI.parse('aa:bb'), [diag1]);
|
||||
p = Event.toPromise(emitter.event).then(e => {
|
||||
assert.equal(e[0].toString(), 'aa:bb');
|
||||
});
|
||||
collection.set(URI.parse('aa:bb'), undefined!);
|
||||
await p;
|
||||
});
|
||||
|
||||
test('diagnostics with related information', function (done) {
|
||||
|
||||
let collection = new DiagnosticCollection('ddd', 'test', 100, new class extends DiagnosticsShape {
|
||||
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]) {
|
||||
|
||||
let [[, data]] = entries;
|
||||
assert.equal(entries.length, 1);
|
||||
assert.equal(data.length, 1);
|
||||
|
||||
let [diag] = data;
|
||||
assert.equal(diag.relatedInformation!.length, 2);
|
||||
assert.equal(diag.relatedInformation![0].message, 'more1');
|
||||
assert.equal(diag.relatedInformation![1].message, 'more2');
|
||||
done();
|
||||
}
|
||||
}, new Emitter<any>());
|
||||
|
||||
let diag = new Diagnostic(new Range(0, 0, 1, 1), 'Foo');
|
||||
diag.relatedInformation = [
|
||||
new DiagnosticRelatedInformation(new Location(URI.parse('cc:dd'), new Range(0, 0, 0, 0)), 'more1'),
|
||||
new DiagnosticRelatedInformation(new Location(URI.parse('cc:ee'), new Range(0, 0, 0, 0)), 'more2')
|
||||
];
|
||||
|
||||
collection.set(URI.parse('aa:bb'), [diag]);
|
||||
});
|
||||
|
||||
test('vscode.languages.getDiagnostics appears to return old diagnostics in some circumstances #54359', function () {
|
||||
const ownerHistory: string[] = [];
|
||||
const diags = new ExtHostDiagnostics(new class implements IMainContext {
|
||||
getProxy(id: any): any {
|
||||
return new class DiagnosticsShape {
|
||||
$clear(owner: string): void {
|
||||
ownerHistory.push(owner);
|
||||
}
|
||||
};
|
||||
}
|
||||
set(): any {
|
||||
return null;
|
||||
}
|
||||
assertRegistered(): void {
|
||||
|
||||
}
|
||||
}, new NullLogService());
|
||||
|
||||
let collection1 = diags.createDiagnosticCollection('foo');
|
||||
let collection2 = diags.createDiagnosticCollection('foo'); // warns, uses a different owner
|
||||
|
||||
collection1.clear();
|
||||
collection2.clear();
|
||||
|
||||
assert.equal(ownerHistory.length, 2);
|
||||
assert.equal(ownerHistory[0], 'foo');
|
||||
assert.equal(ownerHistory[1], 'foo0');
|
||||
});
|
||||
|
||||
test('Error updating diagnostics from extension #60394', function () {
|
||||
let callCount = 0;
|
||||
let collection = new DiagnosticCollection('ddd', 'test', 100, new class extends DiagnosticsShape {
|
||||
$changeMany(owner: string, entries: [UriComponents, IMarkerData[]][]) {
|
||||
callCount += 1;
|
||||
}
|
||||
}, new Emitter<any>());
|
||||
|
||||
let array: Diagnostic[] = [];
|
||||
let diag1 = new Diagnostic(new Range(0, 0, 1, 1), 'Foo');
|
||||
let diag2 = new Diagnostic(new Range(0, 0, 1, 1), 'Bar');
|
||||
|
||||
array.push(diag1, diag2);
|
||||
|
||||
collection.set(URI.parse('test:me'), array);
|
||||
assert.equal(callCount, 1);
|
||||
|
||||
collection.set(URI.parse('test:me'), array);
|
||||
assert.equal(callCount, 2); // equal array
|
||||
|
||||
array.push(diag2);
|
||||
collection.set(URI.parse('test:me'), array);
|
||||
assert.equal(callCount, 3); // same but un-equal array
|
||||
});
|
||||
|
||||
test('Diagnostics created by tasks aren\'t accessible to extensions #47292', async function () {
|
||||
const diags = new ExtHostDiagnostics(new class implements IMainContext {
|
||||
getProxy(id: any): any {
|
||||
return {};
|
||||
}
|
||||
set(): any {
|
||||
return null;
|
||||
}
|
||||
assertRegistered(): void {
|
||||
|
||||
}
|
||||
}, new NullLogService());
|
||||
|
||||
|
||||
//
|
||||
const uri = URI.parse('foo:bar');
|
||||
const data: IMarkerData[] = [{
|
||||
message: 'message',
|
||||
startLineNumber: 1,
|
||||
startColumn: 1,
|
||||
endLineNumber: 1,
|
||||
endColumn: 1,
|
||||
severity: 3
|
||||
}];
|
||||
|
||||
const p1 = Event.toPromise(diags.onDidChangeDiagnostics);
|
||||
diags.$acceptMarkersChange([[uri, data]]);
|
||||
await p1;
|
||||
assert.equal(diags.getDiagnostics(uri).length, 1);
|
||||
|
||||
const p2 = Event.toPromise(diags.onDidChangeDiagnostics);
|
||||
diags.$acceptMarkersChange([[uri, []]]);
|
||||
await p2;
|
||||
assert.equal(diags.getDiagnostics(uri).length, 0);
|
||||
});
|
||||
});
|
||||
468
src/vs/workbench/test/browser/api/extHostDocumentData.test.ts
Normal file
468
src/vs/workbench/test/browser/api/extHostDocumentData.test.ts
Normal file
@@ -0,0 +1,468 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { Position } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { MainThreadDocumentsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
|
||||
import { mock } from 'vs/workbench/test/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', () => {
|
||||
assert.throws(() => (data as any).document.uri = null);
|
||||
assert.throws(() => (data as any).document.fileName = 'foofile');
|
||||
assert.throws(() => (data as any).document.isDirty = false);
|
||||
assert.throws(() => (data as any).document.isUntitled = false);
|
||||
assert.throws(() => (data as any).document.languageId = 'dddd');
|
||||
assert.throws(() => (data as any).document.lineCount = 9);
|
||||
});
|
||||
|
||||
test('save, when disposed', function () {
|
||||
let saved: URI;
|
||||
let data = new ExtHostDocumentData(new class extends mock<MainThreadDocumentsShape>() {
|
||||
$trySaveDocument(uri: URI) {
|
||||
assert.ok(!saved);
|
||||
saved = uri;
|
||||
return Promise.resolve(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', () => {
|
||||
|
||||
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 },
|
||||
rangeOffset: undefined!,
|
||||
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', () => {
|
||||
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 },
|
||||
rangeOffset: undefined!,
|
||||
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 },
|
||||
rangeOffset: undefined!,
|
||||
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 },
|
||||
rangeOffset: undefined!,
|
||||
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 },
|
||||
rangeOffset: undefined!,
|
||||
rangeLength: undefined!,
|
||||
text: ''
|
||||
}],
|
||||
eol: undefined!,
|
||||
versionId: undefined!,
|
||||
});
|
||||
|
||||
assertOffsetAt(0, 1, 1);
|
||||
assertOffsetAt(0, 2, 2);
|
||||
assertOffsetAt(1, 0, 25);
|
||||
});
|
||||
|
||||
test('positionAt', () => {
|
||||
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', () => {
|
||||
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 /.*/
|
||||
assert.throws(() => data.document.getWordRangeAtPosition(new Position(0, 2), /.*/)!);
|
||||
|
||||
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,
|
||||
rangeOffset: undefined!,
|
||||
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,395 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { TextDocumentSaveReason, TextEdit, Position, EndOfLine } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { MainThreadTextEditorsShape, IWorkspaceEditDto, IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
suite('ExtHostDocumentSaveParticipant', () => {
|
||||
|
||||
let resource = URI.parse('foo:bar');
|
||||
let mainThreadEditors = new class extends mock<MainThreadTextEditorsShape>() { };
|
||||
let documents: ExtHostDocuments;
|
||||
let nullLogService = new NullLogService();
|
||||
let nullExtensionDescription: IExtensionDescription = {
|
||||
identifier: new ExtensionIdentifier('nullExtensionDescription'),
|
||||
name: 'Null Extension Description',
|
||||
publisher: 'vscode',
|
||||
enableProposedApi: false,
|
||||
engines: undefined!,
|
||||
extensionLocation: undefined!,
|
||||
isBuiltin: false,
|
||||
isUnderDevelopment: false,
|
||||
version: undefined!
|
||||
};
|
||||
|
||||
setup(() => {
|
||||
const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null), new NullLogService());
|
||||
documentsAndEditors.$acceptDocumentsAndEditorsDelta({
|
||||
addedDocuments: [{
|
||||
isDirty: false,
|
||||
modeId: 'foo',
|
||||
uri: resource,
|
||||
versionId: 1,
|
||||
lines: ['foo'],
|
||||
EOL: '\n',
|
||||
}]
|
||||
});
|
||||
documents = new ExtHostDocuments(SingleProxyRPCProtocol(null), documentsAndEditors);
|
||||
});
|
||||
|
||||
test('no listeners, no problem', () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => assert.ok(true));
|
||||
});
|
||||
|
||||
test('event delivery', () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let event: vscode.TextDocumentWillSaveEvent;
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(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(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let event: vscode.TextDocumentWillSaveEvent;
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
event = e;
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
sub.dispose();
|
||||
|
||||
assert.ok(event);
|
||||
assert.throws(() => { (event.document as any) = null!; });
|
||||
});
|
||||
});
|
||||
|
||||
test('event delivery, bad listener', () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(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(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
throw new Error('💀');
|
||||
});
|
||||
let event: vscode.TextDocumentWillSaveEvent;
|
||||
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(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(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let counter = 0;
|
||||
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
assert.equal(counter++, 0);
|
||||
});
|
||||
|
||||
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
assert.equal(counter++, 1);
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
sub1.dispose();
|
||||
sub2.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
test('event delivery, ignore bad listeners', async () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 5, errors: 1 });
|
||||
|
||||
let callCount = 0;
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
callCount += 1;
|
||||
throw new Error('boom');
|
||||
});
|
||||
|
||||
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
|
||||
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
|
||||
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
|
||||
await participant.$participateInSave(resource, SaveReason.EXPLICIT);
|
||||
|
||||
sub.dispose();
|
||||
assert.equal(callCount, 2);
|
||||
});
|
||||
|
||||
test('event delivery, overall timeout', () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 20, errors: 5 });
|
||||
|
||||
let callCount = 0;
|
||||
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
callCount += 1;
|
||||
event.waitUntil(timeout(1));
|
||||
});
|
||||
|
||||
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
callCount += 1;
|
||||
event.waitUntil(timeout(170));
|
||||
});
|
||||
|
||||
let sub3 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(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(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
|
||||
event.waitUntil(timeout(10));
|
||||
event.waitUntil(timeout(10));
|
||||
event.waitUntil(timeout(10));
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
sub.dispose();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('event delivery, waitUntil must be called sync', () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
|
||||
event.waitUntil(new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
assert.throws(() => event.waitUntil(timeout(10)));
|
||||
resolve(undefined);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
|
||||
}, 10);
|
||||
}));
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
sub.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
test('event delivery, waitUntil will timeout', () => {
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, mainThreadEditors, { timeout: 5, errors: 3 });
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (event) {
|
||||
event.waitUntil(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(nullLogService, documents, mainThreadEditors);
|
||||
|
||||
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
e.waitUntil(Promise.reject(new Error('dddd')));
|
||||
});
|
||||
|
||||
let event: vscode.TextDocumentWillSaveEvent;
|
||||
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
event = e;
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
assert.ok(event);
|
||||
sub1.dispose();
|
||||
sub2.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
test('event delivery, pushEdits sync', () => {
|
||||
|
||||
let dto: IWorkspaceEditDto;
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, new class extends mock<MainThreadTextEditorsShape>() {
|
||||
$tryApplyWorkspaceEdit(_edits: IWorkspaceEditDto) {
|
||||
dto = _edits;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
e.waitUntil(Promise.resolve([TextEdit.insert(new Position(0, 0), 'bar')]));
|
||||
e.waitUntil(Promise.resolve([TextEdit.setEndOfLine(EndOfLine.CRLF)]));
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
sub.dispose();
|
||||
|
||||
assert.equal(dto.edits.length, 2);
|
||||
assert.ok((<IWorkspaceTextEditDto>dto.edits[0]).edit);
|
||||
assert.ok((<IWorkspaceTextEditDto>dto.edits[1]).edit);
|
||||
});
|
||||
});
|
||||
|
||||
test('event delivery, concurrent change', () => {
|
||||
|
||||
let edits: IWorkspaceEditDto;
|
||||
const participant = new ExtHostDocumentSaveParticipant(nullLogService, documents, new class extends mock<MainThreadTextEditorsShape>() {
|
||||
$tryApplyWorkspaceEdit(_edits: IWorkspaceEditDto) {
|
||||
edits = _edits;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
|
||||
// concurrent change from somewhere
|
||||
documents.$acceptModelChanged(resource, {
|
||||
changes: [{
|
||||
range: { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 },
|
||||
rangeOffset: undefined!,
|
||||
rangeLength: undefined!,
|
||||
text: 'bar'
|
||||
}],
|
||||
eol: undefined!,
|
||||
versionId: 2
|
||||
}, true);
|
||||
|
||||
e.waitUntil(Promise.resolve([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(nullLogService, documents, new class extends mock<MainThreadTextEditorsShape>() {
|
||||
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto) {
|
||||
|
||||
for (const edit of dto.edits) {
|
||||
|
||||
const uri = URI.revive((<IWorkspaceTextEditDto>edit).resource);
|
||||
const { text, range } = (<IWorkspaceTextEditDto>edit).edit;
|
||||
documents.$acceptModelChanged(uri, {
|
||||
changes: [{
|
||||
range,
|
||||
text,
|
||||
rangeOffset: undefined!,
|
||||
rangeLength: undefined!,
|
||||
}],
|
||||
eol: undefined!,
|
||||
versionId: documents.getDocumentData(uri)!.version + 1
|
||||
}, true);
|
||||
// }
|
||||
}
|
||||
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
const document = documents.getDocument(resource);
|
||||
|
||||
let sub1 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
// the document state we started with
|
||||
assert.equal(document.version, 1);
|
||||
assert.equal(document.getText(), 'foo');
|
||||
|
||||
e.waitUntil(Promise.resolve([TextEdit.insert(new Position(0, 0), 'bar')]));
|
||||
});
|
||||
|
||||
let sub2 = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
// the document state AFTER the first listener kicked in
|
||||
assert.equal(document.version, 2);
|
||||
assert.equal(document.getText(), 'barfoo');
|
||||
|
||||
e.waitUntil(Promise.resolve([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');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('Log failing listener', function () {
|
||||
let didLogSomething = false;
|
||||
let participant = new ExtHostDocumentSaveParticipant(new class extends NullLogService {
|
||||
error(message: string | Error, ...args: any[]): void {
|
||||
didLogSomething = true;
|
||||
}
|
||||
}, documents, mainThreadEditors);
|
||||
|
||||
|
||||
let sub = participant.getOnWillSaveTextDocumentEvent(nullExtensionDescription)(function (e) {
|
||||
throw new Error('boom');
|
||||
});
|
||||
|
||||
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
|
||||
sub.dispose();
|
||||
assert.equal(didLogSomething, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('ExtHostDocumentsAndEditors', () => {
|
||||
|
||||
let editors: ExtHostDocumentsAndEditors;
|
||||
|
||||
setup(function () {
|
||||
editors = new ExtHostDocumentsAndEditors(new TestRPCProtocol(), new NullLogService());
|
||||
});
|
||||
|
||||
test('The value of TextDocument.isClosed is incorrect when a text document is closed, #27949', () => {
|
||||
|
||||
editors.$acceptDocumentsAndEditorsDelta({
|
||||
addedDocuments: [{
|
||||
EOL: '\n',
|
||||
isDirty: true,
|
||||
modeId: 'fooLang',
|
||||
uri: URI.parse('foo:bar'),
|
||||
versionId: 1,
|
||||
lines: [
|
||||
'first',
|
||||
'second'
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
return new Promise((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: [URI.parse('foo:bar')]
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
|
||||
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('ExtHostFileSystemEventService', () => {
|
||||
|
||||
|
||||
test('FileSystemWatcher ignore events properties are reversed #26851', function () {
|
||||
|
||||
const protocol: IMainContext = {
|
||||
getProxy: () => { return undefined!; },
|
||||
set: undefined!,
|
||||
assertRegistered: undefined!
|
||||
};
|
||||
|
||||
const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingInteresting', false, false, false);
|
||||
assert.equal(watcher1.ignoreChangeEvents, false);
|
||||
assert.equal(watcher1.ignoreCreateEvents, false);
|
||||
assert.equal(watcher1.ignoreDeleteEvents, false);
|
||||
|
||||
const watcher2 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher('**/somethingBoring', true, true, true);
|
||||
assert.equal(watcher2.ignoreChangeEvents, true);
|
||||
assert.equal(watcher2.ignoreCreateEvents, true);
|
||||
assert.equal(watcher2.ignoreDeleteEvents, true);
|
||||
});
|
||||
|
||||
});
|
||||
1124
src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts
Normal file
1124
src/vs/workbench/test/browser/api/extHostLanguageFeatures.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
147
src/vs/workbench/test/browser/api/extHostMessagerService.test.ts
Normal file
147
src/vs/workbench/test/browser/api/extHostMessagerService.test.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { MainThreadMessageService } from 'vs/workbench/api/browser/mainThreadMessageService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService, INotification, NoOpNotification, INotificationHandle, Severity, IPromptChoice, IPromptOptions, IStatusMessageOptions, NotificationsFilter } from 'vs/platform/notification/common/notification';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
const emptyDialogService = new class implements IDialogService {
|
||||
_serviceBrand: undefined;
|
||||
show(): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
confirm(): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
|
||||
about(): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
};
|
||||
|
||||
const emptyCommandService: ICommandService = {
|
||||
_serviceBrand: undefined,
|
||||
onWillExecuteCommand: () => Disposable.None,
|
||||
onDidExecuteCommand: () => Disposable.None,
|
||||
executeCommand: (commandId: string, ...args: any[]): Promise<any> => {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const emptyNotificationService = new class implements INotificationService {
|
||||
_serviceBrand: undefined;
|
||||
notify(...args: any[]): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
info(...args: any[]): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
warn(...args: any[]): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
error(...args: any[]): never {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle {
|
||||
throw new Error('not implemented');
|
||||
}
|
||||
status(message: string | Error, options?: IStatusMessageOptions): IDisposable {
|
||||
return Disposable.None;
|
||||
}
|
||||
setFilter(filter: NotificationsFilter): void {
|
||||
throw new Error('not implemented.');
|
||||
}
|
||||
};
|
||||
|
||||
class EmptyNotificationService implements INotificationService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(private withNotify: (notification: INotification) => void) {
|
||||
}
|
||||
|
||||
notify(notification: INotification): INotificationHandle {
|
||||
this.withNotify(notification);
|
||||
|
||||
return new NoOpNotification();
|
||||
}
|
||||
info(message: any): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
warn(message: any): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
error(message: any): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle {
|
||||
throw new Error('Method not implemented');
|
||||
}
|
||||
status(message: string, options?: IStatusMessageOptions): IDisposable {
|
||||
return Disposable.None;
|
||||
}
|
||||
setFilter(filter: NotificationsFilter): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
suite('ExtHostMessageService', function () {
|
||||
|
||||
test('propagte handle on select', async function () {
|
||||
|
||||
let service = new MainThreadMessageService(null!, new EmptyNotificationService(notification => {
|
||||
assert.equal(notification.actions!.primary!.length, 1);
|
||||
platform.setImmediate(() => notification.actions!.primary![0].run());
|
||||
}), emptyCommandService, emptyDialogService);
|
||||
|
||||
const handle = await service.$showMessage(1, 'h', {}, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]);
|
||||
assert.equal(handle, 42);
|
||||
});
|
||||
|
||||
suite('modal', () => {
|
||||
test('calls dialog service', async () => {
|
||||
const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock<IDialogService>() {
|
||||
show(severity: Severity, message: string, buttons: string[]) {
|
||||
assert.equal(severity, 1);
|
||||
assert.equal(message, 'h');
|
||||
assert.equal(buttons.length, 2);
|
||||
assert.equal(buttons[1], 'Cancel');
|
||||
return Promise.resolve({ choice: 0 });
|
||||
}
|
||||
} as IDialogService);
|
||||
|
||||
const handle = await service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]);
|
||||
assert.equal(handle, 42);
|
||||
});
|
||||
|
||||
test('returns undefined when cancelled', async () => {
|
||||
const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock<IDialogService>() {
|
||||
show() {
|
||||
return Promise.resolve({ choice: 1 });
|
||||
}
|
||||
} as IDialogService);
|
||||
|
||||
const handle = await service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: false }]);
|
||||
assert.equal(handle, undefined);
|
||||
});
|
||||
|
||||
test('hides Cancel button when not needed', async () => {
|
||||
const service = new MainThreadMessageService(null!, emptyNotificationService, emptyCommandService, new class extends mock<IDialogService>() {
|
||||
show(severity: Severity, message: string, buttons: string[]) {
|
||||
assert.equal(buttons.length, 1);
|
||||
return Promise.resolve({ choice: 0 });
|
||||
}
|
||||
} as IDialogService);
|
||||
|
||||
const handle = await service.$showMessage(1, 'h', { modal: true }, [{ handle: 42, title: 'a thing', isCloseAffordance: true }]);
|
||||
assert.equal(handle, 42);
|
||||
});
|
||||
});
|
||||
});
|
||||
514
src/vs/workbench/test/browser/api/extHostTextEditor.test.ts
Normal file
514
src/vs/workbench/test/browser/api/extHostTextEditor.test.ts
Normal file
@@ -0,0 +1,514 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import { TextEditorLineNumbersStyle, Range } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { TextEditorCursorStyle, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { MainThreadTextEditorsShape, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('ExtHostTextEditor', () => {
|
||||
|
||||
let editor: ExtHostTextEditor;
|
||||
let doc = new ExtHostDocumentData(undefined!, URI.file(''), [
|
||||
'aaaa bbbb+cccc abc'
|
||||
], '\n', 'text', 1, false);
|
||||
|
||||
setup(() => {
|
||||
editor = new ExtHostTextEditor('fake', null!, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 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([]));
|
||||
});
|
||||
|
||||
test('API [bug]: registerTextEditorCommand clears redo stack even if no edits are made #55163', async function () {
|
||||
let applyCount = 0;
|
||||
let editor = new ExtHostTextEditor('edt1',
|
||||
new class extends mock<MainThreadTextEditorsShape>() {
|
||||
$tryApplyEdits(): Promise<boolean> {
|
||||
applyCount += 1;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1);
|
||||
|
||||
await editor.edit(edit => { });
|
||||
assert.equal(applyCount, 0);
|
||||
|
||||
await editor.edit(edit => { edit.setEndOfLine(1); });
|
||||
assert.equal(applyCount, 1);
|
||||
|
||||
await editor.edit(edit => { edit.delete(new Range(0, 0, 1, 1)); });
|
||||
assert.equal(applyCount, 2);
|
||||
});
|
||||
});
|
||||
|
||||
suite('ExtHostTextEditorOptions', () => {
|
||||
|
||||
let opts: ExtHostTextEditorOptions;
|
||||
let calls: ITextEditorConfigurationUpdate[] = [];
|
||||
|
||||
setup(() => {
|
||||
calls = [];
|
||||
let mockProxy: MainThreadTextEditorsShape = {
|
||||
dispose: undefined!,
|
||||
$trySetOptions: (id: string, options: ITextEditorConfigurationUpdate) => {
|
||||
assert.equal(id, '1');
|
||||
calls.push(options);
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
$tryShowTextDocument: undefined!,
|
||||
$registerTextEditorDecorationType: undefined!,
|
||||
$removeTextEditorDecorationType: undefined!,
|
||||
$tryShowEditor: undefined!,
|
||||
$tryHideEditor: undefined!,
|
||||
$trySetDecorations: undefined!,
|
||||
$trySetDecorationsFast: undefined!,
|
||||
$tryRevealRange: undefined!,
|
||||
$trySetSelections: undefined!,
|
||||
$tryApplyEdits: undefined!,
|
||||
$tryApplyWorkspaceEdit: undefined!,
|
||||
$tryInsertSnippet: undefined!,
|
||||
$getDiffInformation: undefined!
|
||||
};
|
||||
opts = new ExtHostTextEditorOptions(mockProxy, '1', {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
}, new NullLogService());
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
opts = null!;
|
||||
calls = null!;
|
||||
});
|
||||
|
||||
function assertState(opts: ExtHostTextEditorOptions, expected: IResolvedTextEditorConfiguration): void {
|
||||
let actual = {
|
||||
tabSize: opts.tabSize,
|
||||
indentSize: opts.indentSize,
|
||||
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,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can change tabSize to positive integer', () => {
|
||||
opts.tabSize = 1;
|
||||
assertState(opts, {
|
||||
tabSize: 1,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ tabSize: 1 }]);
|
||||
});
|
||||
|
||||
test('can change tabSize to positive float', () => {
|
||||
opts.tabSize = 2.3;
|
||||
assertState(opts, {
|
||||
tabSize: 2,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ tabSize: 2 }]);
|
||||
});
|
||||
|
||||
test('can change tabSize to a string number', () => {
|
||||
opts.tabSize = '2';
|
||||
assertState(opts, {
|
||||
tabSize: 2,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ tabSize: 2 }]);
|
||||
});
|
||||
|
||||
test('tabSize can request indentation detection', () => {
|
||||
opts.tabSize = 'auto';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ tabSize: 'auto' }]);
|
||||
});
|
||||
|
||||
test('ignores invalid tabSize 1', () => {
|
||||
opts.tabSize = null!;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid tabSize 2', () => {
|
||||
opts.tabSize = -5;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid tabSize 3', () => {
|
||||
opts.tabSize = 'hello';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid tabSize 4', () => {
|
||||
opts.tabSize = '-17';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can set indentSize to the same value', () => {
|
||||
opts.indentSize = 4;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can change indentSize to positive integer', () => {
|
||||
opts.indentSize = 1;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 1,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ indentSize: 1 }]);
|
||||
});
|
||||
|
||||
test('can change indentSize to positive float', () => {
|
||||
opts.indentSize = 2.3;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 2,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ indentSize: 2 }]);
|
||||
});
|
||||
|
||||
test('can change indentSize to a string number', () => {
|
||||
opts.indentSize = '2';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 2,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ indentSize: 2 }]);
|
||||
});
|
||||
|
||||
test('indentSize can request to use tabSize', () => {
|
||||
opts.indentSize = 'tabSize';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ indentSize: 'tabSize' }]);
|
||||
});
|
||||
|
||||
test('indentSize cannot request indentation detection', () => {
|
||||
opts.indentSize = 'auto';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid indentSize 1', () => {
|
||||
opts.indentSize = null!;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid indentSize 2', () => {
|
||||
opts.indentSize = -5;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid indentSize 3', () => {
|
||||
opts.indentSize = 'hello';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('ignores invalid indentSize 4', () => {
|
||||
opts.indentSize = '-17';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can set insertSpaces to the same value', () => {
|
||||
opts.insertSpaces = false;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can set insertSpaces to boolean', () => {
|
||||
opts.insertSpaces = true;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: true,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ insertSpaces: true }]);
|
||||
});
|
||||
|
||||
test('can set insertSpaces to false string', () => {
|
||||
opts.insertSpaces = 'false';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can set insertSpaces to truey', () => {
|
||||
opts.insertSpaces = 'hello';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: true,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ insertSpaces: true }]);
|
||||
});
|
||||
|
||||
test('insertSpaces can request indentation detection', () => {
|
||||
opts.insertSpaces = 'auto';
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ insertSpaces: 'auto' }]);
|
||||
});
|
||||
|
||||
test('can set cursorStyle to same value', () => {
|
||||
opts.cursorStyle = TextEditorCursorStyle.Line;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can change cursorStyle', () => {
|
||||
opts.cursorStyle = TextEditorCursorStyle.Block;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Block,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block }]);
|
||||
});
|
||||
|
||||
test('can set lineNumbers to same value', () => {
|
||||
opts.lineNumbers = TextEditorLineNumbersStyle.On;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can change lineNumbers', () => {
|
||||
opts.lineNumbers = TextEditorLineNumbersStyle.Off;
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.Off
|
||||
});
|
||||
assert.deepEqual(calls, [{ lineNumbers: RenderLineNumbersType.Off }]);
|
||||
});
|
||||
|
||||
test('can do bulk updates 0', () => {
|
||||
opts.assign({
|
||||
tabSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: TextEditorLineNumbersStyle.On
|
||||
});
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, []);
|
||||
});
|
||||
|
||||
test('can do bulk updates 1', () => {
|
||||
opts.assign({
|
||||
tabSize: 'auto',
|
||||
insertSpaces: true
|
||||
});
|
||||
assertState(opts, {
|
||||
tabSize: 4,
|
||||
indentSize: 4,
|
||||
insertSpaces: true,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.On
|
||||
});
|
||||
assert.deepEqual(calls, [{ tabSize: 'auto', insertSpaces: true }]);
|
||||
});
|
||||
|
||||
test('can do bulk updates 2', () => {
|
||||
opts.assign({
|
||||
tabSize: 3,
|
||||
insertSpaces: 'auto'
|
||||
});
|
||||
assertState(opts, {
|
||||
tabSize: 3,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Line,
|
||||
lineNumbers: RenderLineNumbersType.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,
|
||||
indentSize: 4,
|
||||
insertSpaces: false,
|
||||
cursorStyle: TextEditorCursorStyle.Block,
|
||||
lineNumbers: RenderLineNumbersType.Relative
|
||||
});
|
||||
assert.deepEqual(calls, [{ cursorStyle: TextEditorCursorStyle.Block, lineNumbers: RenderLineNumbersType.Relative }]);
|
||||
});
|
||||
|
||||
});
|
||||
62
src/vs/workbench/test/browser/api/extHostTextEditors.test.ts
Normal file
62
src/vs/workbench/test/browser/api/extHostTextEditors.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as assert from 'assert';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { MainContext, MainThreadTextEditorsShape, IWorkspaceEditDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol';
|
||||
import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors';
|
||||
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('ExtHostTextEditors.applyWorkspaceEdit', () => {
|
||||
|
||||
const resource = URI.parse('foo:bar');
|
||||
let editors: ExtHostEditors;
|
||||
let workspaceResourceEdits: IWorkspaceEditDto;
|
||||
|
||||
setup(() => {
|
||||
workspaceResourceEdits = null!;
|
||||
|
||||
let rpcProtocol = new TestRPCProtocol();
|
||||
rpcProtocol.set(MainContext.MainThreadTextEditors, new class extends mock<MainThreadTextEditorsShape>() {
|
||||
$tryApplyWorkspaceEdit(_workspaceResourceEdits: IWorkspaceEditDto): Promise<boolean> {
|
||||
workspaceResourceEdits = _workspaceResourceEdits;
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
});
|
||||
const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null), new NullLogService());
|
||||
documentsAndEditors.$acceptDocumentsAndEditorsDelta({
|
||||
addedDocuments: [{
|
||||
isDirty: false,
|
||||
modeId: 'foo',
|
||||
uri: resource,
|
||||
versionId: 1337,
|
||||
lines: ['foo'],
|
||||
EOL: '\n',
|
||||
}]
|
||||
});
|
||||
editors = new ExtHostEditors(rpcProtocol, documentsAndEditors);
|
||||
});
|
||||
|
||||
test('uses version id if document available', async () => {
|
||||
let edit = new extHostTypes.WorkspaceEdit();
|
||||
edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello');
|
||||
await editors.applyWorkspaceEdit(edit);
|
||||
assert.equal(workspaceResourceEdits.edits.length, 1);
|
||||
assert.equal((<WorkspaceTextEdit>workspaceResourceEdits.edits[0]).modelVersionId, 1337);
|
||||
});
|
||||
|
||||
test('does not use version id if document is not available', async () => {
|
||||
let edit = new extHostTypes.WorkspaceEdit();
|
||||
edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello');
|
||||
await editors.applyWorkspaceEdit(edit);
|
||||
assert.equal(workspaceResourceEdits.edits.length, 1);
|
||||
assert.ok(typeof (<WorkspaceTextEdit>workspaceResourceEdits.edits[0]).modelVersionId === 'undefined');
|
||||
});
|
||||
|
||||
});
|
||||
741
src/vs/workbench/test/browser/api/extHostTreeViews.test.ts
Normal file
741
src/vs/workbench/test/browser/api/extHostTreeViews.test.ts
Normal file
@@ -0,0 +1,741 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import * as sinon from 'sinon';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTreeViews } from 'vs/workbench/api/common/extHostTreeViews';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainThreadTreeViewsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TreeDataProvider, TreeItem } from 'vscode';
|
||||
import { TestRPCProtocol } from './testRPCProtocol';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { TreeItemCollapsibleState, ITreeItem } from 'vs/workbench/common/views';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import type { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
suite('ExtHostTreeView', function () {
|
||||
|
||||
class RecordingShape extends mock<MainThreadTreeViewsShape>() {
|
||||
|
||||
onRefresh = new Emitter<{ [treeItemHandle: string]: ITreeItem }>();
|
||||
|
||||
$registerTreeViewDataProvider(treeViewId: string): void {
|
||||
}
|
||||
|
||||
$refresh(viewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): Promise<void> {
|
||||
return Promise.resolve(null).then(() => {
|
||||
this.onRefresh.fire(itemsToRefresh);
|
||||
});
|
||||
}
|
||||
|
||||
$reveal(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let testObject: ExtHostTreeViews;
|
||||
let target: RecordingShape;
|
||||
let onDidChangeTreeNode: Emitter<{ key: string } | undefined>;
|
||||
let onDidChangeTreeNodeWithId: Emitter<{ key: string }>;
|
||||
let tree: { [key: string]: any };
|
||||
let labels: { [key: string]: string };
|
||||
let nodes: { [key: string]: { key: string } };
|
||||
|
||||
setup(() => {
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ab': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bb': {}
|
||||
}
|
||||
};
|
||||
|
||||
labels = {};
|
||||
nodes = {};
|
||||
|
||||
let rpcProtocol = new TestRPCProtocol();
|
||||
// Use IInstantiationService to get typechecking when instantiating
|
||||
let inst: IInstantiationService;
|
||||
{
|
||||
let instantiationService = new TestInstantiationService();
|
||||
inst = instantiationService;
|
||||
}
|
||||
|
||||
rpcProtocol.set(MainContext.MainThreadCommands, inst.createInstance(MainThreadCommands, rpcProtocol));
|
||||
target = new RecordingShape();
|
||||
testObject = new ExtHostTreeViews(target, new ExtHostCommands(
|
||||
rpcProtocol,
|
||||
new NullLogService()
|
||||
), new NullLogService());
|
||||
onDidChangeTreeNode = new Emitter<{ key: string } | undefined>();
|
||||
onDidChangeTreeNodeWithId = new Emitter<{ key: string }>();
|
||||
testObject.createTreeView('testNodeTreeProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
testObject.createTreeView('testNodeWithIdTreeProvider', { treeDataProvider: aNodeWithIdTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
testObject.createTreeView('testNodeWithHighlightsTreeProvider', { treeDataProvider: aNodeWithHighlightedLabelTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
|
||||
return loadCompleteTree('testNodeTreeProvider');
|
||||
});
|
||||
|
||||
test('construct node tree', () => {
|
||||
return testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:a', '0/0:b']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:a')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:a/0:aa', '0/0:a/0:ab']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:aa').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:a/0:ab').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
}),
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:b')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:b/0:ba', '0/0:b/0:bb']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:ba').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeTreeProvider', '0/0:b/0:bb').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('construct id tree', () => {
|
||||
return testObject.$getChildren('testNodeWithIdTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/a', '1/b']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/a')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/aa', '1/ab']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/aa').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/ab').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
}),
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/b')
|
||||
.then(children => {
|
||||
const actuals = children.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/ba', '1/bb']);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/ba').then(children => assert.equal(children.length, 0)),
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider', '1/bb').then(children => assert.equal(children.length, 0))
|
||||
]);
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('construct highlights tree', () => {
|
||||
return testObject.$getChildren('testNodeWithHighlightsTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(removeUnsetKeys(elements), [{
|
||||
handle: '1/a',
|
||||
label: { label: 'a', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
}, {
|
||||
handle: '1/b',
|
||||
label: { label: 'b', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
}]);
|
||||
return Promise.all([
|
||||
testObject.$getChildren('testNodeWithHighlightsTreeProvider', '1/a')
|
||||
.then(children => {
|
||||
assert.deepEqual(removeUnsetKeys(children), [{
|
||||
handle: '1/aa',
|
||||
parentHandle: '1/a',
|
||||
label: { label: 'aa', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}, {
|
||||
handle: '1/ab',
|
||||
parentHandle: '1/a',
|
||||
label: { label: 'ab', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}]);
|
||||
}),
|
||||
testObject.$getChildren('testNodeWithHighlightsTreeProvider', '1/b')
|
||||
.then(children => {
|
||||
assert.deepEqual(removeUnsetKeys(children), [{
|
||||
handle: '1/ba',
|
||||
parentHandle: '1/b',
|
||||
label: { label: 'ba', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}, {
|
||||
handle: '1/bb',
|
||||
parentHandle: '1/b',
|
||||
label: { label: 'bb', highlights: [[0, 2], [3, 5]] },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
}]);
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('error is thrown if id is not unique', (done) => {
|
||||
tree['a'] = {
|
||||
'aa': {},
|
||||
};
|
||||
tree['b'] = {
|
||||
'aa': {},
|
||||
'ba': {}
|
||||
};
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeWithIdTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['1/a', '1/b']);
|
||||
return testObject.$getChildren('testNodeWithIdTreeProvider', '1/a')
|
||||
.then(() => testObject.$getChildren('testNodeWithIdTreeProvider', '1/b'))
|
||||
.then(() => { assert.fail('Should fail with duplicate id'); done(); }, () => done());
|
||||
});
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('refresh root', function (done) {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
done();
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('refresh a parent node', () => {
|
||||
return new Promise((c, e) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:b'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
|
||||
handle: '0/0:b',
|
||||
label: { label: 'b' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
c(undefined);
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh a leaf node', function (done) {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:b/0:bb'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b/0:bb']), {
|
||||
handle: '0/0:b/0:bb',
|
||||
parentHandle: '0/0:b',
|
||||
label: { label: 'bb' },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
});
|
||||
done();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('bb'));
|
||||
});
|
||||
|
||||
async function runWithEventMerging(action: (resolve: () => void) => void) {
|
||||
await new Promise((resolve) => {
|
||||
let subscription: IDisposable | undefined = undefined;
|
||||
subscription = target.onRefresh.event(() => {
|
||||
subscription!.dispose();
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
});
|
||||
await new Promise(action);
|
||||
}
|
||||
|
||||
test('refresh parent and child node trigger refresh only on parent - scenario 1', async () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:b', '0/0:a/0:aa'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
|
||||
handle: '0/0:b',
|
||||
label: { label: 'b' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), {
|
||||
handle: '0/0:a/0:aa',
|
||||
parentHandle: '0/0:a',
|
||||
label: { label: 'aa' },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('aa'));
|
||||
onDidChangeTreeNode.fire(getNode('bb'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh parent and child node trigger refresh only on parent - scenario 2', async () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a/0:aa', '0/0:b'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:b']), {
|
||||
handle: '0/0:b',
|
||||
label: { label: 'b' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:a/0:aa']), {
|
||||
handle: '0/0:a/0:aa',
|
||||
parentHandle: '0/0:a',
|
||||
label: { label: 'aa' },
|
||||
collapsibleState: TreeItemCollapsibleState.None
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('bb'));
|
||||
onDidChangeTreeNode.fire(getNode('aa'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh an element for label change', function (done) {
|
||||
labels['a'] = 'aa';
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a'], Object.keys(actuals));
|
||||
assert.deepEqual(removeUnsetKeys(actuals['0/0:a']), {
|
||||
handle: '0/0:aa',
|
||||
label: { label: 'aa' },
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed
|
||||
});
|
||||
done();
|
||||
});
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on roots', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
resolve();
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on elements', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on unknown elements', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('g'));
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on unknown elements and root', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(getNode('g'));
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
test('refresh calls are throttled on elements and root', () => {
|
||||
return runWithEventMerging((resolve) => {
|
||||
target.onRefresh.event(actuals => {
|
||||
assert.equal(undefined, actuals);
|
||||
resolve();
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
});
|
||||
});
|
||||
|
||||
test('generate unique handles from labels by escaping them', (done) => {
|
||||
tree = {
|
||||
'a/0:b': {}
|
||||
};
|
||||
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(elements.map(e => e.handle), ['0/0:a//0:b']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('tree with duplicate labels', (done) => {
|
||||
|
||||
const dupItems = {
|
||||
'adup1': 'c',
|
||||
'adup2': 'g',
|
||||
'bdup1': 'e',
|
||||
'hdup1': 'i',
|
||||
'hdup2': 'l',
|
||||
'jdup1': 'k'
|
||||
};
|
||||
|
||||
labels['c'] = 'a';
|
||||
labels['e'] = 'b';
|
||||
labels['g'] = 'a';
|
||||
labels['i'] = 'h';
|
||||
labels['l'] = 'h';
|
||||
labels['k'] = 'j';
|
||||
|
||||
tree[dupItems['adup1']] = {};
|
||||
tree['d'] = {};
|
||||
|
||||
const bdup1Tree: { [key: string]: any } = {};
|
||||
bdup1Tree['h'] = {};
|
||||
bdup1Tree[dupItems['hdup1']] = {};
|
||||
bdup1Tree['j'] = {};
|
||||
bdup1Tree[dupItems['jdup1']] = {};
|
||||
bdup1Tree[dupItems['hdup2']] = {};
|
||||
|
||||
tree[dupItems['bdup1']] = bdup1Tree;
|
||||
tree['f'] = {};
|
||||
tree[dupItems['adup2']] = {};
|
||||
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/0:a', '0/0:b', '0/1:a', '0/0:d', '0/1:b', '0/0:f', '0/2:a']);
|
||||
return testObject.$getChildren('testNodeTreeProvider', '0/1:b')
|
||||
.then(elements => {
|
||||
const actuals = elements.map(e => e.handle);
|
||||
assert.deepEqual(actuals, ['0/1:b/0:h', '0/1:b/1:h', '0/1:b/0:j', '0/1:b/1:j', '0/1:b/2:h']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('getChildren is not returned from cache if refreshed', (done) => {
|
||||
tree = {
|
||||
'c': {}
|
||||
};
|
||||
|
||||
target.onRefresh.event(() => {
|
||||
testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(elements.map(e => e.handle), ['0/0:c']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
onDidChangeTreeNode.fire(undefined);
|
||||
});
|
||||
|
||||
test('getChildren is returned from cache if not refreshed', () => {
|
||||
tree = {
|
||||
'c': {}
|
||||
};
|
||||
|
||||
return testObject.$getChildren('testNodeTreeProvider')
|
||||
.then(elements => {
|
||||
assert.deepEqual(elements.map(e => e.handle), ['0/0:a', '0/0:b']);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal will throw an error if getParent is not implemented', () => {
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'a' })
|
||||
.then(() => assert.fail('Reveal should throw an error as getParent is not implemented'), () => null);
|
||||
});
|
||||
|
||||
test('reveal will return empty array for root element', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'a' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([], revealTarget.args[0][2]);
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal will return parents array for an element when hierarchy is not loaded', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'aa' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal will return parents array for an element when hierarchy is loaded', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return testObject.$getChildren('treeDataProvider')
|
||||
.then(() => testObject.$getChildren('treeDataProvider', '0/0:a'))
|
||||
.then(() => treeView.reveal({ key: 'aa' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a/0:aa', label: { label: 'aa' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
}));
|
||||
});
|
||||
|
||||
test('reveal will return parents array for deeper element with no selection', () => {
|
||||
tree = {
|
||||
'b': {
|
||||
'ba': {
|
||||
'bac': {}
|
||||
}
|
||||
}
|
||||
};
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return treeView.reveal({ key: 'bac' }, { select: false, focus: false, expand: false })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:b/0:ba/0:bac', label: { label: 'bac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b/0:ba' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([
|
||||
{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed },
|
||||
{ handle: '0/0:b/0:ba', label: { label: 'ba' }, collapsibleState: TreeItemCollapsibleState.Collapsed, parentHandle: '0/0:b' }
|
||||
], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: false, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal after first udpate', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return loadCompleteTree('treeDataProvider')
|
||||
.then(() => {
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ac': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bb': {}
|
||||
}
|
||||
};
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
|
||||
return treeView.reveal({ key: 'ac' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:a/0:ac', label: { label: 'ac' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:a' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:a', label: { label: 'a' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('reveal after second udpate', () => {
|
||||
const revealTarget = sinon.spy(target, '$reveal');
|
||||
const treeView = testObject.createTreeView('treeDataProvider', { treeDataProvider: aCompleteNodeTreeDataProvider() }, { enableProposedApi: true } as IExtensionDescription);
|
||||
return loadCompleteTree('treeDataProvider')
|
||||
.then(() => {
|
||||
runWithEventMerging((resolve) => {
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ac': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bb': {}
|
||||
}
|
||||
};
|
||||
onDidChangeTreeNode.fire(getNode('a'));
|
||||
tree = {
|
||||
'a': {
|
||||
'aa': {},
|
||||
'ac': {}
|
||||
},
|
||||
'b': {
|
||||
'ba': {},
|
||||
'bc': {}
|
||||
}
|
||||
};
|
||||
onDidChangeTreeNode.fire(getNode('b'));
|
||||
resolve();
|
||||
}).then(() => {
|
||||
return treeView.reveal({ key: 'bc' })
|
||||
.then(() => {
|
||||
assert.ok(revealTarget.calledOnce);
|
||||
assert.deepEqual('treeDataProvider', revealTarget.args[0][0]);
|
||||
assert.deepEqual({ handle: '0/0:b/0:bc', label: { label: 'bc' }, collapsibleState: TreeItemCollapsibleState.None, parentHandle: '0/0:b' }, removeUnsetKeys(revealTarget.args[0][1]));
|
||||
assert.deepEqual([{ handle: '0/0:b', label: { label: 'b' }, collapsibleState: TreeItemCollapsibleState.Collapsed }], (<Array<any>>revealTarget.args[0][2]).map(arg => removeUnsetKeys(arg)));
|
||||
assert.deepEqual({ select: true, focus: false, expand: false }, revealTarget.args[0][3]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function loadCompleteTree(treeId: string, element?: string): Promise<null> {
|
||||
return testObject.$getChildren(treeId, element)
|
||||
.then(elements => elements.map(e => loadCompleteTree(treeId, e.handle)))
|
||||
.then(() => null);
|
||||
}
|
||||
|
||||
function removeUnsetKeys(obj: any): any {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(o => removeUnsetKeys(o));
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
const result: { [key: string]: any } = {};
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (obj[key] !== undefined) {
|
||||
result[key] = removeUnsetKeys(obj[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
function aNodeTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
return getTreeItem(element.key);
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNode.event
|
||||
};
|
||||
}
|
||||
|
||||
function aCompleteNodeTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
return getTreeItem(element.key);
|
||||
},
|
||||
getParent: ({ key }: { key: string }): { key: string } | undefined => {
|
||||
const parentKey = key.substring(0, key.length - 1);
|
||||
return parentKey ? new Key(parentKey) : undefined;
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNode.event
|
||||
};
|
||||
}
|
||||
|
||||
function aNodeWithIdTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
const treeItem = getTreeItem(element.key);
|
||||
treeItem.id = element.key;
|
||||
return treeItem;
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNodeWithId.event
|
||||
};
|
||||
}
|
||||
|
||||
function aNodeWithHighlightedLabelTreeDataProvider(): TreeDataProvider<{ key: string }> {
|
||||
return {
|
||||
getChildren: (element: { key: string }): { key: string }[] => {
|
||||
return getChildren(element ? element.key : undefined).map(key => getNode(key));
|
||||
},
|
||||
getTreeItem: (element: { key: string }): TreeItem => {
|
||||
const treeItem = getTreeItem(element.key, [[0, 2], [3, 5]]);
|
||||
treeItem.id = element.key;
|
||||
return treeItem;
|
||||
},
|
||||
onDidChangeTreeData: onDidChangeTreeNodeWithId.event
|
||||
};
|
||||
}
|
||||
|
||||
function getTreeElement(element: string): any {
|
||||
let parent = tree;
|
||||
for (let i = 0; i < element.length; i++) {
|
||||
parent = parent[element.substring(0, i + 1)];
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function getChildren(key: string | undefined): string[] {
|
||||
if (!key) {
|
||||
return Object.keys(tree);
|
||||
}
|
||||
let treeElement = getTreeElement(key);
|
||||
if (treeElement) {
|
||||
return Object.keys(treeElement);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getTreeItem(key: string, highlights?: [number, number][]): TreeItem {
|
||||
const treeElement = getTreeElement(key);
|
||||
return {
|
||||
label: <any>{ label: labels[key] || key, highlights },
|
||||
collapsibleState: treeElement && Object.keys(treeElement).length ? TreeItemCollapsibleState.Collapsed : TreeItemCollapsibleState.None
|
||||
};
|
||||
}
|
||||
|
||||
function getNode(key: string): { key: string } {
|
||||
if (!nodes[key]) {
|
||||
nodes[key] = new Key(key);
|
||||
}
|
||||
return nodes[key];
|
||||
}
|
||||
|
||||
class Key {
|
||||
constructor(readonly key: string) { }
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { MarkdownString, LogLevel } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { size, forEach } from 'vs/base/common/collections';
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
suite('ExtHostTypeConverter', function () {
|
||||
|
||||
test('MarkdownConvert - uris', function () {
|
||||
|
||||
let data = MarkdownString.from('Hello');
|
||||
assert.equal(isEmptyObject(data.uris), true);
|
||||
assert.equal(data.value, 'Hello');
|
||||
|
||||
data = MarkdownString.from('Hello [link](foo)');
|
||||
assert.equal(data.value, 'Hello [link](foo)');
|
||||
assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri
|
||||
|
||||
data = MarkdownString.from('Hello [link](www.noscheme.bad)');
|
||||
assert.equal(data.value, 'Hello [link](www.noscheme.bad)');
|
||||
assert.equal(isEmptyObject(data.uris), true); // no scheme, no uri
|
||||
|
||||
data = MarkdownString.from('Hello [link](foo:path)');
|
||||
assert.equal(data.value, 'Hello [link](foo:path)');
|
||||
assert.equal(size(data.uris!), 1);
|
||||
assert.ok(!!data.uris!['foo:path']);
|
||||
|
||||
data = MarkdownString.from('hello@foo.bar');
|
||||
assert.equal(data.value, 'hello@foo.bar');
|
||||
assert.equal(size(data.uris!), 1);
|
||||
assert.ok(!!data.uris!['mailto:hello@foo.bar']);
|
||||
|
||||
data = MarkdownString.from('*hello* [click](command:me)');
|
||||
assert.equal(data.value, '*hello* [click](command:me)');
|
||||
assert.equal(size(data.uris!), 1);
|
||||
assert.ok(!!data.uris!['command:me']);
|
||||
|
||||
data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
|
||||
assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
|
||||
assert.equal(size(data.uris!), 1);
|
||||
assert.ok(!!data.uris!['file:///somepath/here']);
|
||||
|
||||
data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
|
||||
assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here)');
|
||||
assert.equal(size(data.uris!), 1);
|
||||
assert.ok(!!data.uris!['file:///somepath/here']);
|
||||
|
||||
data = MarkdownString.from('*hello* [click](file:///somepath/here). [click](file:///somepath/here2)');
|
||||
assert.equal(data.value, '*hello* [click](file:///somepath/here). [click](file:///somepath/here2)');
|
||||
assert.equal(size(data.uris!), 2);
|
||||
assert.ok(!!data.uris!['file:///somepath/here']);
|
||||
assert.ok(!!data.uris!['file:///somepath/here2']);
|
||||
});
|
||||
|
||||
test('NPM script explorer running a script from the hover does not work #65561', function () {
|
||||
|
||||
let data = MarkdownString.from('*hello* [click](command:npm.runScriptFromHover?%7B%22documentUri%22%3A%7B%22%24mid%22%3A1%2C%22external%22%3A%22file%3A%2F%2F%2Fc%253A%2Ffoo%2Fbaz.ex%22%2C%22path%22%3A%22%2Fc%3A%2Ffoo%2Fbaz.ex%22%2C%22scheme%22%3A%22file%22%7D%2C%22script%22%3A%22dev%22%7D)');
|
||||
// assert that both uri get extracted but that the latter is only decoded once...
|
||||
assert.equal(size(data.uris!), 2);
|
||||
forEach(data.uris!, entry => {
|
||||
if (entry.value.scheme === 'file') {
|
||||
assert.ok(URI.revive(entry.value).toString().indexOf('file:///c%3A') === 0);
|
||||
} else {
|
||||
assert.equal(entry.value.scheme, 'command');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('LogLevel', () => {
|
||||
assert.equal(LogLevel.from(types.LogLevel.Error), _MainLogLevel.Error);
|
||||
assert.equal(LogLevel.from(types.LogLevel.Info), _MainLogLevel.Info);
|
||||
assert.equal(LogLevel.from(types.LogLevel.Off), _MainLogLevel.Off);
|
||||
|
||||
assert.equal(LogLevel.to(_MainLogLevel.Error), types.LogLevel.Error);
|
||||
assert.equal(LogLevel.to(_MainLogLevel.Info), types.LogLevel.Info);
|
||||
assert.equal(LogLevel.to(_MainLogLevel.Off), types.LogLevel.Off);
|
||||
});
|
||||
});
|
||||
569
src/vs/workbench/test/browser/api/extHostTypes.test.ts
Normal file
569
src/vs/workbench/test/browser/api/extHostTypes.test.ts
Normal file
@@ -0,0 +1,569 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { assertType } from 'vs/base/common/types';
|
||||
|
||||
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');
|
||||
assert.deepEqual(uri.toJSON(), {
|
||||
$mid: 1,
|
||||
scheme: 'file',
|
||||
path: '/path/test.file'
|
||||
});
|
||||
|
||||
assert.ok(uri.fsPath);
|
||||
assert.deepEqual(uri.toJSON(), {
|
||||
$mid: 1,
|
||||
scheme: 'file',
|
||||
path: '/path/test.file',
|
||||
fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'),
|
||||
_sep: isWindows ? 1 : undefined,
|
||||
});
|
||||
|
||||
assert.ok(uri.toString());
|
||||
assert.deepEqual(uri.toJSON(), {
|
||||
$mid: 1,
|
||||
scheme: 'file',
|
||||
path: '/path/test.file',
|
||||
fsPath: '/path/test.file'.replace(/\//g, isWindows ? '\\' : '/'),
|
||||
_sep: isWindows ? 1 : undefined,
|
||||
external: 'file:///path/test.file'
|
||||
});
|
||||
});
|
||||
|
||||
test('Disposable', () => {
|
||||
|
||||
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', () => {
|
||||
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', () => {
|
||||
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', () => {
|
||||
|
||||
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', () => {
|
||||
|
||||
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, [[a.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, [
|
||||
[a.toJSON(), [{ range: [{ line: 0, character: 0 }, { line: 0, character: 0 }], newText: 'fff' }]],
|
||||
[b.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, 1);
|
||||
|
||||
edit.set(b, [types.TextEdit.insert(new types.Position(0, 0), 'ffff')]);
|
||||
assert.equal(edit.get(b).length, 1);
|
||||
});
|
||||
|
||||
test('WorkspaceEdit - keep order of text and file changes', function () {
|
||||
|
||||
const edit = new types.WorkspaceEdit();
|
||||
edit.replace(URI.parse('foo:a'), new types.Range(1, 1, 1, 1), 'foo');
|
||||
edit.renameFile(URI.parse('foo:a'), URI.parse('foo:b'));
|
||||
edit.replace(URI.parse('foo:a'), new types.Range(2, 1, 2, 1), 'bar');
|
||||
edit.replace(URI.parse('foo:b'), new types.Range(3, 1, 3, 1), 'bazz');
|
||||
|
||||
const all = edit.allEntries();
|
||||
assert.equal(all.length, 4);
|
||||
|
||||
const [first, second, third, fourth] = all;
|
||||
assertType(first._type === 2);
|
||||
assert.equal(first.uri.toString(), 'foo:a');
|
||||
|
||||
assertType(second._type === 1);
|
||||
assert.equal(second.from!.toString(), 'foo:a');
|
||||
assert.equal(second.to!.toString(), 'foo:b');
|
||||
|
||||
assertType(third._type === 2);
|
||||
assert.equal(third.uri.toString(), 'foo:a');
|
||||
|
||||
assertType(fourth._type === 2);
|
||||
assert.equal(fourth.uri.toString(), 'foo:b');
|
||||
});
|
||||
|
||||
test('WorkspaceEdit - two edits for one resource', function () {
|
||||
let edit = new types.WorkspaceEdit();
|
||||
let uri = URI.parse('foo:bar');
|
||||
edit.insert(uri, new types.Position(0, 0), 'Hello');
|
||||
edit.insert(uri, new types.Position(0, 0), 'Foo');
|
||||
|
||||
assert.equal(edit.allEntries().length, 2);
|
||||
let [first, second] = edit.allEntries();
|
||||
|
||||
assertType(first._type === 2);
|
||||
assertType(second._type === 2);
|
||||
assert.equal(first.edit.newText, 'Hello');
|
||||
assert.equal(second.edit.newText, 'Foo');
|
||||
});
|
||||
|
||||
test('DocumentLink', () => {
|
||||
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}');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendChoice(['b', 'a', 'r']);
|
||||
assert.equal(string.value, '${1|b,a,r|}');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendChoice(['b', 'a', 'r'], 0);
|
||||
assert.equal(string.value, '${0|b,a,r|}');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendText('foo').appendChoice(['far', 'boo']).appendText('bar');
|
||||
assert.equal(string.value, 'foo${1|far,boo|}bar');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendText('foo').appendChoice(['far', '$boo']).appendText('bar');
|
||||
assert.equal(string.value, 'foo${1|far,\\$boo|}bar');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendText('foo').appendPlaceholder('farboo').appendChoice(['far', 'boo']).appendText('bar');
|
||||
assert.equal(string.value, 'foo${1:farboo}${2|far,boo|}bar');
|
||||
});
|
||||
|
||||
test('instanceof doesn\'t work for FileSystemError #49386', function () {
|
||||
const error = types.FileSystemError.Unavailable('foo');
|
||||
assert.ok(error instanceof Error);
|
||||
assert.ok(error instanceof types.FileSystemError);
|
||||
});
|
||||
|
||||
test('CodeActionKind contains', () => {
|
||||
assert.ok(types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.RefactorExtract));
|
||||
assert.ok(types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.RefactorExtract.append('other')));
|
||||
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Refactor));
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Refactor.append('other')));
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Empty.append('other').append('refactor')));
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Empty.append('refactory')));
|
||||
});
|
||||
|
||||
test('CodeActionKind intersects', () => {
|
||||
assert.ok(types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.RefactorExtract));
|
||||
assert.ok(types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Refactor));
|
||||
assert.ok(types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.RefactorExtract.append('other')));
|
||||
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Refactor.append('other')));
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Empty.append('other').append('refactor')));
|
||||
assert.ok(!types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Empty.append('refactory')));
|
||||
});
|
||||
});
|
||||
153
src/vs/workbench/test/browser/api/extHostWebview.test.ts
Normal file
153
src/vs/workbench/test/browser/api/extHostWebview.test.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { MainThreadWebviews } from 'vs/workbench/api/browser/mainThreadWebview';
|
||||
import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
|
||||
suite('ExtHostWebview', () => {
|
||||
|
||||
test('Cannot register multiple serializers for the same view type', async () => {
|
||||
const viewType = 'view.type';
|
||||
|
||||
const shape = createNoopMainThreadWebviews();
|
||||
const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), {
|
||||
webviewCspSource: '',
|
||||
webviewResourceRoot: '',
|
||||
isExtensionDevelopmentDebug: false,
|
||||
}, undefined, new NullLogService());
|
||||
|
||||
let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined;
|
||||
|
||||
class NoopSerializer implements vscode.WebviewPanelSerializer {
|
||||
async deserializeWebviewPanel(_webview: vscode.WebviewPanel, _state: any): Promise<void> {
|
||||
lastInvokedDeserializer = this;
|
||||
}
|
||||
}
|
||||
|
||||
const extension = {} as IExtensionDescription;
|
||||
|
||||
const serializerA = new NoopSerializer();
|
||||
const serializerB = new NoopSerializer();
|
||||
|
||||
const serializerARegistration = extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerA);
|
||||
|
||||
await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {});
|
||||
assert.strictEqual(lastInvokedDeserializer, serializerA);
|
||||
|
||||
assert.throws(
|
||||
() => extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB),
|
||||
'Should throw when registering two serializers for the same view');
|
||||
|
||||
serializerARegistration.dispose();
|
||||
|
||||
extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializerB);
|
||||
|
||||
await extHostWebviews.$deserializeWebviewPanel('x', viewType, 'title', {}, 0 as EditorViewColumn, {});
|
||||
assert.strictEqual(lastInvokedDeserializer, serializerB);
|
||||
});
|
||||
|
||||
test('asWebviewUri for desktop vscode-resource scheme', () => {
|
||||
const shape = createNoopMainThreadWebviews();
|
||||
const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), {
|
||||
webviewCspSource: '',
|
||||
webviewResourceRoot: 'vscode-resource://{{resource}}',
|
||||
isExtensionDevelopmentDebug: false,
|
||||
}, undefined, new NullLogService());
|
||||
const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {});
|
||||
|
||||
assert.strictEqual(
|
||||
webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString(),
|
||||
'vscode-resource://file///Users/codey/file.html',
|
||||
'Unix basic'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString(),
|
||||
'vscode-resource://file///Users/codey/file.html#frag',
|
||||
'Unix should preserve fragment'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString(),
|
||||
'vscode-resource://file///Users/codey/f%20ile.html',
|
||||
'Unix with encoding'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString(),
|
||||
'vscode-resource://file//localhost/Users/codey/file.html',
|
||||
'Unix should preserve authority'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString(),
|
||||
'vscode-resource://file///c%3A/codey/file.txt',
|
||||
'Windows C drive'
|
||||
);
|
||||
});
|
||||
|
||||
test('asWebviewUri for web endpoint', () => {
|
||||
const shape = createNoopMainThreadWebviews();
|
||||
|
||||
const extHostWebviews = new ExtHostWebviews(SingleProxyRPCProtocol(shape), {
|
||||
webviewCspSource: '',
|
||||
webviewResourceRoot: `https://{{uuid}}.webview.contoso.com/commit/{{resource}}`,
|
||||
isExtensionDevelopmentDebug: false,
|
||||
}, undefined, new NullLogService());
|
||||
const webview = extHostWebviews.createWebviewPanel({} as any, 'type', 'title', 1, {});
|
||||
|
||||
function stripEndpointUuid(input: string) {
|
||||
return input.replace(/^https:\/\/[^\.]+?\./, '');
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html')).toString()),
|
||||
'webview.contoso.com/commit/file///Users/codey/file.html',
|
||||
'Unix basic'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/file.html#frag')).toString()),
|
||||
'webview.contoso.com/commit/file///Users/codey/file.html#frag',
|
||||
'Unix should preserve fragment'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///Users/codey/f%20ile.html')).toString()),
|
||||
'webview.contoso.com/commit/file///Users/codey/f%20ile.html',
|
||||
'Unix with encoding'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file://localhost/Users/codey/file.html')).toString()),
|
||||
'webview.contoso.com/commit/file//localhost/Users/codey/file.html',
|
||||
'Unix should preserve authority'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
stripEndpointUuid(webview.webview.asWebviewUri(URI.parse('file:///c:/codey/file.txt')).toString()),
|
||||
'webview.contoso.com/commit/file///c%3A/codey/file.txt',
|
||||
'Windows C drive'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function createNoopMainThreadWebviews() {
|
||||
return new class extends mock<MainThreadWebviews>() {
|
||||
$createWebviewPanel() { /* noop */ }
|
||||
$registerSerializer() { /* noop */ }
|
||||
$unregisterSerializer() { /* noop */ }
|
||||
};
|
||||
}
|
||||
|
||||
781
src/vs/workbench/test/browser/api/extHostWorkspace.test.ts
Normal file
781
src/vs/workbench/test/browser/api/extHostWorkspace.test.ts
Normal file
@@ -0,0 +1,781 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkspaceFolderData } from 'vs/platform/workspace/common/workspace';
|
||||
import { MainThreadWorkspace } from 'vs/workbench/api/browser/mainThreadWorkspace';
|
||||
import { IMainContext, IWorkspaceData, MainContext, ITextSearchComplete } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { RelativePattern } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { TestRPCProtocol } from './testRPCProtocol';
|
||||
import { ExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
|
||||
import { IPatternInfo } from 'vs/workbench/services/search/common/search';
|
||||
|
||||
function createExtHostWorkspace(mainContext: IMainContext, data: IWorkspaceData, logService: ILogService): ExtHostWorkspace {
|
||||
const result = new ExtHostWorkspace(
|
||||
new ExtHostRpcService(mainContext),
|
||||
new class extends mock<IExtHostInitDataService>() { workspace = data; },
|
||||
logService
|
||||
);
|
||||
result.$initializeWorkspace(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
suite('ExtHostWorkspace', function () {
|
||||
|
||||
const extensionDescriptor: IExtensionDescription = {
|
||||
identifier: new ExtensionIdentifier('nullExtensionDescription'),
|
||||
name: 'ext',
|
||||
publisher: 'vscode',
|
||||
enableProposedApi: false,
|
||||
engines: undefined!,
|
||||
extensionLocation: undefined!,
|
||||
isBuiltin: false,
|
||||
isUnderDevelopment: false,
|
||||
version: undefined!
|
||||
};
|
||||
|
||||
function assertAsRelativePath(workspace: ExtHostWorkspace, input: string, expected: string, includeWorkspace?: boolean) {
|
||||
const actual = workspace.getRelativePath(input, includeWorkspace);
|
||||
assert.equal(actual, expected);
|
||||
}
|
||||
|
||||
test('asRelativePath', () => {
|
||||
|
||||
const ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/Applications/NewsWoWBot'), 0)], name: 'Test' }, new NullLogService());
|
||||
|
||||
assertAsRelativePath(ws, '/Coding/Applications/NewsWoWBot/bernd/das/brot', 'bernd/das/brot');
|
||||
assertAsRelativePath(ws, '/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart',
|
||||
'/Apps/DartPubCache/hosted/pub.dartlang.org/convert-2.0.1/lib/src/hex.dart');
|
||||
|
||||
assertAsRelativePath(ws, '', '');
|
||||
assertAsRelativePath(ws, '/foo/bar', '/foo/bar');
|
||||
assertAsRelativePath(ws, 'in/out', 'in/out');
|
||||
});
|
||||
|
||||
test('asRelativePath, same paths, #11402', function () {
|
||||
const root = '/home/aeschli/workspaces/samples/docker';
|
||||
const input = '/home/aeschli/workspaces/samples/docker';
|
||||
const ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
|
||||
assertAsRelativePath(ws, input, input);
|
||||
|
||||
const input2 = '/home/aeschli/workspaces/samples/docker/a.file';
|
||||
assertAsRelativePath(ws, input2, 'a.file');
|
||||
});
|
||||
|
||||
test('asRelativePath, no workspace', function () {
|
||||
const ws = createExtHostWorkspace(new TestRPCProtocol(), null!, new NullLogService());
|
||||
assertAsRelativePath(ws, '', '');
|
||||
assertAsRelativePath(ws, '/foo/bar', '/foo/bar');
|
||||
});
|
||||
|
||||
test('asRelativePath, multiple folders', function () {
|
||||
const ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService());
|
||||
assertAsRelativePath(ws, '/Coding/One/file.txt', 'One/file.txt');
|
||||
assertAsRelativePath(ws, '/Coding/Two/files/out.txt', 'Two/files/out.txt');
|
||||
assertAsRelativePath(ws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt');
|
||||
});
|
||||
|
||||
test('slightly inconsistent behaviour of asRelativePath and getWorkspaceFolder, #31553', function () {
|
||||
const mrws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService());
|
||||
|
||||
assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt');
|
||||
assertAsRelativePath(mrws, '/Coding/One/file.txt', 'One/file.txt', true);
|
||||
assertAsRelativePath(mrws, '/Coding/One/file.txt', 'file.txt', false);
|
||||
assertAsRelativePath(mrws, '/Coding/Two/files/out.txt', 'Two/files/out.txt');
|
||||
assertAsRelativePath(mrws, '/Coding/Two/files/out.txt', 'Two/files/out.txt', true);
|
||||
assertAsRelativePath(mrws, '/Coding/Two/files/out.txt', 'files/out.txt', false);
|
||||
assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt');
|
||||
assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true);
|
||||
assertAsRelativePath(mrws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false);
|
||||
|
||||
const srws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0)], name: 'Test' }, new NullLogService());
|
||||
assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt');
|
||||
assertAsRelativePath(srws, '/Coding/One/file.txt', 'file.txt', false);
|
||||
assertAsRelativePath(srws, '/Coding/One/file.txt', 'One/file.txt', true);
|
||||
assertAsRelativePath(srws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt');
|
||||
assertAsRelativePath(srws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', true);
|
||||
assertAsRelativePath(srws, '/Coding/Two2/files/out.txt', '/Coding/Two2/files/out.txt', false);
|
||||
});
|
||||
|
||||
test('getPath, legacy', function () {
|
||||
let ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
|
||||
assert.equal(ws.getPath(), undefined);
|
||||
|
||||
ws = createExtHostWorkspace(new TestRPCProtocol(), null!, new NullLogService());
|
||||
assert.equal(ws.getPath(), undefined);
|
||||
|
||||
ws = createExtHostWorkspace(new TestRPCProtocol(), undefined!, new NullLogService());
|
||||
assert.equal(ws.getPath(), undefined);
|
||||
|
||||
ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('Folder'), 0), aWorkspaceFolderData(URI.file('Another/Folder'), 1)] }, new NullLogService());
|
||||
assert.equal(ws.getPath()!.replace(/\\/g, '/'), '/Folder');
|
||||
|
||||
ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Folder'), 0)] }, new NullLogService());
|
||||
assert.equal(ws.getPath()!.replace(/\\/g, '/'), '/Folder');
|
||||
});
|
||||
|
||||
test('WorkspaceFolder has name and index', function () {
|
||||
const ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', folders: [aWorkspaceFolderData(URI.file('/Coding/One'), 0), aWorkspaceFolderData(URI.file('/Coding/Two'), 1)], name: 'Test' }, new NullLogService());
|
||||
|
||||
const [one, two] = ws.getWorkspaceFolders()!;
|
||||
|
||||
assert.equal(one.name, 'One');
|
||||
assert.equal(one.index, 0);
|
||||
assert.equal(two.name, 'Two');
|
||||
assert.equal(two.index, 1);
|
||||
});
|
||||
|
||||
test('getContainingWorkspaceFolder', () => {
|
||||
const ws = createExtHostWorkspace(new TestRPCProtocol(), {
|
||||
id: 'foo',
|
||||
name: 'Test',
|
||||
folders: [
|
||||
aWorkspaceFolderData(URI.file('/Coding/One'), 0),
|
||||
aWorkspaceFolderData(URI.file('/Coding/Two'), 1),
|
||||
aWorkspaceFolderData(URI.file('/Coding/Two/Nested'), 2)
|
||||
]
|
||||
}, new NullLogService());
|
||||
|
||||
let folder = ws.getWorkspaceFolder(URI.file('/foo/bar'));
|
||||
assert.equal(folder, undefined);
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/One/file/path.txt'))!;
|
||||
assert.equal(folder.name, 'One');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/file/path.txt'))!;
|
||||
assert.equal(folder.name, 'Two');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nest'))!;
|
||||
assert.equal(folder.name, 'Two');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/file'))!;
|
||||
assert.equal(folder.name, 'Nested');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/f'))!;
|
||||
assert.equal(folder.name, 'Nested');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested'), true)!;
|
||||
assert.equal(folder.name, 'Two');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/'), true)!;
|
||||
assert.equal(folder.name, 'Two');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested'))!;
|
||||
assert.equal(folder.name, 'Nested');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two/Nested/'))!;
|
||||
assert.equal(folder.name, 'Nested');
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two'), true)!;
|
||||
assert.equal(folder, undefined);
|
||||
|
||||
folder = ws.getWorkspaceFolder(URI.file('/Coding/Two'), false)!;
|
||||
assert.equal(folder.name, 'Two');
|
||||
});
|
||||
|
||||
test('Multiroot change event should have a delta, #29641', function (done) {
|
||||
let ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
|
||||
|
||||
let finished = false;
|
||||
const finish = (error?: any) => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
|
||||
let sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.added, []);
|
||||
assert.deepEqual(e.removed, []);
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [] });
|
||||
sub.dispose();
|
||||
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.removed, []);
|
||||
assert.equal(e.added.length, 1);
|
||||
assert.equal(e.added[0].uri.toString(), 'foo:bar');
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] });
|
||||
sub.dispose();
|
||||
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.removed, []);
|
||||
assert.equal(e.added.length, 1);
|
||||
assert.equal(e.added[0].uri.toString(), 'foo:bar2');
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar2'), 1)] });
|
||||
sub.dispose();
|
||||
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.equal(e.removed.length, 2);
|
||||
assert.equal(e.removed[0].uri.toString(), 'foo:bar');
|
||||
assert.equal(e.removed[1].uri.toString(), 'foo:bar2');
|
||||
|
||||
assert.equal(e.added.length, 1);
|
||||
assert.equal(e.added[0].uri.toString(), 'foo:bar3');
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0)] });
|
||||
sub.dispose();
|
||||
finish();
|
||||
});
|
||||
|
||||
test('Multiroot change keeps existing workspaces live', function () {
|
||||
let ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService());
|
||||
|
||||
let firstFolder = ws.getWorkspaceFolders()![0];
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar2'), 0), aWorkspaceFolderData(URI.parse('foo:bar'), 1, 'renamed')] });
|
||||
|
||||
assert.equal(ws.getWorkspaceFolders()![1], firstFolder);
|
||||
assert.equal(firstFolder.index, 1);
|
||||
assert.equal(firstFolder.name, 'renamed');
|
||||
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0), aWorkspaceFolderData(URI.parse('foo:bar2'), 1), aWorkspaceFolderData(URI.parse('foo:bar'), 2)] });
|
||||
assert.equal(ws.getWorkspaceFolders()![2], firstFolder);
|
||||
assert.equal(firstFolder.index, 2);
|
||||
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0)] });
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0), aWorkspaceFolderData(URI.parse('foo:bar'), 1)] });
|
||||
|
||||
assert.notEqual(firstFolder, ws.workspace!.folders[0]);
|
||||
});
|
||||
|
||||
test('updateWorkspaceFolders - invalid arguments', function () {
|
||||
let ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
|
||||
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, null!, null!));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 0));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 1));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 1, 0));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, -1, 0));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, -1, -1));
|
||||
|
||||
ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }, new NullLogService());
|
||||
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 1, 1));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2));
|
||||
assert.equal(false, ws.updateWorkspaceFolders(extensionDescriptor, 0, 1, asUpdateWorkspaceFolderData(URI.parse('foo:bar'))));
|
||||
});
|
||||
|
||||
test('updateWorkspaceFolders - valid arguments', function (done) {
|
||||
let finished = false;
|
||||
const finish = (error?: any) => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
|
||||
const protocol: IMainContext = {
|
||||
getProxy: () => { return undefined!; },
|
||||
set: () => { return undefined!; },
|
||||
assertRegistered: () => { }
|
||||
};
|
||||
|
||||
const ws = createExtHostWorkspace(protocol, { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
|
||||
|
||||
//
|
||||
// Add one folder
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 0, asUpdateWorkspaceFolderData(URI.parse('foo:bar'))));
|
||||
assert.equal(1, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar').toString());
|
||||
|
||||
const firstAddedFolder = ws.getWorkspaceFolders()![0];
|
||||
|
||||
let gotEvent = false;
|
||||
let sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.removed, []);
|
||||
assert.equal(e.added.length, 1);
|
||||
assert.equal(e.added[0].uri.toString(), 'foo:bar');
|
||||
assert.equal(e.added[0], firstAddedFolder); // verify object is still live
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0)] }); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
assert.equal(ws.getWorkspaceFolders()![0], firstAddedFolder); // verify object is still live
|
||||
|
||||
//
|
||||
// Add two more folders
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 1, 0, asUpdateWorkspaceFolderData(URI.parse('foo:bar1')), asUpdateWorkspaceFolderData(URI.parse('foo:bar2'))));
|
||||
assert.equal(3, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar').toString());
|
||||
assert.equal(ws.workspace!.folders[1].uri.toString(), URI.parse('foo:bar1').toString());
|
||||
assert.equal(ws.workspace!.folders[2].uri.toString(), URI.parse('foo:bar2').toString());
|
||||
|
||||
const secondAddedFolder = ws.getWorkspaceFolders()![1];
|
||||
const thirdAddedFolder = ws.getWorkspaceFolders()![2];
|
||||
|
||||
gotEvent = false;
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.removed, []);
|
||||
assert.equal(e.added.length, 2);
|
||||
assert.equal(e.added[0].uri.toString(), 'foo:bar1');
|
||||
assert.equal(e.added[1].uri.toString(), 'foo:bar2');
|
||||
assert.equal(e.added[0], secondAddedFolder);
|
||||
assert.equal(e.added[1], thirdAddedFolder);
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar1'), 1), aWorkspaceFolderData(URI.parse('foo:bar2'), 2)] }); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
assert.equal(ws.getWorkspaceFolders()![0], firstAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], secondAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![2], thirdAddedFolder); // verify object is still live
|
||||
|
||||
//
|
||||
// Remove one folder
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 2, 1));
|
||||
assert.equal(2, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar').toString());
|
||||
assert.equal(ws.workspace!.folders[1].uri.toString(), URI.parse('foo:bar1').toString());
|
||||
|
||||
gotEvent = false;
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.added, []);
|
||||
assert.equal(e.removed.length, 1);
|
||||
assert.equal(e.removed[0], thirdAddedFolder);
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0), aWorkspaceFolderData(URI.parse('foo:bar1'), 1)] }); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
assert.equal(ws.getWorkspaceFolders()![0], firstAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], secondAddedFolder); // verify object is still live
|
||||
|
||||
//
|
||||
// Rename folder
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2, asUpdateWorkspaceFolderData(URI.parse('foo:bar'), 'renamed 1'), asUpdateWorkspaceFolderData(URI.parse('foo:bar1'), 'renamed 2')));
|
||||
assert.equal(2, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar').toString());
|
||||
assert.equal(ws.workspace!.folders[1].uri.toString(), URI.parse('foo:bar1').toString());
|
||||
assert.equal(ws.workspace!.folders[0].name, 'renamed 1');
|
||||
assert.equal(ws.workspace!.folders[1].name, 'renamed 2');
|
||||
assert.equal(ws.getWorkspaceFolders()![0].name, 'renamed 1');
|
||||
assert.equal(ws.getWorkspaceFolders()![1].name, 'renamed 2');
|
||||
|
||||
gotEvent = false;
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.deepEqual(e.added, []);
|
||||
assert.equal(e.removed.length, []);
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar'), 0, 'renamed 1'), aWorkspaceFolderData(URI.parse('foo:bar1'), 1, 'renamed 2')] }); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
assert.equal(ws.getWorkspaceFolders()![0], firstAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], secondAddedFolder); // verify object is still live
|
||||
assert.equal(ws.workspace!.folders[0].name, 'renamed 1');
|
||||
assert.equal(ws.workspace!.folders[1].name, 'renamed 2');
|
||||
assert.equal(ws.getWorkspaceFolders()![0].name, 'renamed 1');
|
||||
assert.equal(ws.getWorkspaceFolders()![1].name, 'renamed 2');
|
||||
|
||||
//
|
||||
// Add and remove folders
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2, asUpdateWorkspaceFolderData(URI.parse('foo:bar3')), asUpdateWorkspaceFolderData(URI.parse('foo:bar4'))));
|
||||
assert.equal(2, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar3').toString());
|
||||
assert.equal(ws.workspace!.folders[1].uri.toString(), URI.parse('foo:bar4').toString());
|
||||
|
||||
const fourthAddedFolder = ws.getWorkspaceFolders()![0];
|
||||
const fifthAddedFolder = ws.getWorkspaceFolders()![1];
|
||||
|
||||
gotEvent = false;
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.equal(e.added.length, 2);
|
||||
assert.equal(e.added[0], fourthAddedFolder);
|
||||
assert.equal(e.added[1], fifthAddedFolder);
|
||||
assert.equal(e.removed.length, 2);
|
||||
assert.equal(e.removed[0], firstAddedFolder);
|
||||
assert.equal(e.removed[1], secondAddedFolder);
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar3'), 0), aWorkspaceFolderData(URI.parse('foo:bar4'), 1)] }); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
assert.equal(ws.getWorkspaceFolders()![0], fourthAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], fifthAddedFolder); // verify object is still live
|
||||
|
||||
//
|
||||
// Swap folders
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 0, 2, asUpdateWorkspaceFolderData(URI.parse('foo:bar4')), asUpdateWorkspaceFolderData(URI.parse('foo:bar3'))));
|
||||
assert.equal(2, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar4').toString());
|
||||
assert.equal(ws.workspace!.folders[1].uri.toString(), URI.parse('foo:bar3').toString());
|
||||
|
||||
assert.equal(ws.getWorkspaceFolders()![0], fifthAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], fourthAddedFolder); // verify object is still live
|
||||
|
||||
gotEvent = false;
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.equal(e.added.length, 0);
|
||||
assert.equal(e.removed.length, 0);
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.parse('foo:bar4'), 0), aWorkspaceFolderData(URI.parse('foo:bar3'), 1)] }); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
assert.equal(ws.getWorkspaceFolders()![0], fifthAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], fourthAddedFolder); // verify object is still live
|
||||
assert.equal(fifthAddedFolder.index, 0);
|
||||
assert.equal(fourthAddedFolder.index, 1);
|
||||
|
||||
//
|
||||
// Add one folder after the other without waiting for confirmation (not supported currently)
|
||||
//
|
||||
|
||||
assert.equal(true, ws.updateWorkspaceFolders(extensionDescriptor, 2, 0, asUpdateWorkspaceFolderData(URI.parse('foo:bar5'))));
|
||||
|
||||
assert.equal(3, ws.workspace!.folders.length);
|
||||
assert.equal(ws.workspace!.folders[0].uri.toString(), URI.parse('foo:bar4').toString());
|
||||
assert.equal(ws.workspace!.folders[1].uri.toString(), URI.parse('foo:bar3').toString());
|
||||
assert.equal(ws.workspace!.folders[2].uri.toString(), URI.parse('foo:bar5').toString());
|
||||
|
||||
const sixthAddedFolder = ws.getWorkspaceFolders()![2];
|
||||
|
||||
gotEvent = false;
|
||||
sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.equal(e.added.length, 1);
|
||||
assert.equal(e.added[0], sixthAddedFolder);
|
||||
gotEvent = true;
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({
|
||||
id: 'foo', name: 'Test', folders: [
|
||||
aWorkspaceFolderData(URI.parse('foo:bar4'), 0),
|
||||
aWorkspaceFolderData(URI.parse('foo:bar3'), 1),
|
||||
aWorkspaceFolderData(URI.parse('foo:bar5'), 2)
|
||||
]
|
||||
}); // simulate acknowledgement from main side
|
||||
assert.equal(gotEvent, true);
|
||||
sub.dispose();
|
||||
|
||||
assert.equal(ws.getWorkspaceFolders()![0], fifthAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![1], fourthAddedFolder); // verify object is still live
|
||||
assert.equal(ws.getWorkspaceFolders()![2], sixthAddedFolder); // verify object is still live
|
||||
|
||||
finish();
|
||||
});
|
||||
|
||||
test('Multiroot change event is immutable', function (done) {
|
||||
let finished = false;
|
||||
const finish = (error?: any) => {
|
||||
if (!finished) {
|
||||
finished = true;
|
||||
done(error);
|
||||
}
|
||||
};
|
||||
|
||||
let ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [] }, new NullLogService());
|
||||
let sub = ws.onDidChangeWorkspace(e => {
|
||||
try {
|
||||
assert.throws(() => {
|
||||
(<any>e).added = [];
|
||||
});
|
||||
// assert.throws(() => {
|
||||
// (<any>e.added)[0] = null;
|
||||
// });
|
||||
} catch (error) {
|
||||
finish(error);
|
||||
}
|
||||
});
|
||||
ws.$acceptWorkspaceData({ id: 'foo', name: 'Test', folders: [] });
|
||||
sub.dispose();
|
||||
finish();
|
||||
});
|
||||
|
||||
test('`vscode.workspace.getWorkspaceFolder(file)` don\'t return workspace folder when file open from command line. #36221', function () {
|
||||
let ws = createExtHostWorkspace(new TestRPCProtocol(), {
|
||||
id: 'foo', name: 'Test', folders: [
|
||||
aWorkspaceFolderData(URI.file('c:/Users/marek/Desktop/vsc_test/'), 0)
|
||||
]
|
||||
}, new NullLogService());
|
||||
|
||||
assert.ok(ws.getWorkspaceFolder(URI.file('c:/Users/marek/Desktop/vsc_test/a.txt')));
|
||||
assert.ok(ws.getWorkspaceFolder(URI.file('C:/Users/marek/Desktop/vsc_test/b.txt')));
|
||||
});
|
||||
|
||||
function aWorkspaceFolderData(uri: URI, index: number, name: string = ''): IWorkspaceFolderData {
|
||||
return {
|
||||
uri,
|
||||
index,
|
||||
name: name || basename(uri.path)
|
||||
};
|
||||
}
|
||||
|
||||
function asUpdateWorkspaceFolderData(uri: URI, name?: string): { uri: URI, name?: string } {
|
||||
return { uri, name };
|
||||
}
|
||||
|
||||
test('findFiles - string include', () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
$startFileSearch(includePattern: string, _includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<URI[] | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(includePattern, 'foo');
|
||||
assert.equal(_includeFolder, null);
|
||||
assert.equal(excludePatternOrDisregardExcludes, null);
|
||||
assert.equal(maxResults, 10);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
return ws.findFiles('foo', undefined, 10, new ExtensionIdentifier('test')).then(() => {
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
});
|
||||
|
||||
test('findFiles - RelativePattern include', () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
$startFileSearch(includePattern: string, _includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<URI[] | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(includePattern, 'glob/**');
|
||||
assert.deepEqual(_includeFolder, URI.file('/other/folder').toJSON());
|
||||
assert.equal(excludePatternOrDisregardExcludes, null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
return ws.findFiles(new RelativePattern('/other/folder', 'glob/**'), undefined, 10, new ExtensionIdentifier('test')).then(() => {
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
});
|
||||
|
||||
test('findFiles - no excludes', () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
$startFileSearch(includePattern: string, _includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<URI[] | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(includePattern, 'glob/**');
|
||||
assert.deepEqual(_includeFolder, URI.file('/other/folder').toJSON());
|
||||
assert.equal(excludePatternOrDisregardExcludes, false);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
return ws.findFiles(new RelativePattern('/other/folder', 'glob/**'), null!, 10, new ExtensionIdentifier('test')).then(() => {
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
});
|
||||
|
||||
test('findFiles - with cancelled token', () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
$startFileSearch(includePattern: string, _includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<URI[] | null> {
|
||||
mainThreadCalled = true;
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
|
||||
const token = CancellationToken.Cancelled;
|
||||
return ws.findFiles(new RelativePattern('/other/folder', 'glob/**'), null!, 10, new ExtensionIdentifier('test'), token).then(() => {
|
||||
assert(!mainThreadCalled, '!mainThreadCalled');
|
||||
});
|
||||
});
|
||||
|
||||
test('findFiles - RelativePattern exclude', () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
$startFileSearch(includePattern: string, _includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false, maxResults: number, token: CancellationToken): Promise<URI[] | null> {
|
||||
mainThreadCalled = true;
|
||||
assert(excludePatternOrDisregardExcludes, 'glob/**'); // Note that the base portion is ignored, see #52651
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
return ws.findFiles('', new RelativePattern(root, 'glob/**'), 10, new ExtensionIdentifier('test')).then(() => {
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
});
|
||||
|
||||
test('findTextInFiles - no include', async () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
async $startTextSearch(query: IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(query.pattern, 'foo');
|
||||
assert.equal(folder, null);
|
||||
assert.equal(options.includePattern, null);
|
||||
assert.equal(options.excludePattern, null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
await ws.findTextInFiles({ pattern: 'foo' }, {}, () => { }, new ExtensionIdentifier('test'));
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
|
||||
test('findTextInFiles - string include', async () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
async $startTextSearch(query: IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(query.pattern, 'foo');
|
||||
assert.equal(folder, null);
|
||||
assert.equal(options.includePattern, '**/files');
|
||||
assert.equal(options.excludePattern, null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
await ws.findTextInFiles({ pattern: 'foo' }, { include: '**/files' }, () => { }, new ExtensionIdentifier('test'));
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
|
||||
test('findTextInFiles - RelativePattern include', async () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
async $startTextSearch(query: IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(query.pattern, 'foo');
|
||||
assert.deepEqual(folder, URI.file('/other/folder').toJSON());
|
||||
assert.equal(options.includePattern, 'glob/**');
|
||||
assert.equal(options.excludePattern, null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
await ws.findTextInFiles({ pattern: 'foo' }, { include: new RelativePattern('/other/folder', 'glob/**') }, () => { }, new ExtensionIdentifier('test'));
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
|
||||
test('findTextInFiles - with cancelled token', async () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
async $startTextSearch(query: IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
mainThreadCalled = true;
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
const token = CancellationToken.Cancelled;
|
||||
await ws.findTextInFiles({ pattern: 'foo' }, {}, () => { }, new ExtensionIdentifier('test'), token);
|
||||
assert(!mainThreadCalled, '!mainThreadCalled');
|
||||
});
|
||||
|
||||
test('findTextInFiles - RelativePattern exclude', async () => {
|
||||
const root = '/project/foo';
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
|
||||
let mainThreadCalled = false;
|
||||
rpcProtocol.set(MainContext.MainThreadWorkspace, new class extends mock<MainThreadWorkspace>() {
|
||||
async $startTextSearch(query: IPatternInfo, folder: UriComponents | null, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise<ITextSearchComplete | null> {
|
||||
mainThreadCalled = true;
|
||||
assert.equal(query.pattern, 'foo');
|
||||
assert.deepEqual(folder, null);
|
||||
assert.equal(options.includePattern, null);
|
||||
assert.equal(options.excludePattern, 'glob/**'); // exclude folder is ignored...
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const ws = createExtHostWorkspace(rpcProtocol, { id: 'foo', folders: [aWorkspaceFolderData(URI.file(root), 0)], name: 'Test' }, new NullLogService());
|
||||
await ws.findTextInFiles({ pattern: 'foo' }, { exclude: new RelativePattern('/other/folder', 'glob/**') }, () => { }, new ExtensionIdentifier('test'));
|
||||
assert(mainThreadCalled, 'mainThreadCalled');
|
||||
});
|
||||
});
|
||||
87
src/vs/workbench/test/browser/api/mainThreadCommands.test.ts
Normal file
87
src/vs/workbench/test/browser/api/mainThreadCommands.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { MainThreadCommands } from 'vs/workbench/api/browser/mainThreadCommands';
|
||||
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
|
||||
suite('MainThreadCommands', function () {
|
||||
|
||||
test('dispose on unregister', function () {
|
||||
|
||||
const commands = new MainThreadCommands(SingleProxyRPCProtocol(null), undefined!, new class extends mock<IExtensionService>() { });
|
||||
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(SingleProxyRPCProtocol(null), undefined!, new class extends mock<IExtensionService>() { });
|
||||
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);
|
||||
});
|
||||
|
||||
test('activate and throw when needed', async function () {
|
||||
|
||||
const activations: string[] = [];
|
||||
const runs: string[] = [];
|
||||
|
||||
const commands = new MainThreadCommands(
|
||||
SingleProxyRPCProtocol(null),
|
||||
new class extends mock<ICommandService>() {
|
||||
executeCommand<T>(id: string): Promise<T | undefined> {
|
||||
runs.push(id);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
},
|
||||
new class extends mock<IExtensionService>() {
|
||||
activateByEvent(id: string) {
|
||||
activations.push(id);
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// case 1: arguments and retry
|
||||
try {
|
||||
activations.length = 0;
|
||||
await commands.$executeCommand('bazz', [1, 2, { n: 3 }], true);
|
||||
assert.ok(false);
|
||||
} catch (e) {
|
||||
assert.deepEqual(activations, ['onCommand:bazz']);
|
||||
assert.equal((<Error>e).message, '$executeCommand:retry');
|
||||
}
|
||||
|
||||
// case 2: no arguments and retry
|
||||
runs.length = 0;
|
||||
await commands.$executeCommand('bazz', [], true);
|
||||
assert.deepEqual(runs, ['bazz']);
|
||||
|
||||
// case 3: arguments and no retry
|
||||
runs.length = 0;
|
||||
await commands.$executeCommand('bazz', [1, 2, true], false);
|
||||
assert.deepEqual(runs, ['bazz']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,232 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
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, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { MainThreadConfiguration } from 'vs/workbench/api/browser/mainThreadConfiguration';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
suite('MainThreadConfiguration', function () {
|
||||
|
||||
let proxy = {
|
||||
$initializeConfiguration: () => { }
|
||||
};
|
||||
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(() => {
|
||||
target = sinon.spy();
|
||||
|
||||
instantiationService = new TestInstantiationService();
|
||||
instantiationService.stub(IConfigurationService, WorkspaceService);
|
||||
instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', sinon.mock());
|
||||
instantiationService.stub(IConfigurationService, 'onDidChangeConfiguration', sinon.mock());
|
||||
instantiationService.stub(IConfigurationService, 'updateValue', target);
|
||||
instantiationService.stub(IEnvironmentService, {
|
||||
isBuilt: false
|
||||
});
|
||||
});
|
||||
|
||||
test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update resource configuration without configuration target defaults to folder', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE_FOLDER, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update configuration with user configuration target', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.USER, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update configuration with workspace configuration target', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('update configuration with folder configuration target', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE_FOLDER, 'extHostConfiguration.window', 'value', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE_FOLDER, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.FOLDER });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', undefined, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE, target.args[0][3]);
|
||||
});
|
||||
|
||||
test('remove configuration without configuration target defaults to folder', function () {
|
||||
instantiationService.stub(IWorkspaceContextService, <IWorkspaceContextService>{ getWorkbenchState: () => WorkbenchState.WORKSPACE });
|
||||
const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy));
|
||||
|
||||
testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', { resource: URI.file('abc') }, undefined);
|
||||
|
||||
assert.equal(ConfigurationTarget.WORKSPACE_FOLDER, target.args[0][3]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { MarkerService } from 'vs/platform/markers/common/markerService';
|
||||
import { MainThreadDiagnostics } from 'vs/workbench/api/browser/mainThreadDiagnostics';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
|
||||
suite('MainThreadDiagnostics', function () {
|
||||
|
||||
let markerService: MarkerService;
|
||||
|
||||
setup(function () {
|
||||
markerService = new MarkerService();
|
||||
});
|
||||
|
||||
test('clear markers on dispose', function () {
|
||||
|
||||
let diag = new MainThreadDiagnostics(new class implements IExtHostContext {
|
||||
remoteAuthority = '';
|
||||
assertRegistered() { }
|
||||
set(v: any): any { return null; }
|
||||
getProxy(): any {
|
||||
return {
|
||||
$acceptMarkersChange() { }
|
||||
};
|
||||
}
|
||||
}, 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,55 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { MainThreadDocumentContentProviders } from 'vs/workbench/api/browser/mainThreadDocumentContentProviders';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { TestRPCProtocol } from 'vs/workbench/test/browser/api/testRPCProtocol';
|
||||
import { TextEdit } from 'vs/editor/common/modes';
|
||||
|
||||
suite('MainThreadDocumentContentProviders', function () {
|
||||
|
||||
test('events are processed properly', function () {
|
||||
|
||||
let uri = URI.parse('test:uri');
|
||||
let model = TextModel.createFromString('1', undefined, undefined, uri);
|
||||
|
||||
let providers = new MainThreadDocumentContentProviders(new TestRPCProtocol(), null!, null!,
|
||||
new class extends mock<IModelService>() {
|
||||
getModel(_uri: URI) {
|
||||
assert.equal(uri.toString(), _uri.toString());
|
||||
return model;
|
||||
}
|
||||
},
|
||||
new class extends mock<IEditorWorkerService>() {
|
||||
computeMoreMinimalEdits(_uri: URI, data: TextEdit[] | undefined) {
|
||||
assert.equal(model.getValue(), '1');
|
||||
return Promise.resolve(data);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let expectedEvents = 1;
|
||||
model.onDidChangeContent(e => {
|
||||
expectedEvents -= 1;
|
||||
try {
|
||||
assert.ok(expectedEvents >= 0);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
if (model.getValue() === '1\n2\n3') {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
providers.$onVirtualDocumentChange(uri, '1\n2');
|
||||
providers.$onVirtualDocumentChange(uri, '1\n2\n3');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
|
||||
suite('BoundModelReferenceCollection', () => {
|
||||
|
||||
let col = new BoundModelReferenceCollection(15, 75);
|
||||
|
||||
teardown(() => {
|
||||
col.dispose();
|
||||
});
|
||||
|
||||
test('max age', async () => {
|
||||
|
||||
let didDispose = false;
|
||||
|
||||
col.add({
|
||||
object: <any>{ textEditorModel: TextModel.createFromString('farboo') },
|
||||
dispose() {
|
||||
didDispose = true;
|
||||
}
|
||||
});
|
||||
|
||||
await timeout(30);
|
||||
assert.equal(didDispose, true);
|
||||
});
|
||||
|
||||
test('max size', () => {
|
||||
|
||||
let disposed: number[] = [];
|
||||
|
||||
col.add({
|
||||
object: <any>{ textEditorModel: TextModel.createFromString('farboo') },
|
||||
dispose() {
|
||||
disposed.push(0);
|
||||
}
|
||||
});
|
||||
col.add({
|
||||
object: <any>{ textEditorModel: TextModel.createFromString('boofar') },
|
||||
dispose() {
|
||||
disposed.push(1);
|
||||
}
|
||||
});
|
||||
|
||||
col.add({
|
||||
object: <any>{ textEditorModel: TextModel.createFromString(new Array(71).join('x')) },
|
||||
dispose() {
|
||||
disposed.push(2);
|
||||
}
|
||||
});
|
||||
|
||||
assert.deepEqual(disposed, [0, 1]);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,203 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
|
||||
import { SingleProxyRPCProtocol } from './testRPCProtocol';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ExtHostDocumentsAndEditorsShape, IDocumentsAndEditorsDelta } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { createTestCodeEditor, TestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { TestEditorService, TestEditorGroupsService, TestTextResourcePropertiesService, TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
|
||||
suite('MainThreadDocumentsAndEditors', () => {
|
||||
|
||||
let modelService: ModelServiceImpl;
|
||||
let codeEditorService: TestCodeEditorService;
|
||||
let textFileService: ITextFileService;
|
||||
let deltas: IDocumentsAndEditorsDelta[] = [];
|
||||
const hugeModelString = new Array(2 + (50 * 1024 * 1024)).join('-');
|
||||
|
||||
function myCreateTestCodeEditor(model: ITextModel | undefined): TestCodeEditor {
|
||||
return createTestCodeEditor({
|
||||
model: model,
|
||||
serviceCollection: new ServiceCollection(
|
||||
[ICodeEditorService, codeEditorService]
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
setup(() => {
|
||||
deltas.length = 0;
|
||||
const configService = new TestConfigurationService();
|
||||
configService.setUserConfiguration('editor', { 'detectIndentation': false });
|
||||
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService());
|
||||
codeEditorService = new TestCodeEditorService();
|
||||
textFileService = new class extends mock<ITextFileService>() {
|
||||
isDirty() { return false; }
|
||||
files = <any>{
|
||||
onDidSave: Event.None,
|
||||
onDidRevert: Event.None,
|
||||
onDidChangeDirty: Event.None
|
||||
};
|
||||
};
|
||||
const workbenchEditorService = new TestEditorService();
|
||||
const editorGroupService = new TestEditorGroupsService();
|
||||
|
||||
const fileService = new class extends mock<IFileService>() {
|
||||
onAfterOperation = Event.None;
|
||||
};
|
||||
|
||||
new MainThreadDocumentsAndEditors(
|
||||
SingleProxyRPCProtocol(new class extends mock<ExtHostDocumentsAndEditorsShape>() {
|
||||
$acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta) { deltas.push(delta); }
|
||||
}),
|
||||
modelService,
|
||||
textFileService,
|
||||
workbenchEditorService,
|
||||
codeEditorService,
|
||||
fileService,
|
||||
null!,
|
||||
editorGroupService,
|
||||
null!,
|
||||
new class extends mock<IPanelService>() implements IPanelService {
|
||||
_serviceBrand: undefined;
|
||||
onDidPanelOpen = Event.None;
|
||||
onDidPanelClose = Event.None;
|
||||
getActivePanel() {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
TestEnvironmentService
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
test('Model#add', () => {
|
||||
deltas.length = 0;
|
||||
|
||||
modelService.createModel('farboo', 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);
|
||||
assert.ok(model.isTooLargeForSyncing());
|
||||
|
||||
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 simple widget model', function () {
|
||||
this.timeout(1000 * 60); // increase timeout for this one test
|
||||
|
||||
const model = modelService.createModel('test', null, undefined, true);
|
||||
assert.ok(model.isForSimpleWidget);
|
||||
|
||||
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);
|
||||
const editor = myCreateTestCodeEditor(model);
|
||||
|
||||
assert.equal(deltas.length, 1);
|
||||
deltas.length = 0;
|
||||
assert.equal(deltas.length, 0);
|
||||
|
||||
editor.dispose();
|
||||
});
|
||||
|
||||
test('ignore editor w/o model', () => {
|
||||
const editor = myCreateTestCodeEditor(undefined);
|
||||
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);
|
||||
|
||||
editor.dispose();
|
||||
});
|
||||
|
||||
test('editor with model', () => {
|
||||
deltas.length = 0;
|
||||
|
||||
const model = modelService.createModel('farboo', null);
|
||||
const editor = myCreateTestCodeEditor(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(second.newActiveEditor, undefined);
|
||||
|
||||
editor.dispose();
|
||||
});
|
||||
|
||||
test('editor with dispos-ed/-ing model', () => {
|
||||
modelService.createModel('foobar', null);
|
||||
const model = modelService.createModel('farboo', null);
|
||||
const editor = myCreateTestCodeEditor(model);
|
||||
|
||||
// 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);
|
||||
|
||||
editor.dispose();
|
||||
});
|
||||
});
|
||||
209
src/vs/workbench/test/browser/api/mainThreadEditors.test.ts
Normal file
209
src/vs/workbench/test/browser/api/mainThreadEditors.test.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { MainThreadDocumentsAndEditors } from 'vs/workbench/api/browser/mainThreadDocumentsAndEditors';
|
||||
import { SingleProxyRPCProtocol, TestRPCProtocol } from './testRPCProtocol';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ExtHostDocumentsAndEditorsShape, ExtHostContext, ExtHostDocumentsShape, IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { mock } from 'vs/workbench/test/browser/api/mock';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { TestFileService, TestEditorService, TestEditorGroupsService, TestEnvironmentService, TestContextService, TestTextResourcePropertiesService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { BulkEditService } from 'vs/workbench/services/bulkEdit/browser/bulkEditService';
|
||||
import { NullLogService, ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IReference, ImmortalReference } from 'vs/base/common/lifecycle';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { LabelService } from 'vs/workbench/services/label/common/labelService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
|
||||
suite('MainThreadEditors', () => {
|
||||
|
||||
const resource = URI.parse('foo:bar');
|
||||
|
||||
let modelService: IModelService;
|
||||
let editors: MainThreadTextEditors;
|
||||
|
||||
const movedResources = new Map<URI, URI>();
|
||||
const copiedResources = new Map<URI, URI>();
|
||||
const createdResources = new Set<URI>();
|
||||
const deletedResources = new Set<URI>();
|
||||
|
||||
setup(() => {
|
||||
|
||||
movedResources.clear();
|
||||
copiedResources.clear();
|
||||
createdResources.clear();
|
||||
deletedResources.clear();
|
||||
|
||||
|
||||
const configService = new TestConfigurationService();
|
||||
modelService = new ModelServiceImpl(configService, new TestTextResourcePropertiesService(configService), new TestThemeService(), new NullLogService());
|
||||
|
||||
|
||||
const services = new ServiceCollection();
|
||||
services.set(IBulkEditService, new SyncDescriptor(BulkEditService));
|
||||
services.set(ILabelService, new SyncDescriptor(LabelService));
|
||||
services.set(ILogService, new NullLogService());
|
||||
services.set(IWorkspaceContextService, new TestContextService());
|
||||
services.set(IWorkbenchEnvironmentService, TestEnvironmentService);
|
||||
services.set(IConfigurationService, configService);
|
||||
services.set(IModelService, modelService);
|
||||
services.set(ICodeEditorService, new TestCodeEditorService());
|
||||
services.set(IFileService, new TestFileService());
|
||||
services.set(IEditorService, new TestEditorService());
|
||||
services.set(IEditorGroupsService, new TestEditorGroupsService());
|
||||
services.set(ITextFileService, new class extends mock<ITextFileService>() {
|
||||
isDirty() { return false; }
|
||||
create(uri: URI, contents?: string, options?: any) {
|
||||
createdResources.add(uri);
|
||||
return Promise.resolve(Object.create(null));
|
||||
}
|
||||
delete(resource: URI) {
|
||||
deletedResources.add(resource);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
move(source: URI, target: URI) {
|
||||
movedResources.set(source, target);
|
||||
return Promise.resolve(Object.create(null));
|
||||
}
|
||||
copy(source: URI, target: URI) {
|
||||
copiedResources.set(source, target);
|
||||
return Promise.resolve(Object.create(null));
|
||||
}
|
||||
files = <any>{
|
||||
onDidSave: Event.None,
|
||||
onDidRevert: Event.None,
|
||||
onDidChangeDirty: Event.None
|
||||
};
|
||||
});
|
||||
services.set(ITextModelService, new class extends mock<ITextModelService>() {
|
||||
createModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
|
||||
const textEditorModel = new class extends mock<IResolvedTextEditorModel>() {
|
||||
textEditorModel = modelService.getModel(resource)!;
|
||||
};
|
||||
textEditorModel.isReadonly = () => false;
|
||||
return Promise.resolve(new ImmortalReference(textEditorModel));
|
||||
}
|
||||
});
|
||||
services.set(IEditorWorkerService, new class extends mock<IEditorWorkerService>() {
|
||||
|
||||
});
|
||||
services.set(IPanelService, new class extends mock<IPanelService>() implements IPanelService {
|
||||
_serviceBrand: undefined;
|
||||
onDidPanelOpen = Event.None;
|
||||
onDidPanelClose = Event.None;
|
||||
getActivePanel() {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
const instaService = new InstantiationService(services);
|
||||
|
||||
const rpcProtocol = new TestRPCProtocol();
|
||||
rpcProtocol.set(ExtHostContext.ExtHostDocuments, new class extends mock<ExtHostDocumentsShape>() {
|
||||
$acceptModelChanged(): void {
|
||||
}
|
||||
});
|
||||
rpcProtocol.set(ExtHostContext.ExtHostDocumentsAndEditors, new class extends mock<ExtHostDocumentsAndEditorsShape>() {
|
||||
$acceptDocumentsAndEditorsDelta(): void {
|
||||
}
|
||||
});
|
||||
|
||||
const documentAndEditor = instaService.createInstance(MainThreadDocumentsAndEditors, rpcProtocol);
|
||||
|
||||
editors = instaService.createInstance(MainThreadTextEditors, documentAndEditor, SingleProxyRPCProtocol(null));
|
||||
});
|
||||
|
||||
test(`applyWorkspaceEdit returns false if model is changed by user`, () => {
|
||||
|
||||
let model = modelService.createModel('something', null, resource);
|
||||
|
||||
let workspaceResourceEdit: IWorkspaceTextEditDto = {
|
||||
resource: resource,
|
||||
modelVersionId: model.getVersionId(),
|
||||
edit: {
|
||||
text: 'asdfg',
|
||||
range: new Range(1, 1, 1, 1)
|
||||
}
|
||||
};
|
||||
|
||||
// Act as if the user edited the model
|
||||
model.applyEdits([EditOperation.insert(new Position(0, 0), 'something')]);
|
||||
|
||||
return editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit] }).then((result) => {
|
||||
assert.equal(result, false);
|
||||
});
|
||||
});
|
||||
|
||||
test(`issue #54773: applyWorkspaceEdit checks model version in race situation`, () => {
|
||||
|
||||
let model = modelService.createModel('something', null, resource);
|
||||
|
||||
let workspaceResourceEdit1: IWorkspaceTextEditDto = {
|
||||
resource: resource,
|
||||
modelVersionId: model.getVersionId(),
|
||||
edit: {
|
||||
text: 'asdfg',
|
||||
range: new Range(1, 1, 1, 1)
|
||||
}
|
||||
};
|
||||
let workspaceResourceEdit2: IWorkspaceTextEditDto = {
|
||||
resource: resource,
|
||||
modelVersionId: model.getVersionId(),
|
||||
edit: {
|
||||
text: 'asdfg',
|
||||
range: new Range(1, 1, 1, 1)
|
||||
}
|
||||
};
|
||||
|
||||
let p1 = editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit1] }).then((result) => {
|
||||
// first edit request succeeds
|
||||
assert.equal(result, true);
|
||||
});
|
||||
let p2 = editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit2] }).then((result) => {
|
||||
// second edit request fails
|
||||
assert.equal(result, false);
|
||||
});
|
||||
return Promise.all([p1, p2]);
|
||||
});
|
||||
|
||||
test(`applyWorkspaceEdit with only resource edit`, () => {
|
||||
return editors.$tryApplyWorkspaceEdit({
|
||||
edits: [
|
||||
{ oldUri: resource, newUri: resource, options: undefined },
|
||||
{ oldUri: undefined, newUri: resource, options: undefined },
|
||||
{ oldUri: resource, newUri: undefined, options: undefined }
|
||||
]
|
||||
}).then((result) => {
|
||||
assert.equal(result, true);
|
||||
assert.equal(movedResources.get(resource), resource);
|
||||
assert.equal(createdResources.has(resource), true);
|
||||
assert.equal(deletedResources.has(resource), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
12
src/vs/workbench/test/browser/api/mock.ts
Normal file
12
src/vs/workbench/test/browser/api/mock.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export interface Ctor<T> {
|
||||
new(): T;
|
||||
}
|
||||
|
||||
export function mock<T>(): Ctor<T> {
|
||||
return function () { } as any;
|
||||
}
|
||||
138
src/vs/workbench/test/browser/api/testRPCProtocol.ts
Normal file
138
src/vs/workbench/test/browser/api/testRPCProtocol.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { isThenable } from 'vs/base/common/async';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
|
||||
export function SingleProxyRPCProtocol(thing: any): IExtHostContext & IExtHostRpcService {
|
||||
return {
|
||||
_serviceBrand: undefined,
|
||||
remoteAuthority: null!,
|
||||
getProxy<T>(): T {
|
||||
return thing;
|
||||
},
|
||||
set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
|
||||
return value;
|
||||
},
|
||||
assertRegistered: undefined!
|
||||
};
|
||||
}
|
||||
|
||||
export class TestRPCProtocol implements IExtHostContext, IExtHostRpcService {
|
||||
|
||||
public _serviceBrand: undefined;
|
||||
public remoteAuthority = null!;
|
||||
|
||||
private _callCountValue: number = 0;
|
||||
private _idle?: Promise<any>;
|
||||
private _completeIdle?: Function;
|
||||
|
||||
private readonly _locals: { [id: string]: any; };
|
||||
private readonly _proxies: { [id: string]: any; };
|
||||
|
||||
constructor() {
|
||||
this._locals = Object.create(null);
|
||||
this._proxies = Object.create(null);
|
||||
}
|
||||
|
||||
private get _callCount(): number {
|
||||
return this._callCountValue;
|
||||
}
|
||||
|
||||
private set _callCount(value: number) {
|
||||
this._callCountValue = value;
|
||||
if (this._callCountValue === 0) {
|
||||
if (this._completeIdle) {
|
||||
this._completeIdle();
|
||||
}
|
||||
this._idle = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
sync(): Promise<any> {
|
||||
return new Promise<any>((c) => {
|
||||
setTimeout(c, 0);
|
||||
}).then(() => {
|
||||
if (this._callCount === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (!this._idle) {
|
||||
this._idle = new Promise<any>((c, e) => {
|
||||
this._completeIdle = c;
|
||||
});
|
||||
}
|
||||
return this._idle;
|
||||
});
|
||||
}
|
||||
|
||||
public getProxy<T>(identifier: ProxyIdentifier<T>): T {
|
||||
if (!this._proxies[identifier.sid]) {
|
||||
this._proxies[identifier.sid] = this._createProxy(identifier.sid);
|
||||
}
|
||||
return this._proxies[identifier.sid];
|
||||
}
|
||||
|
||||
private _createProxy<T>(proxyId: string): T {
|
||||
let handler = {
|
||||
get: (target: any, name: string) => {
|
||||
if (!target[name] && name.charCodeAt(0) === CharCode.DollarSign) {
|
||||
target[name] = (...myArgs: any[]) => {
|
||||
return this._remoteCall(proxyId, name, myArgs);
|
||||
};
|
||||
}
|
||||
return target[name];
|
||||
}
|
||||
};
|
||||
return new Proxy(Object.create(null), handler);
|
||||
}
|
||||
|
||||
public set<T, R extends T>(identifier: ProxyIdentifier<T>, value: R): R {
|
||||
this._locals[identifier.sid] = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
protected _remoteCall(proxyId: string, path: string, args: any[]): Promise<any> {
|
||||
this._callCount++;
|
||||
|
||||
return new Promise<any>((c) => {
|
||||
setTimeout(c, 0);
|
||||
}).then(() => {
|
||||
const instance = this._locals[proxyId];
|
||||
// pretend the args went over the wire... (invoke .toJSON on objects...)
|
||||
const wireArgs = simulateWireTransfer(args);
|
||||
let p: Promise<any>;
|
||||
try {
|
||||
let result = (<Function>instance[path]).apply(instance, wireArgs);
|
||||
p = isThenable(result) ? result : Promise.resolve(result);
|
||||
} catch (err) {
|
||||
p = Promise.reject(err);
|
||||
}
|
||||
|
||||
return p.then(result => {
|
||||
this._callCount--;
|
||||
// pretend the result went over the wire... (invoke .toJSON on objects...)
|
||||
const wireResult = simulateWireTransfer(result);
|
||||
return wireResult;
|
||||
}, err => {
|
||||
this._callCount--;
|
||||
return Promise.reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public assertRegistered(identifiers: ProxyIdentifier<any>[]): void {
|
||||
throw new Error('Not implemented!');
|
||||
}
|
||||
}
|
||||
|
||||
function simulateWireTransfer<T>(obj: T): T {
|
||||
if (!obj) {
|
||||
return obj;
|
||||
}
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { Part } from 'vs/workbench/browser/part';
|
||||
import * as Types from 'vs/base/common/types';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { append, $, hide } from 'vs/base/browser/dom';
|
||||
import { TestStorageService, TestLayoutService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestStorageService, TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
class SimplePart extends Part {
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as Platform from 'vs/platform/registry/common/platform';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { workbenchInstantiationService, TestEditorGroupView, TestEditorGroupsService, TestStorageService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestEditorGroupView, TestEditorGroupsService, TestStorageService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Workspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { EditorBreadcrumbsModel, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
|
||||
import { TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { TestContextService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { FileKind } from 'vs/platform/files/common/files';
|
||||
|
||||
|
||||
83
src/vs/workbench/test/browser/parts/editor/editor.test.ts
Normal file
83
src/vs/workbench/test/browser/parts/editor/editor.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { EditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(@IUntitledTextEditorService public untitledTextEditorService: UntitledTextEditorService) { }
|
||||
}
|
||||
|
||||
class FileEditorInput extends EditorInput {
|
||||
|
||||
constructor(private resource: URI) {
|
||||
super();
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return 'editorResourceFileTest';
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
resolve(): Promise<IEditorModel | null> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
suite('Workbench editor', () => {
|
||||
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: ServiceAccessor;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
accessor.untitledTextEditorService.dispose();
|
||||
});
|
||||
|
||||
test('toResource', () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
|
||||
assert.ok(!toResource(null!));
|
||||
|
||||
const untitled = service.create();
|
||||
|
||||
assert.equal(toResource(untitled)!.toString(), untitled.getResource().toString());
|
||||
assert.equal(toResource(untitled, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), untitled.getResource().toString());
|
||||
assert.equal(toResource(untitled, { filterByScheme: Schemas.untitled })!.toString(), untitled.getResource().toString());
|
||||
assert.equal(toResource(untitled, { filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), untitled.getResource().toString());
|
||||
assert.ok(!toResource(untitled, { filterByScheme: Schemas.file }));
|
||||
|
||||
const file = new FileEditorInput(URI.file('/some/path.txt'));
|
||||
|
||||
assert.equal(toResource(file)!.toString(), file.getResource().toString());
|
||||
assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), file.getResource().toString());
|
||||
assert.equal(toResource(file, { filterByScheme: Schemas.file })!.toString(), file.getResource().toString());
|
||||
assert.equal(toResource(file, { filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), file.getResource().toString());
|
||||
assert.ok(!toResource(file, { filterByScheme: Schemas.untitled }));
|
||||
|
||||
const diffEditorInput = new DiffEditorInput('name', 'description', untitled, file);
|
||||
|
||||
assert.ok(!toResource(diffEditorInput));
|
||||
assert.ok(!toResource(diffEditorInput, { filterByScheme: Schemas.file }));
|
||||
|
||||
assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.MASTER })!.toString(), file.getResource().toString());
|
||||
assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: Schemas.file })!.toString(), file.getResource().toString());
|
||||
assert.equal(toResource(file, { supportSideBySide: SideBySideEditor.MASTER, filterByScheme: [Schemas.file, Schemas.untitled] })!.toString(), file.getResource().toString());
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
@ITextModelService public textModelResolverService: ITextModelService,
|
||||
@IModelService public modelService: IModelService,
|
||||
@IModeService public modeService: IModeService,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
suite('Workbench editor model', () => {
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: ServiceAccessor;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
});
|
||||
|
||||
test('TextDiffEditorModel', async () => {
|
||||
const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', {
|
||||
provideTextContent: function (resource: URI): Promise<ITextModel> {
|
||||
if (resource.scheme === 'test') {
|
||||
let modelContent = 'Hello Test';
|
||||
let languageSelection = accessor.modeService.create('json');
|
||||
return Promise.resolve(accessor.modelService.createModel(modelContent, languageSelection, resource));
|
||||
}
|
||||
|
||||
return Promise.resolve(null!);
|
||||
}
|
||||
});
|
||||
|
||||
let input = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined);
|
||||
let otherInput = instantiationService.createInstance(ResourceEditorInput, 'name2', 'description', URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), undefined);
|
||||
let diffInput = new DiffEditorInput('name', 'description', input, otherInput);
|
||||
|
||||
let model = await diffInput.resolve() as TextDiffEditorModel;
|
||||
|
||||
assert(model);
|
||||
assert(model instanceof TextDiffEditorModel);
|
||||
|
||||
let diffEditorModel = model.textDiffEditorModel!;
|
||||
assert(diffEditorModel.original);
|
||||
assert(diffEditorModel.modified);
|
||||
|
||||
model = await diffInput.resolve() as TextDiffEditorModel;
|
||||
assert(model.isResolved());
|
||||
|
||||
assert(diffEditorModel !== model.textDiffEditorModel);
|
||||
diffInput.dispose();
|
||||
assert(!model.textDiffEditorModel);
|
||||
|
||||
dispose.dispose();
|
||||
});
|
||||
});
|
||||
1383
src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts
Normal file
1383
src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
|
||||
class MyEditorInput extends EditorInput {
|
||||
getTypeId(): string { return ''; }
|
||||
resolve(): any { return null; }
|
||||
}
|
||||
|
||||
suite('Workbench editor input', () => {
|
||||
|
||||
test('EditorInput', () => {
|
||||
let counter = 0;
|
||||
let input = new MyEditorInput();
|
||||
let otherInput = new MyEditorInput();
|
||||
|
||||
assert(input.matches(input));
|
||||
assert(!input.matches(otherInput));
|
||||
assert(!input.matches(null));
|
||||
assert(input.getName());
|
||||
|
||||
input.onDispose(() => {
|
||||
assert(true);
|
||||
counter++;
|
||||
});
|
||||
|
||||
input.dispose();
|
||||
assert.equal(counter, 1);
|
||||
});
|
||||
|
||||
test('DiffEditorInput', () => {
|
||||
let counter = 0;
|
||||
let input = new MyEditorInput();
|
||||
input.onDispose(() => {
|
||||
assert(true);
|
||||
counter++;
|
||||
});
|
||||
|
||||
let otherInput = new MyEditorInput();
|
||||
otherInput.onDispose(() => {
|
||||
assert(true);
|
||||
counter++;
|
||||
});
|
||||
|
||||
let diffInput = new DiffEditorInput('name', 'description', input, otherInput);
|
||||
|
||||
assert.equal(diffInput.originalInput, input);
|
||||
assert.equal(diffInput.modifiedInput, otherInput);
|
||||
assert(diffInput.matches(diffInput));
|
||||
assert(!diffInput.matches(otherInput));
|
||||
assert(!diffInput.matches(null));
|
||||
|
||||
diffInput.dispose();
|
||||
assert.equal(counter, 0);
|
||||
});
|
||||
|
||||
test('DiffEditorInput disposes when input inside disposes', function () {
|
||||
let counter = 0;
|
||||
let input = new MyEditorInput();
|
||||
let otherInput = new MyEditorInput();
|
||||
|
||||
let diffInput = new DiffEditorInput('name', 'description', input, otherInput);
|
||||
diffInput.onDispose(() => {
|
||||
counter++;
|
||||
assert(true);
|
||||
});
|
||||
|
||||
input.dispose();
|
||||
|
||||
input = new MyEditorInput();
|
||||
otherInput = new MyEditorInput();
|
||||
|
||||
let diffInput2 = new DiffEditorInput('name', 'description', input, otherInput);
|
||||
diffInput2.onDispose(() => {
|
||||
counter++;
|
||||
assert(true);
|
||||
});
|
||||
|
||||
otherInput.dispose();
|
||||
assert.equal(counter, 2);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { ITextBufferFactory } from 'vs/editor/common/model';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { TestTextResourcePropertiesService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
|
||||
class MyEditorModel extends EditorModel { }
|
||||
class MyTextEditorModel extends BaseTextEditorModel {
|
||||
createTextEditorModel(value: ITextBufferFactory, resource?: URI, preferredMode?: string) {
|
||||
return super.createTextEditorModel(value, resource, preferredMode);
|
||||
}
|
||||
|
||||
isReadonly(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
suite('Workbench editor model', () => {
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let modeService: IModeService;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = new TestInstantiationService();
|
||||
modeService = instantiationService.stub(IModeService, ModeServiceImpl);
|
||||
});
|
||||
|
||||
test('EditorModel', async () => {
|
||||
let counter = 0;
|
||||
|
||||
let m = new MyEditorModel();
|
||||
|
||||
m.onDispose(() => {
|
||||
assert(true);
|
||||
counter++;
|
||||
});
|
||||
|
||||
const model = await m.load();
|
||||
assert(model === m);
|
||||
assert.strictEqual(m.isResolved(), true);
|
||||
m.dispose();
|
||||
assert.equal(counter, 1);
|
||||
});
|
||||
|
||||
test('BaseTextEditorModel', async () => {
|
||||
let modelService = stubModelService(instantiationService);
|
||||
|
||||
let m = new MyTextEditorModel(modelService, modeService);
|
||||
const model = await m.load() as MyTextEditorModel;
|
||||
|
||||
assert(model === m);
|
||||
model.createTextEditorModel(createTextBufferFactory('foo'), null!, 'text/plain');
|
||||
assert.strictEqual(m.isResolved(), true);
|
||||
m.dispose();
|
||||
});
|
||||
|
||||
function stubModelService(instantiationService: TestInstantiationService): IModelService {
|
||||
instantiationService.stub(IConfigurationService, new TestConfigurationService());
|
||||
instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(instantiationService.get(IConfigurationService)));
|
||||
return instantiationService.createInstance(ModelServiceImpl);
|
||||
}
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as assert from 'assert';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { workbenchInstantiationService, TestEditorService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
@IModelService public modelService: IModelService,
|
||||
@IModeService public modeService: IModeService
|
||||
) { }
|
||||
}
|
||||
|
||||
suite('Resource text editors', () => {
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: ServiceAccessor;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
});
|
||||
|
||||
test('basics', async () => {
|
||||
const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' });
|
||||
accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource);
|
||||
|
||||
const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, undefined);
|
||||
|
||||
const model = await input.resolve();
|
||||
|
||||
assert.ok(model);
|
||||
assert.equal(snapshotToString(((model as ResourceEditorModel).createSnapshot()!)), 'function test() {}');
|
||||
});
|
||||
|
||||
test('custom mode', async () => {
|
||||
ModesRegistry.registerLanguage({
|
||||
id: 'resource-input-test',
|
||||
});
|
||||
|
||||
const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' });
|
||||
accessor.modelService.createModel('function test() {}', accessor.modeService.create('text'), resource);
|
||||
|
||||
const input: ResourceEditorInput = instantiationService.createInstance(ResourceEditorInput, 'The Name', 'The Description', resource, 'resource-input-test');
|
||||
|
||||
const model = await input.resolve();
|
||||
assert.ok(model);
|
||||
assert.equal(model.textEditorModel.getModeId(), 'resource-input-test');
|
||||
|
||||
input.setMode('text');
|
||||
assert.equal(model.textEditorModel.getModeId(), PLAINTEXT_MODE_ID);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,496 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as assert from 'assert';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { workbenchInstantiationService, TestEditorService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
|
||||
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ModesRegistry, PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
import { IWorkingCopyService, IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
@IUntitledTextEditorService public readonly untitledTextEditorService: IUntitledTextEditorService,
|
||||
@IEditorService public readonly editorService: TestEditorService,
|
||||
@IWorkingCopyService public readonly workingCopyService: IWorkingCopyService,
|
||||
@IModeService public readonly modeService: ModeServiceImpl,
|
||||
@IConfigurationService public readonly testConfigurationService: TestConfigurationService
|
||||
) { }
|
||||
}
|
||||
|
||||
suite('Untitled text editors', () => {
|
||||
|
||||
let instantiationService: IInstantiationService;
|
||||
let accessor: ServiceAccessor;
|
||||
|
||||
setup(() => {
|
||||
instantiationService = workbenchInstantiationService();
|
||||
accessor = instantiationService.createInstance(ServiceAccessor);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
(accessor.untitledTextEditorService as UntitledTextEditorService).dispose();
|
||||
});
|
||||
|
||||
test('basics', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const workingCopyService = accessor.workingCopyService;
|
||||
|
||||
const input1 = service.create();
|
||||
await input1.resolve();
|
||||
assert.equal(input1, service.create({ untitledResource: input1.getResource() }));
|
||||
assert.equal(service.get(input1.getResource()), input1);
|
||||
|
||||
assert.ok(service.exists(input1.getResource()));
|
||||
assert.ok(!service.exists(URI.file('testing')));
|
||||
|
||||
const input2 = service.create();
|
||||
assert.equal(service.get(input2.getResource()), input2);
|
||||
|
||||
// get()
|
||||
assert.equal(service.get(input1.getResource()), input1);
|
||||
assert.equal(service.get(input2.getResource()), input2);
|
||||
|
||||
// revert()
|
||||
await input1.revert(0);
|
||||
assert.ok(input1.isDisposed());
|
||||
assert.ok(!service.get(input1.getResource()));
|
||||
|
||||
// dirty
|
||||
const model = await input2.resolve();
|
||||
assert.equal(await service.resolve({ untitledResource: input2.getResource() }), model);
|
||||
assert.ok(service.exists(model.resource));
|
||||
|
||||
assert.ok(!input2.isDirty());
|
||||
|
||||
const resourcePromise = awaitDidChangeDirty(accessor.untitledTextEditorService);
|
||||
|
||||
model.textEditorModel.setValue('foo bar');
|
||||
|
||||
const resource = await resourcePromise;
|
||||
|
||||
assert.equal(resource.toString(), input2.getResource().toString());
|
||||
|
||||
assert.ok(input2.isDirty());
|
||||
|
||||
assert.ok(workingCopyService.isDirty(input2.getResource()));
|
||||
assert.equal(workingCopyService.dirtyCount, 1);
|
||||
|
||||
await input1.revert(0);
|
||||
await input2.revert(0);
|
||||
assert.ok(!service.get(input1.getResource()));
|
||||
assert.ok(!service.get(input2.getResource()));
|
||||
assert.ok(!input2.isDirty());
|
||||
assert.ok(!model.isDirty());
|
||||
|
||||
assert.ok(!workingCopyService.isDirty(input2.getResource()));
|
||||
assert.equal(workingCopyService.dirtyCount, 0);
|
||||
|
||||
assert.equal(await input1.revert(0), false);
|
||||
assert.ok(input1.isDisposed());
|
||||
assert.ok(!service.exists(input1.getResource()));
|
||||
|
||||
input2.dispose();
|
||||
assert.ok(!service.exists(input2.getResource()));
|
||||
});
|
||||
|
||||
function awaitDidChangeDirty(service: IUntitledTextEditorService): Promise<URI> {
|
||||
return new Promise(c => {
|
||||
const listener = service.onDidChangeDirty(async resource => {
|
||||
listener.dispose();
|
||||
|
||||
c(resource);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
test('associated resource is dirty', () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const file = URI.file(join('C:\\', '/foo/file.txt'));
|
||||
const untitled = service.create({ associatedResource: file });
|
||||
|
||||
assert.ok(service.hasAssociatedFilePath(untitled.getResource()));
|
||||
assert.equal(untitled.isDirty(), true);
|
||||
|
||||
untitled.dispose();
|
||||
});
|
||||
|
||||
test('no longer dirty when content gets empty (not with associated resource)', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const workingCopyService = accessor.workingCopyService;
|
||||
const input = service.create();
|
||||
|
||||
// dirty
|
||||
const model = await input.resolve();
|
||||
model.textEditorModel.setValue('foo bar');
|
||||
assert.ok(model.isDirty());
|
||||
assert.ok(workingCopyService.isDirty(model.resource));
|
||||
model.textEditorModel.setValue('');
|
||||
assert.ok(!model.isDirty());
|
||||
assert.ok(!workingCopyService.isDirty(model.resource));
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('via create options', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
|
||||
const model1 = await service.create().resolve();
|
||||
|
||||
model1.textEditorModel!.setValue('foo bar');
|
||||
assert.ok(model1.isDirty());
|
||||
|
||||
model1.textEditorModel!.setValue('');
|
||||
assert.ok(!model1.isDirty());
|
||||
|
||||
const model2 = await service.create({ initialValue: 'Hello World' }).resolve();
|
||||
assert.equal(snapshotToString(model2.createSnapshot()!), 'Hello World');
|
||||
|
||||
const input = service.create();
|
||||
|
||||
const model3 = await service.create({ untitledResource: input.getResource() }).resolve();
|
||||
|
||||
assert.equal(model3.resource.toString(), input.getResource().toString());
|
||||
|
||||
const file = URI.file(join('C:\\', '/foo/file44.txt'));
|
||||
const model4 = await service.create({ associatedResource: file }).resolve();
|
||||
assert.ok(service.hasAssociatedFilePath(model4.resource));
|
||||
assert.ok(model4.isDirty());
|
||||
|
||||
model1.dispose();
|
||||
model2.dispose();
|
||||
model3.dispose();
|
||||
model4.dispose();
|
||||
input.dispose();
|
||||
});
|
||||
|
||||
test('associated path remains dirty when content gets empty', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const file = URI.file(join('C:\\', '/foo/file.txt'));
|
||||
const input = service.create({ associatedResource: file });
|
||||
|
||||
// dirty
|
||||
const model = await input.resolve();
|
||||
model.textEditorModel.setValue('foo bar');
|
||||
assert.ok(model.isDirty());
|
||||
model.textEditorModel.setValue('');
|
||||
assert.ok(model.isDirty());
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('initial content is dirty', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const workingCopyService = accessor.workingCopyService;
|
||||
|
||||
const untitled = service.create({ initialValue: 'Hello World' });
|
||||
assert.equal(untitled.isDirty(), true);
|
||||
|
||||
let onDidChangeDirty: IWorkingCopy | undefined = undefined;
|
||||
const listener = workingCopyService.onDidChangeDirty(copy => {
|
||||
onDidChangeDirty = copy;
|
||||
});
|
||||
|
||||
// dirty
|
||||
const model = await untitled.resolve();
|
||||
assert.ok(model.isDirty());
|
||||
assert.equal(workingCopyService.dirtyCount, 1);
|
||||
assert.equal(onDidChangeDirty, model);
|
||||
|
||||
untitled.dispose();
|
||||
listener.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('created with files.defaultLanguage setting', () => {
|
||||
const defaultLanguage = 'javascript';
|
||||
const config = accessor.testConfigurationService;
|
||||
config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage });
|
||||
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
assert.equal(input.getMode(), defaultLanguage);
|
||||
|
||||
config.setUserConfiguration('files', { 'defaultLanguage': undefined });
|
||||
|
||||
input.dispose();
|
||||
});
|
||||
|
||||
test('created with files.defaultLanguage setting (${activeEditorLanguage})', () => {
|
||||
const config = accessor.testConfigurationService;
|
||||
config.setUserConfiguration('files', { 'defaultLanguage': '${activeEditorLanguage}' });
|
||||
|
||||
accessor.editorService.activeTextEditorMode = 'typescript';
|
||||
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
assert.equal(input.getMode(), 'typescript');
|
||||
|
||||
config.setUserConfiguration('files', { 'defaultLanguage': undefined });
|
||||
accessor.editorService.activeTextEditorMode = undefined;
|
||||
|
||||
input.dispose();
|
||||
});
|
||||
|
||||
test('created with mode overrides files.defaultLanguage setting', () => {
|
||||
const mode = 'typescript';
|
||||
const defaultLanguage = 'javascript';
|
||||
const config = accessor.testConfigurationService;
|
||||
config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage });
|
||||
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create({ mode });
|
||||
|
||||
assert.equal(input.getMode(), mode);
|
||||
|
||||
config.setUserConfiguration('files', { 'defaultLanguage': undefined });
|
||||
|
||||
input.dispose();
|
||||
});
|
||||
|
||||
test('can change mode afterwards', async () => {
|
||||
const mode = 'untitled-input-test';
|
||||
|
||||
ModesRegistry.registerLanguage({
|
||||
id: mode,
|
||||
});
|
||||
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create({ mode });
|
||||
|
||||
assert.equal(input.getMode(), mode);
|
||||
|
||||
const model = await input.resolve();
|
||||
assert.equal(model.getMode(), mode);
|
||||
|
||||
input.setMode('text');
|
||||
|
||||
assert.equal(input.getMode(), PLAINTEXT_MODE_ID);
|
||||
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('service#onDidChangeEncoding', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
service.onDidChangeEncoding(r => {
|
||||
counter++;
|
||||
assert.equal(r.toString(), input.getResource().toString());
|
||||
});
|
||||
|
||||
// encoding
|
||||
const model = await input.resolve();
|
||||
model.setEncoding('utf16');
|
||||
assert.equal(counter, 1);
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('service#onDidChangeLabel', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
service.onDidChangeLabel(r => {
|
||||
counter++;
|
||||
assert.equal(r.toString(), input.getResource().toString());
|
||||
});
|
||||
|
||||
// label
|
||||
const model = await input.resolve();
|
||||
model.textEditorModel.setValue('Foo Bar');
|
||||
assert.equal(counter, 1);
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('service#onDidDisposeModel', async () => {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
service.onDidDisposeModel(r => {
|
||||
counter++;
|
||||
assert.equal(r.toString(), input.getResource().toString());
|
||||
});
|
||||
|
||||
const model = await input.resolve();
|
||||
assert.equal(counter, 0);
|
||||
input.dispose();
|
||||
assert.equal(counter, 1);
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('model#onDidChangeContent', async function () {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
const model = await input.resolve();
|
||||
model.onDidChangeContent(() => counter++);
|
||||
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(counter, 1, 'Dirty model should trigger event');
|
||||
model.textEditorModel.setValue('bar');
|
||||
|
||||
assert.equal(counter, 2, 'Content change when dirty should trigger event');
|
||||
model.textEditorModel.setValue('');
|
||||
|
||||
assert.equal(counter, 3, 'Manual revert should trigger event');
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(counter, 4, 'Dirty model should trigger event');
|
||||
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('model#onDispose when reverted', async function () {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
const model = await input.resolve();
|
||||
model.onDispose(() => counter++);
|
||||
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
await model.revert();
|
||||
|
||||
assert.ok(input.isDisposed());
|
||||
assert.ok(counter > 1);
|
||||
});
|
||||
|
||||
test('model#onDidChangeName and input name', async function () {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
let model = await input.resolve();
|
||||
model.onDidChangeName(() => counter++);
|
||||
|
||||
model.textEditorModel.setValue('foo');
|
||||
assert.equal(input.getName(), 'foo');
|
||||
assert.equal(model.name, 'foo');
|
||||
|
||||
assert.equal(counter, 1);
|
||||
model.textEditorModel.setValue('bar');
|
||||
assert.equal(input.getName(), 'bar');
|
||||
assert.equal(model.name, 'bar');
|
||||
|
||||
assert.equal(counter, 2);
|
||||
model.textEditorModel.setValue('');
|
||||
assert.equal(input.getName(), 'Untitled-1');
|
||||
assert.equal(model.name, 'Untitled-1');
|
||||
|
||||
model.textEditorModel.setValue(' ');
|
||||
assert.equal(input.getName(), 'Untitled-1');
|
||||
assert.equal(model.name, 'Untitled-1');
|
||||
|
||||
model.textEditorModel.setValue('([]}'); // require actual words
|
||||
assert.equal(input.getName(), 'Untitled-1');
|
||||
assert.equal(model.name, 'Untitled-1');
|
||||
|
||||
model.textEditorModel.setValue('([]}hello '); // require actual words
|
||||
assert.equal(input.getName(), '([]}hello');
|
||||
assert.equal(model.name, '([]}hello');
|
||||
|
||||
assert.equal(counter, 4);
|
||||
|
||||
model.textEditorModel.setValue('Hello\nWorld');
|
||||
assert.equal(counter, 5);
|
||||
|
||||
function createSingleEditOp(text: string, positionLineNumber: number, positionColumn: number, selectionLineNumber: number = positionLineNumber, selectionColumn: number = positionColumn): IIdentifiedSingleEditOperation {
|
||||
let range = new Range(
|
||||
selectionLineNumber,
|
||||
selectionColumn,
|
||||
positionLineNumber,
|
||||
positionColumn
|
||||
);
|
||||
|
||||
return {
|
||||
identifier: null,
|
||||
range,
|
||||
text,
|
||||
forceMoveMarkers: false
|
||||
};
|
||||
}
|
||||
|
||||
model.textEditorModel.applyEdits([createSingleEditOp('hello', 2, 2)]);
|
||||
assert.equal(counter, 5); // change was not on first line
|
||||
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
|
||||
const inputWithContents = service.create({ initialValue: 'Foo' });
|
||||
model = await inputWithContents.resolve();
|
||||
|
||||
assert.equal(inputWithContents.getName(), 'Foo');
|
||||
|
||||
inputWithContents.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('model#onDidChangeDirty', async function () {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
const model = await input.resolve();
|
||||
model.onDidChangeDirty(() => counter++);
|
||||
|
||||
model.textEditorModel.setValue('foo');
|
||||
|
||||
assert.equal(counter, 1, 'Dirty model should trigger event');
|
||||
model.textEditorModel.setValue('bar');
|
||||
|
||||
assert.equal(counter, 1, 'Another change does not fire event');
|
||||
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('model#onDidChangeEncoding', async function () {
|
||||
const service = accessor.untitledTextEditorService;
|
||||
const input = service.create();
|
||||
|
||||
let counter = 0;
|
||||
|
||||
const model = await input.resolve();
|
||||
model.onDidChangeEncoding(() => counter++);
|
||||
|
||||
model.setEncoding('utf16');
|
||||
|
||||
assert.equal(counter, 1, 'Dirty model should trigger event');
|
||||
model.setEncoding('utf16');
|
||||
|
||||
assert.equal(counter, 1, 'Another change to same encoding does not fire event');
|
||||
|
||||
input.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
});
|
||||
@@ -5,17 +5,18 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ContributableViewsModel, IViewState } from 'vs/workbench/browser/parts/views/views';
|
||||
import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';
|
||||
import { IViewsRegistry, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { move } from 'vs/base/common/arrays';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService';
|
||||
import sinon = require('sinon');
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { ViewDescriptorService } from 'vs/workbench/services/views/browser/viewDescriptorService';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: 'test', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
|
||||
const ViewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
|
||||
@@ -374,3 +375,254 @@ suite('ContributableViewsModel', () => {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
const sidebarContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: 'testSidebar', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Sidebar);
|
||||
const panelContainer = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: 'testPanel', name: 'test', ctorDescriptor: new SyncDescriptor(<any>{}) }, ViewContainerLocation.Panel);
|
||||
|
||||
suite('ViewDescriptorService', () => {
|
||||
|
||||
let viewDescriptorService: IViewDescriptorService;
|
||||
|
||||
setup(() => {
|
||||
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService();
|
||||
viewDescriptorService = instantiationService.createInstance(ViewDescriptorService);
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
ViewsRegistry.deregisterViews(ViewsRegistry.getViews(sidebarContainer), sidebarContainer);
|
||||
ViewsRegistry.deregisterViews(ViewsRegistry.getViews(panelContainer), panelContainer);
|
||||
});
|
||||
|
||||
test('Empty Containers', function () {
|
||||
const sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
|
||||
const panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
|
||||
assert.equal(sidebarViews.allViewDescriptors.length, 0, 'The sidebar container should have no views yet.');
|
||||
assert.equal(panelViews.allViewDescriptors.length, 0, 'The panel container should have no views yet.');
|
||||
});
|
||||
|
||||
test('Register/Deregister', () => {
|
||||
const viewDescriptors: IViewDescriptor[] = [
|
||||
{
|
||||
id: 'view1',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 1',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view2',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 2',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view3',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 3',
|
||||
canMoveView: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(0, 2), sidebarContainer);
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(2), panelContainer);
|
||||
|
||||
|
||||
let sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
|
||||
let panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
|
||||
|
||||
assert.equal(sidebarViews.activeViewDescriptors.length, 2, 'Sidebar should have 2 views');
|
||||
assert.equal(panelViews.activeViewDescriptors.length, 1, 'Panel should have 1 view');
|
||||
|
||||
ViewsRegistry.deregisterViews(viewDescriptors.slice(0, 2), sidebarContainer);
|
||||
ViewsRegistry.deregisterViews(viewDescriptors.slice(2), panelContainer);
|
||||
|
||||
|
||||
sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
|
||||
panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
|
||||
|
||||
assert.equal(sidebarViews.activeViewDescriptors.length, 0, 'Sidebar should have no views');
|
||||
assert.equal(panelViews.activeViewDescriptors.length, 0, 'Panel should have no views');
|
||||
});
|
||||
|
||||
test('move views to existing containers', async function () {
|
||||
const viewDescriptors: IViewDescriptor[] = [
|
||||
{
|
||||
id: 'view1',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 1',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view2',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 2',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view3',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 3',
|
||||
canMoveView: true
|
||||
}
|
||||
];
|
||||
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(0, 2), sidebarContainer);
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(2), panelContainer);
|
||||
|
||||
viewDescriptorService.moveViewsToContainer(viewDescriptors.slice(2), sidebarContainer);
|
||||
viewDescriptorService.moveViewsToContainer(viewDescriptors.slice(0, 2), panelContainer);
|
||||
|
||||
let sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
|
||||
let panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
|
||||
|
||||
assert.equal(sidebarViews.activeViewDescriptors.length, 1, 'Sidebar should have 2 views');
|
||||
assert.equal(panelViews.activeViewDescriptors.length, 2, 'Panel should have 1 view');
|
||||
|
||||
assert.notEqual(sidebarViews.activeViewDescriptors.indexOf(viewDescriptors[2]), -1, `Sidebar should have ${viewDescriptors[2].name}`);
|
||||
assert.notEqual(panelViews.activeViewDescriptors.indexOf(viewDescriptors[0]), -1, `Panel should have ${viewDescriptors[0].name}`);
|
||||
assert.notEqual(panelViews.activeViewDescriptors.indexOf(viewDescriptors[1]), -1, `Panel should have ${viewDescriptors[1].name}`);
|
||||
});
|
||||
|
||||
test('move views to generated containers', async function () {
|
||||
const viewDescriptors: IViewDescriptor[] = [
|
||||
{
|
||||
id: 'view1',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 1',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view2',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 2',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view3',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 3',
|
||||
canMoveView: true
|
||||
}
|
||||
];
|
||||
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(0, 2), sidebarContainer);
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(2), panelContainer);
|
||||
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[0], ViewContainerLocation.Panel);
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[2], ViewContainerLocation.Sidebar);
|
||||
|
||||
let sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
|
||||
let panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
|
||||
|
||||
assert.equal(sidebarViews.activeViewDescriptors.length, 1, 'Sidebar container should have 1 view');
|
||||
assert.equal(panelViews.activeViewDescriptors.length, 0, 'Panel container should have no views');
|
||||
|
||||
const generatedPanel = assertIsDefined(viewDescriptorService.getViewContainer(viewDescriptors[0].id));
|
||||
const generatedSidebar = assertIsDefined(viewDescriptorService.getViewContainer(viewDescriptors[2].id));
|
||||
|
||||
assert.equal(viewDescriptorService.getViewContainerLocation(generatedPanel), ViewContainerLocation.Panel, 'Generated Panel should be in located in the panel');
|
||||
assert.equal(viewDescriptorService.getViewContainerLocation(generatedSidebar), ViewContainerLocation.Sidebar, 'Generated Sidebar should be in located in the sidebar');
|
||||
|
||||
assert.equal(viewDescriptorService.getViewContainerLocation(generatedPanel), viewDescriptorService.getViewLocation(viewDescriptors[0].id), 'Panel view location and container location should match');
|
||||
assert.equal(viewDescriptorService.getViewContainerLocation(generatedSidebar), viewDescriptorService.getViewLocation(viewDescriptors[2].id), 'Sidebar view location and container location should match');
|
||||
|
||||
assert.equal(viewDescriptorService.getDefaultContainer(viewDescriptors[2].id), panelContainer, `${viewDescriptors[2].name} has wrong default container`);
|
||||
assert.equal(viewDescriptorService.getDefaultContainer(viewDescriptors[0].id), sidebarContainer, `${viewDescriptors[0].name} has wrong default container`);
|
||||
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[0], ViewContainerLocation.Sidebar);
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[2], ViewContainerLocation.Panel);
|
||||
|
||||
sidebarViews = viewDescriptorService.getViewDescriptors(sidebarContainer);
|
||||
panelViews = viewDescriptorService.getViewDescriptors(panelContainer);
|
||||
|
||||
assert.equal(sidebarViews.activeViewDescriptors.length, 2, 'Sidebar should have 2 views');
|
||||
assert.equal(panelViews.activeViewDescriptors.length, 1, 'Panel should have 1 view');
|
||||
|
||||
assert.equal(viewDescriptorService.getViewLocation(viewDescriptors[0].id), ViewContainerLocation.Sidebar, 'View should be located in the sidebar');
|
||||
assert.equal(viewDescriptorService.getViewLocation(viewDescriptors[2].id), ViewContainerLocation.Panel, 'View should be located in the panel');
|
||||
});
|
||||
|
||||
test('move view events', async function () {
|
||||
const viewDescriptors: IViewDescriptor[] = [
|
||||
{
|
||||
id: 'view1',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 1',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view2',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 2',
|
||||
canMoveView: true
|
||||
},
|
||||
{
|
||||
id: 'view3',
|
||||
ctorDescriptor: null!,
|
||||
name: 'Test View 3',
|
||||
canMoveView: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
let expectedSequence = '';
|
||||
let actualSequence = '';
|
||||
const disposables = [];
|
||||
|
||||
const containerMoveString = (view: IViewDescriptor, from: ViewContainer, to: ViewContainer) => {
|
||||
return `Moved ${view.id} from ${from.id} to ${to.id}\n`;
|
||||
};
|
||||
|
||||
const locationMoveString = (view: IViewDescriptor, from: ViewContainerLocation, to: ViewContainerLocation) => {
|
||||
return `Moved ${view.id} from ${from === ViewContainerLocation.Sidebar ? 'Sidebar' : 'Panel'} to ${to === ViewContainerLocation.Sidebar ? 'Sidebar' : 'Panel'}\n`;
|
||||
};
|
||||
disposables.push(viewDescriptorService.onDidChangeContainer(({ views, from, to }) => {
|
||||
views.forEach(view => {
|
||||
actualSequence += containerMoveString(view, from, to);
|
||||
});
|
||||
}));
|
||||
|
||||
disposables.push(viewDescriptorService.onDidChangeLocation(({ views, from, to }) => {
|
||||
views.forEach(view => {
|
||||
actualSequence += locationMoveString(view, from, to);
|
||||
});
|
||||
}));
|
||||
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(0, 2), sidebarContainer);
|
||||
ViewsRegistry.registerViews(viewDescriptors.slice(2), panelContainer);
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[0], ViewContainerLocation.Sidebar, ViewContainerLocation.Panel);
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[0], ViewContainerLocation.Panel);
|
||||
expectedSequence += containerMoveString(viewDescriptors[0], sidebarContainer, viewDescriptorService.getViewContainer(viewDescriptors[0].id)!);
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[2], ViewContainerLocation.Panel, ViewContainerLocation.Sidebar);
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[2], ViewContainerLocation.Sidebar);
|
||||
expectedSequence += containerMoveString(viewDescriptors[2], panelContainer, viewDescriptorService.getViewContainer(viewDescriptors[2].id)!);
|
||||
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[0], ViewContainerLocation.Panel, ViewContainerLocation.Sidebar);
|
||||
expectedSequence += containerMoveString(viewDescriptors[0], viewDescriptorService.getViewContainer(viewDescriptors[0].id)!, sidebarContainer);
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[0], ViewContainerLocation.Sidebar);
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[2], ViewContainerLocation.Sidebar, ViewContainerLocation.Panel);
|
||||
expectedSequence += containerMoveString(viewDescriptors[2], viewDescriptorService.getViewContainer(viewDescriptors[2].id)!, panelContainer);
|
||||
viewDescriptorService.moveViewToLocation(viewDescriptors[2], ViewContainerLocation.Panel);
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[0], ViewContainerLocation.Sidebar, ViewContainerLocation.Panel);
|
||||
expectedSequence += containerMoveString(viewDescriptors[0], sidebarContainer, panelContainer);
|
||||
viewDescriptorService.moveViewsToContainer([viewDescriptors[0]], panelContainer);
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[2], ViewContainerLocation.Panel, ViewContainerLocation.Sidebar);
|
||||
expectedSequence += containerMoveString(viewDescriptors[2], panelContainer, sidebarContainer);
|
||||
viewDescriptorService.moveViewsToContainer([viewDescriptors[2]], sidebarContainer);
|
||||
|
||||
expectedSequence += locationMoveString(viewDescriptors[1], ViewContainerLocation.Sidebar, ViewContainerLocation.Panel);
|
||||
expectedSequence += locationMoveString(viewDescriptors[2], ViewContainerLocation.Sidebar, ViewContainerLocation.Panel);
|
||||
expectedSequence += containerMoveString(viewDescriptors[1], sidebarContainer, panelContainer);
|
||||
expectedSequence += containerMoveString(viewDescriptors[2], sidebarContainer, panelContainer);
|
||||
viewDescriptorService.moveViewsToContainer([viewDescriptors[1], viewDescriptors[2]], panelContainer);
|
||||
|
||||
assert.equal(actualSequence, expectedSequence, 'Event sequence not matching expected sequence');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
882
src/vs/workbench/test/browser/workbenchTestServices.ts
Normal file
882
src/vs/workbench/test/browser/workbenchTestServices.ts
Normal file
@@ -0,0 +1,882 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/workbench/contrib/files/browser/files.contribution'; // load our contribution into the test
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder } from 'vs/workbench/common/editor';
|
||||
import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { IEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { ILifecycleService, BeforeShutdownEvent, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { FileOperationEvent, IFileService, IFileStat, IResolveFileResult, FileChangesEvent, IResolveFileOptions, ICreateFileOptions, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, IFileStatWithMetadata, IResolveMetadataFileOptions, IWriteFileOptions, IReadFileOptions, IFileContent, IFileStreamContent, FileOperationError } from 'vs/platform/files/common/files';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl';
|
||||
import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
||||
import { IResourceEncoding, ITextFileService, IReadTextFileOptions, ITextFileStreamContent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IInstantiationService, ServicesAccessor, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { MenuBarVisibility, IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position';
|
||||
import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, IModelDecorationOptions, ITextModel, ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IShowResult, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations';
|
||||
import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IMoveEditorOptions, ICopyEditorOptions, IEditorReplacement, IGroupChangeEvent, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, IOpenEditorOverrideHandler, IVisibleEditor, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditor } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { ViewletDescriptor, Viewlet } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewlet } from 'vs/workbench/common/viewlet';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { LabelService } from 'vs/workbench/services/label/common/labelService';
|
||||
import { IDimension } from 'vs/platform/layout/browser/layoutService';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
|
||||
import { IPanel } from 'vs/workbench/common/panel';
|
||||
import { IBadge } from 'vs/workbench/services/activity/common/activity';
|
||||
import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IFilesConfigurationService, FilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
|
||||
import { BrowserTextFileService } from 'vs/workbench/services/textfile/browser/browserTextFileService';
|
||||
import * as CommonWorkbenchTestServices from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
|
||||
|
||||
export import TestTextResourcePropertiesService = CommonWorkbenchTestServices.TestTextResourcePropertiesService;
|
||||
export import TestContextService = CommonWorkbenchTestServices.TestContextService;
|
||||
export import TestStorageService = CommonWorkbenchTestServices.TestStorageService;
|
||||
export import TestWorkingCopyService = CommonWorkbenchTestServices.TestWorkingCopyService;
|
||||
import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService';
|
||||
|
||||
export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
|
||||
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
|
||||
}
|
||||
|
||||
export interface ITestInstantiationService extends IInstantiationService {
|
||||
stub<T>(service: ServiceIdentifier<T>, ctor: any): T;
|
||||
}
|
||||
|
||||
export class TestTextFileService extends BrowserTextFileService {
|
||||
private resolveTextContentError!: FileOperationError | null;
|
||||
|
||||
constructor(
|
||||
@IFileService protected fileService: IFileService,
|
||||
@IUntitledTextEditorService untitledTextEditorService: IUntitledTextEditorService,
|
||||
@ILifecycleService lifecycleService: ILifecycleService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IModelService modelService: IModelService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IDialogService dialogService: IDialogService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
|
||||
@IProductService productService: IProductService,
|
||||
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService,
|
||||
@ITextModelService textModelService: ITextModelService,
|
||||
@ICodeEditorService codeEditorService: ICodeEditorService,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@IRemotePathService remotePathService: IRemotePathService
|
||||
) {
|
||||
super(
|
||||
fileService,
|
||||
untitledTextEditorService,
|
||||
lifecycleService,
|
||||
instantiationService,
|
||||
modelService,
|
||||
environmentService,
|
||||
dialogService,
|
||||
fileDialogService,
|
||||
textResourceConfigurationService,
|
||||
filesConfigurationService,
|
||||
textModelService,
|
||||
codeEditorService,
|
||||
notificationService,
|
||||
remotePathService
|
||||
);
|
||||
}
|
||||
|
||||
setResolveTextContentErrorOnce(error: FileOperationError): void {
|
||||
this.resolveTextContentError = error;
|
||||
}
|
||||
|
||||
async readStream(resource: URI, options?: IReadTextFileOptions): Promise<ITextFileStreamContent> {
|
||||
if (this.resolveTextContentError) {
|
||||
const error = this.resolveTextContentError;
|
||||
this.resolveTextContentError = null;
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
const content = await this.fileService.readFileStream(resource, options);
|
||||
return {
|
||||
resource: content.resource,
|
||||
name: content.name,
|
||||
mtime: content.mtime,
|
||||
ctime: content.ctime,
|
||||
etag: content.etag,
|
||||
encoding: 'utf8',
|
||||
value: await createTextBufferFactoryFromStream(content.value),
|
||||
size: 10
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const TestEnvironmentService = new BrowserWorkbenchEnvironmentService(Object.create(null));
|
||||
|
||||
export function workbenchInstantiationService(overrides?: { textFileService?: (instantiationService: IInstantiationService) => ITextFileService }): ITestInstantiationService {
|
||||
const instantiationService = new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]));
|
||||
|
||||
instantiationService.stub(IEnvironmentService, TestEnvironmentService);
|
||||
const contextKeyService = <IContextKeyService>instantiationService.createInstance(MockContextKeyService);
|
||||
instantiationService.stub(IContextKeyService, contextKeyService);
|
||||
const workspaceContextService = new TestContextService(TestWorkspace);
|
||||
instantiationService.stub(IWorkspaceContextService, workspaceContextService);
|
||||
const configService = new TestConfigurationService();
|
||||
instantiationService.stub(IConfigurationService, configService);
|
||||
instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService(contextKeyService, configService, TestEnvironmentService));
|
||||
instantiationService.stub(ITextResourceConfigurationService, new TestTextResourceConfigurationService(configService));
|
||||
instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService));
|
||||
instantiationService.stub(IStorageService, new TestStorageService());
|
||||
instantiationService.stub(IWorkbenchLayoutService, new TestLayoutService());
|
||||
instantiationService.stub(IDialogService, new TestDialogService());
|
||||
instantiationService.stub(IAccessibilityService, new TestAccessibilityService());
|
||||
instantiationService.stub(IFileDialogService, new TestFileDialogService());
|
||||
instantiationService.stub(IModeService, instantiationService.createInstance(ModeServiceImpl));
|
||||
instantiationService.stub(IHistoryService, new TestHistoryService());
|
||||
instantiationService.stub(ITextResourcePropertiesService, new TestTextResourcePropertiesService(configService));
|
||||
instantiationService.stub(IModelService, instantiationService.createInstance(ModelServiceImpl));
|
||||
instantiationService.stub(IFileService, new TestFileService());
|
||||
instantiationService.stub(IBackupFileService, new TestBackupFileService());
|
||||
instantiationService.stub(ITelemetryService, NullTelemetryService);
|
||||
instantiationService.stub(INotificationService, new TestNotificationService());
|
||||
instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService));
|
||||
instantiationService.stub(IMenuService, new TestMenuService());
|
||||
instantiationService.stub(IKeybindingService, new MockKeybindingService());
|
||||
instantiationService.stub(IDecorationsService, new TestDecorationsService());
|
||||
instantiationService.stub(IExtensionService, new TestExtensionService());
|
||||
instantiationService.stub(ITextFileService, overrides?.textFileService ? overrides.textFileService(instantiationService) : <ITextFileService>instantiationService.createInstance(TestTextFileService));
|
||||
instantiationService.stub(IHostService, <IHostService>instantiationService.createInstance(TestHostService));
|
||||
instantiationService.stub(ITextModelService, <ITextModelService>instantiationService.createInstance(TextModelResolverService));
|
||||
instantiationService.stub(IThemeService, new TestThemeService());
|
||||
instantiationService.stub(ILogService, new NullLogService());
|
||||
const editorGroupService = new TestEditorGroupsService([new TestEditorGroupView(0)]);
|
||||
instantiationService.stub(IEditorGroupsService, editorGroupService);
|
||||
instantiationService.stub(ILabelService, <ILabelService>instantiationService.createInstance(LabelService));
|
||||
const editorService = new TestEditorService(editorGroupService);
|
||||
instantiationService.stub(IEditorService, editorService);
|
||||
instantiationService.stub(ICodeEditorService, new TestCodeEditorService());
|
||||
instantiationService.stub(IViewletService, new TestViewletService());
|
||||
instantiationService.stub(IWorkingCopyService, new TestWorkingCopyService());
|
||||
|
||||
return instantiationService;
|
||||
}
|
||||
|
||||
export class TestAccessibilityService implements IAccessibilityService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
onDidChangeScreenReaderOptimized = Event.None;
|
||||
|
||||
isScreenReaderOptimized(): boolean { return false; }
|
||||
alwaysUnderlineAccessKeys(): Promise<boolean> { return Promise.resolve(false); }
|
||||
setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void { }
|
||||
}
|
||||
|
||||
export class TestDecorationsService implements IDecorationsService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
onDidChangeDecorations: Event<IResourceDecorationChangeEvent> = Event.None;
|
||||
|
||||
registerDecorationsProvider(_provider: IDecorationsProvider): IDisposable { return Disposable.None; }
|
||||
getDecoration(_uri: URI, _includeChildren: boolean, _overwrite?: IDecorationData): IDecoration | undefined { return undefined; }
|
||||
}
|
||||
|
||||
export class TestExtensionService extends NullExtensionService { }
|
||||
|
||||
export class TestMenuService implements IMenuService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
createMenu(_id: MenuId, _scopedKeybindingService: IContextKeyService): IMenu {
|
||||
return {
|
||||
onDidChange: Event.None,
|
||||
dispose: () => undefined,
|
||||
getActions: () => []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class TestHistoryService implements IHistoryService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(private root?: URI) { }
|
||||
|
||||
reopenLastClosedEditor(): void { }
|
||||
forward(): void { }
|
||||
back(): void { }
|
||||
last(): void { }
|
||||
remove(_input: IEditorInput | IResourceInput): void { }
|
||||
clear(): void { }
|
||||
clearRecentlyOpened(): void { }
|
||||
getHistory(): ReadonlyArray<IEditorInput | IResourceInput> { return []; }
|
||||
openNextRecentlyUsedEditor(group?: GroupIdentifier): void { }
|
||||
openPreviouslyUsedEditor(group?: GroupIdentifier): void { }
|
||||
getLastActiveWorkspaceRoot(_schemeFilter: string): URI | undefined { return this.root; }
|
||||
getLastActiveFile(_schemeFilter: string): URI | undefined { return undefined; }
|
||||
openLastEditLocation(): void { }
|
||||
}
|
||||
|
||||
export class TestDialogService implements IDialogService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
confirm(_confirmation: IConfirmation): Promise<IConfirmationResult> { return Promise.resolve({ confirmed: false }); }
|
||||
show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise<IShowResult> { return Promise.resolve({ choice: 0 }); }
|
||||
about(): Promise<void> { return Promise.resolve(); }
|
||||
}
|
||||
|
||||
export class TestFileDialogService implements IFileDialogService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private confirmResult!: ConfirmResult;
|
||||
|
||||
defaultFilePath(_schemeFilter?: string): URI | undefined { return undefined; }
|
||||
defaultFolderPath(_schemeFilter?: string): URI | undefined { return undefined; }
|
||||
defaultWorkspacePath(_schemeFilter?: string): URI | undefined { return undefined; }
|
||||
pickFileFolderAndOpen(_options: IPickAndOpenOptions): Promise<any> { return Promise.resolve(0); }
|
||||
pickFileAndOpen(_options: IPickAndOpenOptions): Promise<any> { return Promise.resolve(0); }
|
||||
pickFolderAndOpen(_options: IPickAndOpenOptions): Promise<any> { return Promise.resolve(0); }
|
||||
pickWorkspaceAndOpen(_options: IPickAndOpenOptions): Promise<any> { return Promise.resolve(0); }
|
||||
|
||||
private fileToSave!: URI;
|
||||
setPickFileToSave(path: URI): void { this.fileToSave = path; }
|
||||
pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> { return Promise.resolve(this.fileToSave); }
|
||||
|
||||
showSaveDialog(_options: ISaveDialogOptions): Promise<URI | undefined> { return Promise.resolve(undefined); }
|
||||
showOpenDialog(_options: IOpenDialogOptions): Promise<URI[] | undefined> { return Promise.resolve(undefined); }
|
||||
|
||||
setConfirmResult(result: ConfirmResult): void { this.confirmResult = result; }
|
||||
showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> { return Promise.resolve(this.confirmResult); }
|
||||
}
|
||||
|
||||
export class TestLayoutService implements IWorkbenchLayoutService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
dimension: IDimension = { width: 800, height: 600 };
|
||||
|
||||
container: HTMLElement = window.document.body;
|
||||
|
||||
onZenModeChange: Event<boolean> = Event.None;
|
||||
onCenteredLayoutChange: Event<boolean> = Event.None;
|
||||
onFullscreenChange: Event<boolean> = Event.None;
|
||||
onMaximizeChange: Event<boolean> = Event.None;
|
||||
onPanelPositionChange: Event<string> = Event.None;
|
||||
onPartVisibilityChange: Event<void> = Event.None;
|
||||
onLayout = Event.None;
|
||||
|
||||
private readonly _onMenubarVisibilityChange = new Emitter<Dimension>();
|
||||
get onMenubarVisibilityChange(): Event<Dimension> { return this._onMenubarVisibilityChange.event; }
|
||||
|
||||
isRestored(): boolean { return true; }
|
||||
hasFocus(_part: Parts): boolean { return false; }
|
||||
hasWindowBorder(): boolean { return false; }
|
||||
getWindowBorderRadius(): string | undefined { return undefined; }
|
||||
isVisible(_part: Parts): boolean { return true; }
|
||||
getDimension(_part: Parts): Dimension { return new Dimension(0, 0); }
|
||||
getContainer(_part: Parts): HTMLElement { return null!; }
|
||||
isTitleBarHidden(): boolean { return false; }
|
||||
getTitleBarOffset(): number { return 0; }
|
||||
isStatusBarHidden(): boolean { return false; }
|
||||
isActivityBarHidden(): boolean { return false; }
|
||||
setActivityBarHidden(_hidden: boolean): void { }
|
||||
isSideBarHidden(): boolean { return false; }
|
||||
setEditorHidden(_hidden: boolean): Promise<void> { return Promise.resolve(); }
|
||||
setSideBarHidden(_hidden: boolean): Promise<void> { return Promise.resolve(); }
|
||||
isPanelHidden(): boolean { return false; }
|
||||
setPanelHidden(_hidden: boolean): Promise<void> { return Promise.resolve(); }
|
||||
toggleMaximizedPanel(): void { }
|
||||
isPanelMaximized(): boolean { return false; }
|
||||
getMenubarVisibility(): MenuBarVisibility { throw new Error('not implemented'); }
|
||||
getSideBarPosition() { return 0; }
|
||||
getPanelPosition() { return 0; }
|
||||
setPanelPosition(_position: PartPosition): Promise<void> { return Promise.resolve(); }
|
||||
addClass(_clazz: string): void { }
|
||||
removeClass(_clazz: string): void { }
|
||||
getMaximumEditorDimensions(): Dimension { throw new Error('not implemented'); }
|
||||
getWorkbenchContainer(): HTMLElement { throw new Error('not implemented'); }
|
||||
getWorkbenchElement(): HTMLElement { throw new Error('not implemented'); }
|
||||
toggleZenMode(): void { }
|
||||
isEditorLayoutCentered(): boolean { return false; }
|
||||
centerEditorLayout(_active: boolean): void { }
|
||||
resizePart(_part: Parts, _sizeChange: number): void { }
|
||||
registerPart(part: Part): void { }
|
||||
isWindowMaximized() { return false; }
|
||||
updateWindowMaximizedState(maximized: boolean): void { }
|
||||
}
|
||||
|
||||
let activeViewlet: Viewlet = {} as any;
|
||||
|
||||
export class TestViewletService implements IViewletService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
onDidViewletRegisterEmitter = new Emitter<ViewletDescriptor>();
|
||||
onDidViewletDeregisterEmitter = new Emitter<ViewletDescriptor>();
|
||||
onDidViewletOpenEmitter = new Emitter<IViewlet>();
|
||||
onDidViewletCloseEmitter = new Emitter<IViewlet>();
|
||||
|
||||
onDidViewletRegister = this.onDidViewletRegisterEmitter.event;
|
||||
onDidViewletDeregister = this.onDidViewletDeregisterEmitter.event;
|
||||
onDidViewletOpen = this.onDidViewletOpenEmitter.event;
|
||||
onDidViewletClose = this.onDidViewletCloseEmitter.event;
|
||||
|
||||
openViewlet(id: string, focus?: boolean): Promise<IViewlet | undefined> { return Promise.resolve(undefined); }
|
||||
getViewlets(): ViewletDescriptor[] { return []; }
|
||||
getAllViewlets(): ViewletDescriptor[] { return []; }
|
||||
getActiveViewlet(): IViewlet { return activeViewlet; }
|
||||
getDefaultViewletId(): string { return 'workbench.view.explorer'; }
|
||||
getViewlet(id: string): ViewletDescriptor | undefined { return undefined; }
|
||||
getProgressIndicator(id: string) { return undefined; }
|
||||
hideActiveViewlet(): void { }
|
||||
getLastActiveViewletId(): string { return undefined!; }
|
||||
dispose() { }
|
||||
}
|
||||
|
||||
export class TestPanelService implements IPanelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
onDidPanelOpen = new Emitter<{ panel: IPanel, focus: boolean }>().event;
|
||||
onDidPanelClose = new Emitter<IPanel>().event;
|
||||
|
||||
async openPanel(id?: string, focus?: boolean): Promise<undefined> { return undefined; }
|
||||
getPanel(id: string): any { return activeViewlet; }
|
||||
getPanels() { return []; }
|
||||
getPinnedPanels() { return []; }
|
||||
getActivePanel(): IViewlet { return activeViewlet; }
|
||||
setPanelEnablement(id: string, enabled: boolean): void { }
|
||||
dispose() { }
|
||||
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable { throw new Error('Method not implemented.'); }
|
||||
getProgressIndicator(id: string) { return null!; }
|
||||
hideActivePanel(): void { }
|
||||
getLastActivePanelId(): string { return undefined!; }
|
||||
}
|
||||
|
||||
export class TestEditorGroupsService implements IEditorGroupsService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(public groups: TestEditorGroupView[] = []) { }
|
||||
|
||||
onDidActiveGroupChange: Event<IEditorGroup> = Event.None;
|
||||
onDidActivateGroup: Event<IEditorGroup> = Event.None;
|
||||
onDidAddGroup: Event<IEditorGroup> = Event.None;
|
||||
onDidRemoveGroup: Event<IEditorGroup> = Event.None;
|
||||
onDidMoveGroup: Event<IEditorGroup> = Event.None;
|
||||
onDidGroupIndexChange: Event<IEditorGroup> = Event.None;
|
||||
onDidLayout: Event<IDimension> = Event.None;
|
||||
onDidEditorPartOptionsChange = Event.None;
|
||||
|
||||
orientation: any;
|
||||
whenRestored: Promise<void> = Promise.resolve(undefined);
|
||||
willRestoreEditors = false;
|
||||
|
||||
contentDimension = { width: 800, height: 600 };
|
||||
|
||||
get activeGroup(): IEditorGroup { return this.groups[0]; }
|
||||
get count(): number { return this.groups.length; }
|
||||
|
||||
getGroups(_order?: GroupsOrder): ReadonlyArray<IEditorGroup> { return this.groups; }
|
||||
getGroup(identifier: number): IEditorGroup | undefined { return find(this.groups, group => group.id === identifier); }
|
||||
getLabel(_identifier: number): string { return 'Group 1'; }
|
||||
findGroup(_scope: IFindGroupScope, _source?: number | IEditorGroup, _wrap?: boolean): IEditorGroup { throw new Error('not implemented'); }
|
||||
activateGroup(_group: number | IEditorGroup): IEditorGroup { throw new Error('not implemented'); }
|
||||
restoreGroup(_group: number | IEditorGroup): IEditorGroup { throw new Error('not implemented'); }
|
||||
getSize(_group: number | IEditorGroup): { width: number, height: number } { return { width: 100, height: 100 }; }
|
||||
setSize(_group: number | IEditorGroup, _size: { width: number, height: number }): void { }
|
||||
arrangeGroups(_arrangement: GroupsArrangement): void { }
|
||||
applyLayout(_layout: EditorGroupLayout): void { }
|
||||
setGroupOrientation(_orientation: any): void { }
|
||||
addGroup(_location: number | IEditorGroup, _direction: GroupDirection, _options?: IAddGroupOptions): IEditorGroup { throw new Error('not implemented'); }
|
||||
removeGroup(_group: number | IEditorGroup): void { }
|
||||
moveGroup(_group: number | IEditorGroup, _location: number | IEditorGroup, _direction: GroupDirection): IEditorGroup { throw new Error('not implemented'); }
|
||||
mergeGroup(_group: number | IEditorGroup, _target: number | IEditorGroup, _options?: IMergeGroupOptions): IEditorGroup { throw new Error('not implemented'); }
|
||||
copyGroup(_group: number | IEditorGroup, _location: number | IEditorGroup, _direction: GroupDirection): IEditorGroup { throw new Error('not implemented'); }
|
||||
centerLayout(active: boolean): void { }
|
||||
isLayoutCentered(): boolean { return false; }
|
||||
|
||||
partOptions!: IEditorPartOptions;
|
||||
enforcePartOptions(options: IEditorPartOptions): IDisposable { return Disposable.None; }
|
||||
}
|
||||
|
||||
export class TestEditorGroupView implements IEditorGroupView {
|
||||
|
||||
constructor(public id: number) { }
|
||||
|
||||
get group(): EditorGroup { throw new Error('not implemented'); }
|
||||
activeControl!: IVisibleEditor;
|
||||
activeEditor!: IEditorInput;
|
||||
previewEditor!: IEditorInput;
|
||||
count!: number;
|
||||
disposed!: boolean;
|
||||
editors: ReadonlyArray<IEditorInput> = [];
|
||||
label!: string;
|
||||
ariaLabel!: string;
|
||||
index!: number;
|
||||
whenRestored: Promise<void> = Promise.resolve(undefined);
|
||||
element!: HTMLElement;
|
||||
minimumWidth!: number;
|
||||
maximumWidth!: number;
|
||||
minimumHeight!: number;
|
||||
maximumHeight!: number;
|
||||
|
||||
isEmpty = true;
|
||||
isMinimized = false;
|
||||
|
||||
onWillDispose: Event<void> = Event.None;
|
||||
onDidGroupChange: Event<IGroupChangeEvent> = Event.None;
|
||||
onWillCloseEditor: Event<IEditorCloseEvent> = Event.None;
|
||||
onDidCloseEditor: Event<IEditorCloseEvent> = Event.None;
|
||||
onWillOpenEditor: Event<IEditorOpeningEvent> = Event.None;
|
||||
onDidOpenEditorFail: Event<IEditorInput> = Event.None;
|
||||
onDidFocus: Event<void> = Event.None;
|
||||
onDidChange: Event<{ width: number; height: number; }> = Event.None;
|
||||
|
||||
getEditors(_order?: EditorsOrder): ReadonlyArray<IEditorInput> { return []; }
|
||||
getEditorByIndex(_index: number): IEditorInput { throw new Error('not implemented'); }
|
||||
getIndexOfEditor(_editor: IEditorInput): number { return -1; }
|
||||
openEditor(_editor: IEditorInput, _options?: IEditorOptions): Promise<IEditor> { throw new Error('not implemented'); }
|
||||
openEditors(_editors: IEditorInputWithOptions[]): Promise<IEditor> { throw new Error('not implemented'); }
|
||||
isOpened(_editor: IEditorInput | IResourceInput): boolean { return false; }
|
||||
isPinned(_editor: IEditorInput): boolean { return false; }
|
||||
isActive(_editor: IEditorInput): boolean { return false; }
|
||||
moveEditor(_editor: IEditorInput, _target: IEditorGroup, _options?: IMoveEditorOptions): void { }
|
||||
copyEditor(_editor: IEditorInput, _target: IEditorGroup, _options?: ICopyEditorOptions): void { }
|
||||
closeEditor(_editor?: IEditorInput, options?: ICloseEditorOptions): Promise<void> { return Promise.resolve(); }
|
||||
closeEditors(_editors: IEditorInput[] | { except?: IEditorInput; direction?: CloseDirection; savedOnly?: boolean; }, options?: ICloseEditorOptions): Promise<void> { return Promise.resolve(); }
|
||||
closeAllEditors(): Promise<void> { return Promise.resolve(); }
|
||||
replaceEditors(_editors: IEditorReplacement[]): Promise<void> { return Promise.resolve(); }
|
||||
pinEditor(_editor?: IEditorInput): void { }
|
||||
focus(): void { }
|
||||
invokeWithinContext<T>(fn: (accessor: ServicesAccessor) => T): T { throw new Error('not implemented'); }
|
||||
setActive(_isActive: boolean): void { }
|
||||
notifyIndexChanged(_index: number): void { }
|
||||
dispose(): void { }
|
||||
toJSON(): object { return Object.create(null); }
|
||||
layout(_width: number, _height: number): void { }
|
||||
relayout() { }
|
||||
}
|
||||
|
||||
export class TestEditorGroupAccessor implements IEditorGroupsAccessor {
|
||||
|
||||
groups: IEditorGroupView[] = [];
|
||||
activeGroup!: IEditorGroupView;
|
||||
|
||||
partOptions: IEditorPartOptions = {};
|
||||
|
||||
onDidEditorPartOptionsChange = Event.None;
|
||||
onDidVisibilityChange = Event.None;
|
||||
|
||||
getGroup(identifier: number): IEditorGroupView | undefined { throw new Error('Method not implemented.'); }
|
||||
getGroups(order: GroupsOrder): IEditorGroupView[] { throw new Error('Method not implemented.'); }
|
||||
activateGroup(identifier: number | IEditorGroupView): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
restoreGroup(identifier: number | IEditorGroupView): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
addGroup(location: number | IEditorGroupView, direction: GroupDirection, options?: IAddGroupOptions | undefined): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
mergeGroup(group: number | IEditorGroupView, target: number | IEditorGroupView, options?: IMergeGroupOptions | undefined): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
moveGroup(group: number | IEditorGroupView, location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
copyGroup(group: number | IEditorGroupView, location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); }
|
||||
removeGroup(group: number | IEditorGroupView): void { throw new Error('Method not implemented.'); }
|
||||
arrangeGroups(arrangement: GroupsArrangement, target?: number | IEditorGroupView | undefined): void { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
export class TestEditorService implements EditorServiceImpl {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
onDidActiveEditorChange: Event<void> = Event.None;
|
||||
onDidVisibleEditorsChange: Event<void> = Event.None;
|
||||
onDidCloseEditor: Event<IEditorCloseEvent> = Event.None;
|
||||
onDidOpenEditorFail: Event<IEditorIdentifier> = Event.None;
|
||||
onDidMostRecentlyActiveEditorsChange: Event<void> = Event.None;
|
||||
|
||||
activeControl!: IVisibleEditor;
|
||||
activeTextEditorWidget: any;
|
||||
activeTextEditorMode: any;
|
||||
activeEditor!: IEditorInput;
|
||||
editors: ReadonlyArray<IEditorInput> = [];
|
||||
mostRecentlyActiveEditors: ReadonlyArray<IEditorIdentifier> = [];
|
||||
visibleControls: ReadonlyArray<IVisibleEditor> = [];
|
||||
visibleTextEditorWidgets = [];
|
||||
visibleEditors: ReadonlyArray<IEditorInput> = [];
|
||||
count = this.editors.length;
|
||||
|
||||
constructor(private editorGroupService?: IEditorGroupsService) { }
|
||||
|
||||
getEditors() { return []; }
|
||||
overrideOpenEditor(_handler: IOpenEditorOverrideHandler): IDisposable { return toDisposable(() => undefined); }
|
||||
openEditor(_editor: any, _options?: any, _group?: any): Promise<any> { throw new Error('not implemented'); }
|
||||
doResolveEditorOpenRequest(editor: IEditorInput | IResourceEditor): [IEditorGroup, EditorInput, EditorOptions | undefined] | undefined {
|
||||
if (!this.editorGroupService) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return [this.editorGroupService.activeGroup, editor as EditorInput, undefined];
|
||||
}
|
||||
openEditors(_editors: any, _group?: any): Promise<IEditor[]> { throw new Error('not implemented'); }
|
||||
isOpen(_editor: IEditorInput | IResourceInput | IUntitledTextResourceInput): boolean { return false; }
|
||||
getOpened(_editor: IEditorInput | IResourceInput | IUntitledTextResourceInput): IEditorInput { throw new Error('not implemented'); }
|
||||
replaceEditors(_editors: any, _group: any) { return Promise.resolve(undefined); }
|
||||
invokeWithinEditorContext<T>(fn: (accessor: ServicesAccessor) => T): T { throw new Error('not implemented'); }
|
||||
createInput(_input: IResourceInput | IUntitledTextResourceInput | IResourceDiffInput | IResourceSideBySideInput): IEditorInput { throw new Error('not implemented'); }
|
||||
save(editors: IEditorIdentifier[], options?: ISaveEditorsOptions): Promise<boolean> { throw new Error('Method not implemented.'); }
|
||||
saveAll(options?: ISaveEditorsOptions): Promise<boolean> { throw new Error('Method not implemented.'); }
|
||||
revert(editors: IEditorIdentifier[], options?: IRevertOptions): Promise<boolean> { throw new Error('Method not implemented.'); }
|
||||
revertAll(options?: IRevertAllEditorsOptions): Promise<boolean> { throw new Error('Method not implemented.'); }
|
||||
}
|
||||
|
||||
export class TestFileService implements IFileService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onFileChanges: Emitter<FileChangesEvent>;
|
||||
private readonly _onAfterOperation: Emitter<FileOperationEvent>;
|
||||
|
||||
readonly onWillActivateFileSystemProvider = Event.None;
|
||||
readonly onDidChangeFileSystemProviderCapabilities = Event.None;
|
||||
readonly onError: Event<Error> = Event.None;
|
||||
|
||||
private content = 'Hello Html';
|
||||
private lastReadFileUri!: URI;
|
||||
|
||||
constructor() {
|
||||
this._onFileChanges = new Emitter<FileChangesEvent>();
|
||||
this._onAfterOperation = new Emitter<FileOperationEvent>();
|
||||
}
|
||||
|
||||
setContent(content: string): void { this.content = content; }
|
||||
getContent(): string { return this.content; }
|
||||
getLastReadFileUri(): URI { return this.lastReadFileUri; }
|
||||
get onFileChanges(): Event<FileChangesEvent> { return this._onFileChanges.event; }
|
||||
fireFileChanges(event: FileChangesEvent): void { this._onFileChanges.fire(event); }
|
||||
get onAfterOperation(): Event<FileOperationEvent> { return this._onAfterOperation.event; }
|
||||
fireAfterOperation(event: FileOperationEvent): void { this._onAfterOperation.fire(event); }
|
||||
resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat>;
|
||||
resolve(resource: URI, _options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
|
||||
resolve(resource: URI, _options?: IResolveFileOptions): Promise<IFileStat> {
|
||||
return Promise.resolve({
|
||||
resource,
|
||||
etag: Date.now().toString(),
|
||||
encoding: 'utf8',
|
||||
mtime: Date.now(),
|
||||
size: 42,
|
||||
isFile: true,
|
||||
isDirectory: false,
|
||||
isSymbolicLink: false,
|
||||
name: resources.basename(resource)
|
||||
});
|
||||
}
|
||||
|
||||
async resolveAll(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]> {
|
||||
const stats = await Promise.all(toResolve.map(resourceAndOption => this.resolve(resourceAndOption.resource, resourceAndOption.options)));
|
||||
|
||||
return stats.map(stat => ({ stat, success: true }));
|
||||
}
|
||||
|
||||
async exists(_resource: URI): Promise<boolean> { return true; }
|
||||
|
||||
readFile(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileContent> {
|
||||
this.lastReadFileUri = resource;
|
||||
|
||||
return Promise.resolve({
|
||||
resource: resource,
|
||||
value: VSBuffer.fromString(this.content),
|
||||
etag: 'index.txt',
|
||||
encoding: 'utf8',
|
||||
mtime: Date.now(),
|
||||
ctime: Date.now(),
|
||||
name: resources.basename(resource),
|
||||
size: 1
|
||||
});
|
||||
}
|
||||
|
||||
readFileStream(resource: URI, options?: IReadFileOptions | undefined): Promise<IFileStreamContent> {
|
||||
this.lastReadFileUri = resource;
|
||||
|
||||
return Promise.resolve({
|
||||
resource: resource,
|
||||
value: {
|
||||
on: (event: string, callback: Function): void => {
|
||||
if (event === 'data') {
|
||||
callback(this.content);
|
||||
}
|
||||
if (event === 'end') {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
resume: () => { },
|
||||
pause: () => { },
|
||||
destroy: () => { }
|
||||
},
|
||||
etag: 'index.txt',
|
||||
encoding: 'utf8',
|
||||
mtime: Date.now(),
|
||||
ctime: Date.now(),
|
||||
size: 1,
|
||||
name: resources.basename(resource)
|
||||
});
|
||||
}
|
||||
|
||||
writeShouldThrowError: Error | undefined = undefined;
|
||||
|
||||
async writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {
|
||||
await timeout(0);
|
||||
|
||||
if (this.writeShouldThrowError) {
|
||||
throw this.writeShouldThrowError;
|
||||
}
|
||||
|
||||
return ({
|
||||
resource,
|
||||
etag: 'index.txt',
|
||||
mtime: Date.now(),
|
||||
ctime: Date.now(),
|
||||
size: 42,
|
||||
isFile: true,
|
||||
isDirectory: false,
|
||||
isSymbolicLink: false,
|
||||
name: resources.basename(resource)
|
||||
});
|
||||
}
|
||||
|
||||
move(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }
|
||||
copy(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }
|
||||
createFile(_resource: URI, _content?: VSBuffer | VSBufferReadable, _options?: ICreateFileOptions): Promise<IFileStatWithMetadata> { return Promise.resolve(null!); }
|
||||
createFolder(_resource: URI): Promise<IFileStatWithMetadata> { throw new Error('not implemented'); }
|
||||
|
||||
onDidChangeFileSystemProviderRegistrations = Event.None;
|
||||
|
||||
private providers = new Map<string, IFileSystemProvider>();
|
||||
|
||||
registerProvider(scheme: string, provider: IFileSystemProvider) {
|
||||
this.providers.set(scheme, provider);
|
||||
|
||||
return toDisposable(() => this.providers.delete(scheme));
|
||||
}
|
||||
|
||||
activateProvider(_scheme: string): Promise<void> { throw new Error('not implemented'); }
|
||||
canHandleResource(resource: URI): boolean { return resource.scheme === 'file' || this.providers.has(resource.scheme); }
|
||||
hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean {
|
||||
if (capability === FileSystemProviderCapabilities.PathCaseSensitive && isLinux) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
del(_resource: URI, _options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> { return Promise.resolve(); }
|
||||
watch(_resource: URI): IDisposable { return Disposable.None; }
|
||||
getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; }
|
||||
dispose(): void { }
|
||||
}
|
||||
|
||||
export class TestBackupFileService implements IBackupFileService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
hasBackups(): Promise<boolean> { return Promise.resolve(false); }
|
||||
hasBackup(_resource: URI): Promise<boolean> { return Promise.resolve(false); }
|
||||
hasBackupSync(resource: URI, versionId?: number): boolean { return false; }
|
||||
registerResourceForBackup(_resource: URI): Promise<void> { return Promise.resolve(); }
|
||||
deregisterResourceForBackup(_resource: URI): Promise<void> { return Promise.resolve(); }
|
||||
backup<T extends object>(_resource: URI, _content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> { return Promise.resolve(); }
|
||||
getBackups(): Promise<URI[]> { return Promise.resolve([]); }
|
||||
resolve<T extends object>(_backup: URI): Promise<IResolvedBackup<T> | undefined> { return Promise.resolve(undefined); }
|
||||
discardBackup(_resource: URI): Promise<void> { return Promise.resolve(); }
|
||||
parseBackupContent(textBufferFactory: ITextBufferFactory): string {
|
||||
const textBuffer = textBufferFactory.create(DefaultEndOfLine.LF);
|
||||
const lineCount = textBuffer.getLineCount();
|
||||
const range = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1);
|
||||
return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestCodeEditorService implements ICodeEditorService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
onCodeEditorAdd: Event<ICodeEditor> = Event.None;
|
||||
onCodeEditorRemove: Event<ICodeEditor> = Event.None;
|
||||
onDiffEditorAdd: Event<IDiffEditor> = Event.None;
|
||||
onDiffEditorRemove: Event<IDiffEditor> = Event.None;
|
||||
onDidChangeTransientModelProperty: Event<ITextModel> = Event.None;
|
||||
|
||||
addCodeEditor(_editor: ICodeEditor): void { }
|
||||
removeCodeEditor(_editor: ICodeEditor): void { }
|
||||
listCodeEditors(): ICodeEditor[] { return []; }
|
||||
addDiffEditor(_editor: IDiffEditor): void { }
|
||||
removeDiffEditor(_editor: IDiffEditor): void { }
|
||||
listDiffEditors(): IDiffEditor[] { return []; }
|
||||
getFocusedCodeEditor(): ICodeEditor | null { return null; }
|
||||
registerDecorationType(_key: string, _options: IDecorationRenderOptions, _parentTypeKey?: string): void { }
|
||||
removeDecorationType(_key: string): void { }
|
||||
resolveDecorationOptions(_typeKey: string, _writable: boolean): IModelDecorationOptions { return Object.create(null); }
|
||||
setTransientModelProperty(_model: ITextModel, _key: string, _value: any): void { }
|
||||
getTransientModelProperty(_model: ITextModel, _key: string) { }
|
||||
getTransientModelProperties(_model: ITextModel) { return undefined; }
|
||||
getActiveCodeEditor(): ICodeEditor | null { return null; }
|
||||
openCodeEditor(_input: IResourceInput, _source: ICodeEditor, _sideBySide?: boolean): Promise<ICodeEditor | null> { return Promise.resolve(null); }
|
||||
}
|
||||
|
||||
export class TestLifecycleService implements ILifecycleService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
phase!: LifecyclePhase;
|
||||
startupKind!: StartupKind;
|
||||
|
||||
private readonly _onBeforeShutdown = new Emitter<BeforeShutdownEvent>();
|
||||
get onBeforeShutdown(): Event<BeforeShutdownEvent> { return this._onBeforeShutdown.event; }
|
||||
|
||||
private readonly _onWillShutdown = new Emitter<WillShutdownEvent>();
|
||||
get onWillShutdown(): Event<WillShutdownEvent> { return this._onWillShutdown.event; }
|
||||
|
||||
private readonly _onShutdown = new Emitter<void>();
|
||||
get onShutdown(): Event<void> { return this._onShutdown.event; }
|
||||
|
||||
when(): Promise<void> { return Promise.resolve(); }
|
||||
|
||||
fireShutdown(reason = ShutdownReason.QUIT): void {
|
||||
this._onWillShutdown.fire({
|
||||
join: () => { },
|
||||
reason
|
||||
});
|
||||
}
|
||||
|
||||
fireWillShutdown(event: BeforeShutdownEvent): void { this._onBeforeShutdown.fire(event); }
|
||||
}
|
||||
|
||||
export class TestTextResourceConfigurationService implements ITextResourceConfigurationService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(private configurationService = new TestConfigurationService()) { }
|
||||
|
||||
onDidChangeConfiguration() {
|
||||
return { dispose() { } };
|
||||
}
|
||||
|
||||
getValue<T>(resource: URI, arg2?: any, arg3?: any): T {
|
||||
const position: IPosition | null = EditorPosition.isIPosition(arg2) ? arg2 : null;
|
||||
const section: string | undefined = position ? (typeof arg3 === 'string' ? arg3 : undefined) : (typeof arg2 === 'string' ? arg2 : undefined);
|
||||
return this.configurationService.getValue(section, { resource });
|
||||
}
|
||||
|
||||
updateValue(resource: URI, key: string, value: any, configurationTarget?: ConfigurationTarget): Promise<void> {
|
||||
return this.configurationService.updateValue(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoteFileSystemProvider implements IFileSystemProvider {
|
||||
|
||||
constructor(private readonly diskFileSystemProvider: IFileSystemProvider, private readonly remoteAuthority: string) { }
|
||||
|
||||
readonly capabilities: FileSystemProviderCapabilities = this.diskFileSystemProvider.capabilities;
|
||||
readonly onDidChangeCapabilities: Event<void> = this.diskFileSystemProvider.onDidChangeCapabilities;
|
||||
|
||||
readonly onDidChangeFile: Event<readonly IFileChange[]> = Event.map(this.diskFileSystemProvider.onDidChangeFile, changes => changes.map((c): IFileChange => {
|
||||
return {
|
||||
type: c.type,
|
||||
resource: c.resource.with({ scheme: Schemas.vscodeRemote, authority: this.remoteAuthority }),
|
||||
};
|
||||
}));
|
||||
watch(resource: URI, opts: IWatchOptions): IDisposable { return this.diskFileSystemProvider.watch(this.toFileResource(resource), opts); }
|
||||
|
||||
stat(resource: URI): Promise<IStat> { return this.diskFileSystemProvider.stat(this.toFileResource(resource)); }
|
||||
mkdir(resource: URI): Promise<void> { return this.diskFileSystemProvider.mkdir(this.toFileResource(resource)); }
|
||||
readdir(resource: URI): Promise<[string, FileType][]> { return this.diskFileSystemProvider.readdir(this.toFileResource(resource)); }
|
||||
delete(resource: URI, opts: FileDeleteOptions): Promise<void> { return this.diskFileSystemProvider.delete(this.toFileResource(resource), opts); }
|
||||
|
||||
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { return this.diskFileSystemProvider.rename(this.toFileResource(from), this.toFileResource(to), opts); }
|
||||
copy(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> { return this.diskFileSystemProvider.copy!(this.toFileResource(from), this.toFileResource(to), opts); }
|
||||
|
||||
readFile(resource: URI): Promise<Uint8Array> { return this.diskFileSystemProvider.readFile!(this.toFileResource(resource)); }
|
||||
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> { return this.diskFileSystemProvider.writeFile!(this.toFileResource(resource), content, opts); }
|
||||
|
||||
open(resource: URI, opts: FileOpenOptions): Promise<number> { return this.diskFileSystemProvider.open!(this.toFileResource(resource), opts); }
|
||||
close(fd: number): Promise<void> { return this.diskFileSystemProvider.close!(fd); }
|
||||
read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return this.diskFileSystemProvider.read!(fd, pos, data, offset, length); }
|
||||
write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> { return this.diskFileSystemProvider.write!(fd, pos, data, offset, length); }
|
||||
|
||||
private toFileResource(resource: URI): URI { return resource.with({ scheme: Schemas.file, authority: '' }); }
|
||||
}
|
||||
|
||||
export const productService: IProductService = { _serviceBrand: undefined, ...product };
|
||||
|
||||
export class TestHostService implements IHostService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
readonly hasFocus: boolean = true;
|
||||
readonly onDidChangeFocus: Event<boolean> = Event.None;
|
||||
|
||||
async restart(): Promise<void> { }
|
||||
async reload(): Promise<void> { }
|
||||
|
||||
async focus(): Promise<void> { }
|
||||
|
||||
async openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> { }
|
||||
|
||||
async toggleFullScreen(): Promise<void> { }
|
||||
}
|
||||
|
||||
export class TestFilesConfigurationService extends FilesConfigurationService {
|
||||
|
||||
onFilesConfigurationChange(configuration: any): void {
|
||||
super.onFilesConfigurationChange(configuration);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user