Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)

* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229

* skip failing tests

* update mac build image
This commit is contained in:
Anthony Dresser
2020-01-27 15:28:17 -08:00
committed by Karl Burtram
parent 0eaee18dc4
commit fefe1454de
481 changed files with 12764 additions and 7836 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ContributableViewsModel, ViewDescriptorService, IViewState } from 'vs/workbench/browser/parts/views/views';
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 { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { move } from 'vs/base/common/arrays';
@@ -15,6 +15,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
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';
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);

View File

@@ -271,13 +271,15 @@ suite('Workbench editor groups', () => {
const input3 = input(undefined, true, URI.parse('foo://bar'));
const input4 = input(undefined, true, URI.parse('foo://barsomething'));
group.openEditor(input3, { pinned: true, active: true });
assert.equal(group.contains({ resource: URI.parse('foo://barsomething') }), false);
assert.equal(group.contains({ resource: URI.parse('foo://bar') }), true);
assert.equal(group.contains(input4), false);
assert.equal(group.contains(input3), true);
group.closeEditor(input3);
assert.equal(group.contains({ resource: URI.parse('foo://bar') }), false);
assert.equal(group.contains(input3), false);
});
test('group serialization', function () {

View File

@@ -16,6 +16,8 @@ import { snapshotToString } from 'vs/workbench/services/textfile/common/textfile
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(
@@ -41,11 +43,12 @@ suite('Workbench untitled text editors', () => {
(accessor.untitledTextEditorService as UntitledTextEditorService).dispose();
});
test('Untitled Text Editor Service', async (done) => {
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);
@@ -60,53 +63,59 @@ suite('Workbench untitled text editors', () => {
assert.equal(service.get(input2.getResource()), input2);
// revert()
input1.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 listener = service.onDidChangeDirty(resource => {
listener.dispose();
assert.equal(resource.toString(), input2.getResource().toString());
assert.ok(input2.isDirty());
assert.ok(workingCopyService.isDirty(input2.getResource()));
assert.equal(workingCopyService.dirtyCount, 1);
input1.revert();
input2.revert();
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.ok(input1.revert());
assert.ok(input1.isDisposed());
assert.ok(!service.exists(input1.getResource()));
input2.dispose();
assert.ok(!service.exists(input2.getResource()));
done();
});
const resourcePromise = awaitDidChangeDirty(accessor.untitledTextEditorService);
model.textEditorModel.setValue('foo bar');
model.dispose();
input1.dispose();
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()));
});
test('Untitled with associated resource is dirty', () => {
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 });
@@ -117,7 +126,7 @@ suite('Workbench untitled text editors', () => {
untitled.dispose();
});
test('Untitled no longer dirty when content gets empty (not with associated resource)', async () => {
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();
@@ -134,7 +143,7 @@ suite('Workbench untitled text editors', () => {
model.dispose();
});
test('Untitled via create options', async () => {
test('via create options', async () => {
const service = accessor.untitledTextEditorService;
const model1 = await service.create().resolve();
@@ -166,15 +175,7 @@ suite('Workbench untitled text editors', () => {
input.dispose();
});
test('Untitled suggest name', function () {
const service = accessor.untitledTextEditorService;
const input = service.create();
assert.ok(input.suggestFileName().length > 0);
input.dispose();
});
test('Untitled with associated path remains dirty when content gets empty', async () => {
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 });
@@ -189,7 +190,7 @@ suite('Workbench untitled text editors', () => {
model.dispose();
});
test('Untitled with initial content is dirty', async () => {
test('initial content is dirty', async () => {
const service = accessor.untitledTextEditorService;
const workingCopyService = accessor.workingCopyService;
@@ -212,7 +213,7 @@ suite('Workbench untitled text editors', () => {
model.dispose();
});
test('Untitled created with files.defaultLanguage setting', () => {
test('created with files.defaultLanguage setting', () => {
const defaultLanguage = 'javascript';
const config = accessor.testConfigurationService;
config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage });
@@ -227,7 +228,7 @@ suite('Workbench untitled text editors', () => {
input.dispose();
});
test('Untitled created with files.defaultLanguage setting (${activeEditorLanguage})', () => {
test('created with files.defaultLanguage setting (${activeEditorLanguage})', () => {
const config = accessor.testConfigurationService;
config.setUserConfiguration('files', { 'defaultLanguage': '${activeEditorLanguage}' });
@@ -244,7 +245,7 @@ suite('Workbench untitled text editors', () => {
input.dispose();
});
test('Untitled created with mode overrides files.defaultLanguage setting', () => {
test('created with mode overrides files.defaultLanguage setting', () => {
const mode = 'typescript';
const defaultLanguage = 'javascript';
const config = accessor.testConfigurationService;
@@ -260,7 +261,7 @@ suite('Workbench untitled text editors', () => {
input.dispose();
});
test('Untitled can change mode afterwards', async () => {
test('can change mode afterwards', async () => {
const mode = 'untitled-input-test';
ModesRegistry.registerLanguage({
@@ -283,7 +284,7 @@ suite('Workbench untitled text editors', () => {
model.dispose();
});
test('encoding change event', async () => {
test('service#onDidChangeEncoding', async () => {
const service = accessor.untitledTextEditorService;
const input = service.create();
@@ -294,7 +295,7 @@ suite('Workbench untitled text editors', () => {
assert.equal(r.toString(), input.getResource().toString());
});
// dirty
// encoding
const model = await input.resolve();
model.setEncoding('utf16');
assert.equal(counter, 1);
@@ -302,7 +303,44 @@ suite('Workbench untitled text editors', () => {
model.dispose();
});
test('onDidChangeContent event', async function () {
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();
@@ -328,7 +366,89 @@ suite('Workbench untitled text editors', () => {
model.dispose();
});
test('onDidChangeDirty event', async function () {
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(counter, 1);
model.textEditorModel.setValue('bar');
assert.equal(input.getName(), 'bar');
assert.equal(counter, 2);
model.textEditorModel.setValue('');
assert.equal(input.getName(), 'Untitled-1');
model.textEditorModel.setValue(' ');
assert.equal(input.getName(), 'Untitled-1');
model.textEditorModel.setValue('([]}'); // require actual words
assert.equal(input.getName(), 'Untitled-1');
model.textEditorModel.setValue('([]}hello '); // require actual words
assert.equal(input.getName(), '([]}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();
@@ -347,22 +467,4 @@ suite('Workbench untitled text editors', () => {
input.dispose();
model.dispose();
});
test('onDidDisposeModel event', 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();
});
});

View File

@@ -7,14 +7,13 @@ 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 } from 'vs/workbench/api/common/extHost.protocol';
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/electron-browser/api/mock';
import { NullLogService } from 'vs/platform/log/common/log';
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
import { timeout } from 'vs/base/common/async';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
@@ -278,9 +277,9 @@ suite('ExtHostDocumentSaveParticipant', () => {
return participant.$participateInSave(resource, SaveReason.EXPLICIT).then(() => {
sub.dispose();
assert.equal(dto.edits.length, 1);
assert.ok(WorkspaceTextEdit.is(dto.edits[0]));
assert.equal((<WorkspaceTextEdit>dto.edits[0]).edits.length, 2);
assert.equal(dto.edits.length, 2);
assert.ok((<IWorkspaceTextEditDto>dto.edits[0]).edit);
assert.ok((<IWorkspaceTextEditDto>dto.edits[1]).edit);
});
});
@@ -326,23 +325,20 @@ suite('ExtHostDocumentSaveParticipant', () => {
$tryApplyWorkspaceEdit(dto: IWorkspaceEditDto) {
for (const edit of dto.edits) {
if (!WorkspaceTextEdit.is(edit)) {
continue;
}
const { resource, edits } = edit;
const uri = URI.revive(resource);
for (const { text, range } of edits) {
documents.$acceptModelChanged(uri, {
changes: [{
range,
text,
rangeOffset: undefined!,
rangeLength: undefined!,
}],
eol: undefined!,
versionId: documents.getDocumentData(uri)!.version + 1
}, true);
}
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);

View File

@@ -759,8 +759,6 @@ suite('ExtHostLanguageFeatures', function () {
const value = await rename(model, new EditorPosition(1, 1), 'newName');
// least relevant rename provider
assert.equal(value.edits.length, 2);
assert.equal((<modes.WorkspaceTextEdit>value.edits[0]).edits.length, 1);
assert.equal((<modes.WorkspaceTextEdit>value.edits[1]).edits.length, 1);
});
// --- parameter hints
@@ -881,7 +879,7 @@ suite('ExtHostLanguageFeatures', function () {
await rpcProtocol.sync();
const value = await provideSuggestionItems(model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set<modes.CompletionItemKind>().add(modes.CompletionItemKind.Snippet)));
assert.equal(value[0].container.incomplete, undefined);
assert.equal(value[0].container.incomplete, false);
});
test('Suggest, CompletionList', async () => {

View File

@@ -18,6 +18,7 @@ import { mock } from 'vs/workbench/test/electron-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 () {
@@ -29,7 +30,9 @@ suite('ExtHostTreeView', function () {
}
$refresh(viewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): Promise<void> {
return Promise.resolve(null).then(() => this.onRefresh.fire(itemsToRefresh));
return Promise.resolve(null).then(() => {
this.onRefresh.fire(itemsToRefresh);
});
}
$reveal(): Promise<void> {
@@ -243,46 +246,62 @@ suite('ExtHostTreeView', function () {
onDidChangeTreeNode.fire(getNode('bb'));
});
test('refresh parent and child node trigger refresh only on parent - scenario 1', function (done) {
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
async function runWithEventMerging(action: (resolve: () => void) => void) {
await new Promise((resolve) => {
let subscription: IDisposable | undefined = undefined;
subscription = target.onRefresh.event(() => {
subscription!.dispose();
resolve();
});
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
});
done();
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'));
});
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', function (done) {
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
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();
});
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
});
done();
onDidChangeTreeNode.fire(getNode('bb'));
onDidChangeTreeNode.fire(getNode('aa'));
onDidChangeTreeNode.fire(getNode('b'));
});
onDidChangeTreeNode.fire(getNode('bb'));
onDidChangeTreeNode.fire(getNode('aa'));
onDidChangeTreeNode.fire(getNode('b'));
});
test('refresh an element for label change', function (done) {
@@ -299,63 +318,73 @@ suite('ExtHostTreeView', function () {
onDidChangeTreeNode.fire(getNode('a'));
});
test('refresh calls are throttled on roots', function (done) {
target.onRefresh.event(actuals => {
assert.equal(undefined, actuals);
done();
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);
});
onDidChangeTreeNode.fire(undefined);
onDidChangeTreeNode.fire(undefined);
onDidChangeTreeNode.fire(undefined);
onDidChangeTreeNode.fire(undefined);
});
test('refresh calls are throttled on elements', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
done();
});
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'));
onDidChangeTreeNode.fire(getNode('a'));
onDidChangeTreeNode.fire(getNode('b'));
onDidChangeTreeNode.fire(getNode('b'));
onDidChangeTreeNode.fire(getNode('a'));
});
});
test('refresh calls are throttled on unknown elements', function (done) {
target.onRefresh.event(actuals => {
assert.deepEqual(['0/0:a', '0/0:b'], Object.keys(actuals));
done();
});
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'));
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', function (done) {
target.onRefresh.event(actuals => {
assert.equal(undefined, actuals);
done();
});
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);
onDidChangeTreeNode.fire(getNode('a'));
onDidChangeTreeNode.fire(getNode('b'));
onDidChangeTreeNode.fire(getNode('g'));
onDidChangeTreeNode.fire(undefined);
});
});
test('refresh calls are throttled on elements and root', function (done) {
target.onRefresh.event(actuals => {
assert.equal(undefined, actuals);
done();
});
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'));
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) => {
@@ -552,37 +581,40 @@ suite('ExtHostTreeView', function () {
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'));
tree = {
'a': {
'aa': {},
'ac': {}
},
'b': {
'ba': {},
'bc': {}
}
};
onDidChangeTreeNode.fire(getNode('b'));
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]);
});
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]);
});
});
});
});

View File

@@ -7,6 +7,7 @@ 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);
@@ -383,33 +384,22 @@ suite('ExtHostTypes', function () {
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();
const all = edit.allEntries();
assert.equal(all.length, 4);
function isFileChange(thing: [URI, types.TextEdit[]] | [URI?, URI?, { overwrite?: boolean }?]): thing is [URI?, URI?, { overwrite?: boolean }?] {
const [f, s] = thing;
return URI.isUri(f) && URI.isUri(s);
}
function isTextChange(thing: [URI, types.TextEdit[]] | [URI?, URI?, { overwrite?: boolean }?]): thing is [URI, types.TextEdit[]] {
const [f, s] = thing;
return URI.isUri(f) && Array.isArray(s);
}
const [first, second, third, fourth] = all;
assert.equal(first[0]!.toString(), 'foo:a');
assert.ok(!isFileChange(first));
assert.ok(isTextChange(first) && first[1].length === 1);
assertType(first._type === 2);
assert.equal(first.uri.toString(), 'foo:a');
assert.equal(second[0]!.toString(), 'foo:a');
assert.ok(isFileChange(second));
assertType(second._type === 1);
assert.equal(second.from!.toString(), 'foo:a');
assert.equal(second.to!.toString(), 'foo:b');
assert.equal(third[0]!.toString(), 'foo:a');
assert.ok(isTextChange(third) && third[1].length === 1);
assertType(third._type === 2);
assert.equal(third.uri.toString(), 'foo:a');
assert.equal(fourth[0]!.toString(), 'foo:b');
assert.ok(!isFileChange(fourth));
assert.ok(isTextChange(fourth) && fourth[1].length === 1);
assertType(fourth._type === 2);
assert.equal(fourth.uri.toString(), 'foo:b');
});
test('WorkspaceEdit - two edits for one resource', function () {
@@ -418,10 +408,13 @@ suite('ExtHostTypes', function () {
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();
assert.equal((first as [URI, types.TextEdit[]])[1][0].newText, 'Hello');
assert.equal((second as [URI, types.TextEdit[]])[1][0].newText, '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', () => {

View File

@@ -10,7 +10,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
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 } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostDocumentsAndEditorsShape, ExtHostContext, ExtHostDocumentsShape, IWorkspaceTextEditDto } from 'vs/workbench/api/common/extHost.protocol';
import { mock } from 'vs/workbench/test/electron-browser/api/mock';
import { Event } from 'vs/base/common/event';
import { MainThreadTextEditors } from 'vs/workbench/api/browser/mainThreadEditors';
@@ -20,7 +20,6 @@ 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/workbenchTestServices';
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
import { BulkEditService } from 'vs/workbench/services/bulkEdit/browser/bulkEditService';
import { NullLogService } from 'vs/platform/log/common/log';
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
@@ -141,13 +140,13 @@ suite('MainThreadEditors', () => {
let model = modelService.createModel('something', null, resource);
let workspaceResourceEdit: WorkspaceTextEdit = {
let workspaceResourceEdit: IWorkspaceTextEditDto = {
resource: resource,
modelVersionId: model.getVersionId(),
edits: [{
edit: {
text: 'asdfg',
range: new Range(1, 1, 1, 1)
}]
}
};
// Act as if the user edited the model
@@ -162,21 +161,21 @@ suite('MainThreadEditors', () => {
let model = modelService.createModel('something', null, resource);
let workspaceResourceEdit1: WorkspaceTextEdit = {
let workspaceResourceEdit1: IWorkspaceTextEditDto = {
resource: resource,
modelVersionId: model.getVersionId(),
edits: [{
edit: {
text: 'asdfg',
range: new Range(1, 1, 1, 1)
}]
}
};
let workspaceResourceEdit2: WorkspaceTextEdit = {
let workspaceResourceEdit2: IWorkspaceTextEditDto = {
resource: resource,
modelVersionId: model.getVersionId(),
edits: [{
edit: {
text: 'asdfg',
range: new Range(1, 1, 1, 1)
}]
}
};
let p1 = editors.$tryApplyWorkspaceEdit({ edits: [workspaceResourceEdit1] }).then((result) => {

View File

@@ -63,7 +63,7 @@ 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 { ILogService, NullLogService, LogLevel } 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';
@@ -100,7 +100,19 @@ export function createFileInput(instantiationService: IInstantiationService, res
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined);
}
export const TestEnvironmentService = new NativeWorkbenchEnvironmentService(parseArgs(process.argv, OPTIONS) as IWindowConfiguration, process.execPath, 0);
export const TestWindowConfiguration: IWindowConfiguration = {
windowId: 0,
sessionId: 'testSessionId',
logLevel: LogLevel.Error,
mainPid: 0,
appRoot: '',
userEnv: {},
execPath: process.execPath,
perfEntries: [],
...parseArgs(process.argv, OPTIONS)
};
export const TestEnvironmentService = new NativeWorkbenchEnvironmentService(TestWindowConfiguration, process.execPath, 0);
export class TestContextService implements IWorkspaceContextService {
_serviceBrand: undefined;
@@ -190,7 +202,6 @@ export class TestContextService implements IWorkspaceContextService {
}
export class TestTextFileService extends NativeTextFileService {
private promptPath!: URI;
private resolveTextContentError!: FileOperationError | null;
constructor(
@@ -200,14 +211,13 @@ export class TestTextFileService extends NativeTextFileService {
@IInstantiationService instantiationService: IInstantiationService,
@IModelService modelService: IModelService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IHistoryService historyService: IHistoryService,
@IDialogService dialogService: IDialogService,
@IFileDialogService fileDialogService: IFileDialogService,
@IEditorService editorService: IEditorService,
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
@IProductService productService: IProductService,
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService,
@ITextModelService textModelService: ITextModelService
@ITextModelService textModelService: ITextModelService,
@ICodeEditorService codeEditorService: ICodeEditorService
) {
super(
fileService,
@@ -216,21 +226,16 @@ export class TestTextFileService extends NativeTextFileService {
instantiationService,
modelService,
environmentService,
historyService,
dialogService,
fileDialogService,
editorService,
textResourceConfigurationService,
productService,
filesConfigurationService,
textModelService
textModelService,
codeEditorService
);
}
setPromptPath(path: URI): void {
this.promptPath = path;
}
setResolveTextContentErrorOnce(error: FileOperationError): void {
this.resolveTextContentError = error;
}
@@ -255,10 +260,6 @@ export class TestTextFileService extends NativeTextFileService {
size: 10
};
}
promptForPath(_resource: URI, _defaultPath: URI): Promise<URI> {
return Promise.resolve(this.promptPath);
}
}
export interface ITestInstantiationService extends IInstantiationService {
@@ -317,10 +318,10 @@ export class TestAccessibilityService implements IAccessibilityService {
_serviceBrand: undefined;
onDidChangeAccessibilitySupport = Event.None;
onDidChangeScreenReaderOptimized = Event.None;
isScreenReaderOptimized(): boolean { return false; }
alwaysUnderlineAccessKeys(): Promise<boolean> { return Promise.resolve(false); }
getAccessibilitySupport(): AccessibilitySupport { return AccessibilitySupport.Unknown; }
setAccessibilitySupport(accessibilitySupport: AccessibilitySupport): void { }
}
@@ -411,8 +412,12 @@ export class TestFileDialogService implements IFileDialogService {
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(undefined);
return Promise.resolve(this.fileToSave);
}
showSaveDialog(_options: ISaveDialogOptions): Promise<URI | undefined> {
return Promise.resolve(undefined);
@@ -768,6 +773,7 @@ export class TestEditorGroupView implements IEditorGroupView {
disposed!: boolean;
editors: ReadonlyArray<IEditorInput> = [];
label!: string;
ariaLabel!: string;
index!: number;
whenRestored: Promise<void> = Promise.resolve(undefined);
element!: HTMLElement;
@@ -1096,11 +1102,11 @@ export class TestFileService implements IFileService {
}
copy(_source: URI, _target: URI, _overwrite?: boolean): Promise<IFileStatWithMetadata> {
throw new Error('not implemented');
return Promise.resolve(null!);
}
createFile(_resource: URI, _content?: VSBuffer | VSBufferReadable, _options?: ICreateFileOptions): Promise<IFileStatWithMetadata> {
throw new Error('not implemented');
return Promise.resolve(null!);
}
createFolder(_resource: URI): Promise<IFileStatWithMetadata> {
@@ -1164,15 +1170,6 @@ export class TestBackupFileService implements IBackupFileService {
return false;
}
async loadBackupResource(resource: URI): Promise<URI | undefined> {
const hasBackup = await this.hasBackup(resource);
if (hasBackup) {
return this.toBackupResource(resource);
}
return undefined;
}
registerResourceForBackup(_resource: URI): Promise<void> {
return Promise.resolve();
}
@@ -1181,15 +1178,11 @@ export class TestBackupFileService implements IBackupFileService {
return Promise.resolve();
}
toBackupResource(_resource: URI): URI {
throw new Error('not implemented');
}
backupResource<T extends object>(_resource: URI, _content: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
backup<T extends object>(_resource: URI, _content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> {
return Promise.resolve();
}
getWorkspaceFileBackups(): Promise<URI[]> {
getBackups(): Promise<URI[]> {
return Promise.resolve([]);
}
@@ -1200,19 +1193,11 @@ export class TestBackupFileService implements IBackupFileService {
return textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined);
}
resolveBackupContent<T extends object>(_backup: URI): Promise<IResolvedBackup<T>> {
throw new Error('not implemented');
resolve<T extends object>(_backup: URI): Promise<IResolvedBackup<T> | undefined> {
return Promise.resolve(undefined);
}
discardResourceBackup(_resource: URI): Promise<void> {
return Promise.resolve();
}
didDiscardAllWorkspaceBackups = false;
discardAllWorkspaceBackups(): Promise<void> {
this.didDiscardAllWorkspaceBackups = true;
discardBackup(_resource: URI): Promise<void> {
return Promise.resolve();
}
}
@@ -1238,6 +1223,7 @@ export class TestCodeEditorService implements ICodeEditorService {
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); }
}