Path: blob/main/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as assert from "assert";6import { DisposableStore } from "../../../../base/common/lifecycle.js";7import { IObservable, derivedHandleChanges } from "../../../../base/common/observable.js";8import { ensureNoDisposablesAreLeakedInTestSuite } from "../../../../base/test/common/utils.js";9import { ICodeEditor } from "../../../browser/editorBrowser.js";10import { ObservableCodeEditor, observableCodeEditor } from "../../../browser/observableCodeEditor.js";11import { Position } from "../../../common/core/position.js";12import { Range } from "../../../common/core/range.js";13import { ViewModel } from "../../../common/viewModel/viewModelImpl.js";14import { withTestCodeEditor } from "../testCodeEditor.js";1516suite("CodeEditorWidget", () => {17ensureNoDisposablesAreLeakedInTestSuite();1819function withTestFixture(20cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable<string> }) => void21) {22withEditorSetupTestFixture(undefined, cb);23}2425function withEditorSetupTestFixture(26preSetupCallback:27| ((editor: ICodeEditor, disposables: DisposableStore) => void)28| undefined,29cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable<string> }) => void30) {31withTestCodeEditor("hello world", {}, (editor, viewModel) => {32const disposables = new DisposableStore();33preSetupCallback?.(editor, disposables);34const obsEditor = observableCodeEditor(editor);35const log = new Log();3637const derived = derivedHandleChanges(38{39changeTracker: {40createChangeSummary: () => undefined,41handleChange: (context) => {42const obsName = observableName(context.changedObservable, obsEditor);4344log.log(`handle change: ${obsName} ${formatChange(context.change)}`);45return true;46},47},48},49(reader) => {50const versionId = obsEditor.versionId.read(reader);51const selection = obsEditor.selections.read(reader)?.map((s) => s.toString()).join(", ");52obsEditor.onDidType.read(reader);5354const str = `running derived: selection: ${selection}, value: ${versionId}`;55log.log(str);56return str;57}58);5960derived.recomputeInitiallyAndOnChange(disposables);61assert.deepStrictEqual(log.getAndClearEntries(), [62"running derived: selection: [1,1 -> 1,1], value: 1",63]);6465cb({ editor, viewModel, log, derived });6667disposables.dispose();68});69}7071test("setPosition", () =>72withTestFixture(({ editor, log }) => {73editor.setPosition(new Position(1, 2));7475assert.deepStrictEqual(log.getAndClearEntries(), ([76"handle change: editor.selections {\"selection\":\"[1,2 -> 1,2]\",\"modelVersionId\":1,\"oldSelections\":[\"[1,1 -> 1,1]\"],\"oldModelVersionId\":1,\"source\":\"api\",\"reason\":0}",77"running derived: selection: [1,2 -> 1,2], value: 1"78]));79}));8081test("keyboard.type", () =>82withTestFixture(({ editor, log }) => {83editor.trigger("keyboard", "type", { text: "abc" });8485assert.deepStrictEqual(log.getAndClearEntries(), ([86"handle change: editor.onDidType \"abc\"",87"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,1 -> 1,1]\",\"rangeLength\":0,\"text\":\"a\",\"rangeOffset\":0}],\"eol\":\"\\n\",\"versionId\":2,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",88"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,2 -> 1,2]\",\"rangeLength\":0,\"text\":\"b\",\"rangeOffset\":1}],\"eol\":\"\\n\",\"versionId\":3,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",89"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,3 -> 1,3]\",\"rangeLength\":0,\"text\":\"c\",\"rangeOffset\":2}],\"eol\":\"\\n\",\"versionId\":4,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",90"handle change: editor.selections {\"selection\":\"[1,4 -> 1,4]\",\"modelVersionId\":4,\"oldSelections\":[\"[1,1 -> 1,1]\"],\"oldModelVersionId\":1,\"source\":\"keyboard\",\"reason\":0}",91"running derived: selection: [1,4 -> 1,4], value: 4"92]));93}));9495test("keyboard.type and set position", () =>96withTestFixture(({ editor, log }) => {97editor.trigger("keyboard", "type", { text: "abc" });9899assert.deepStrictEqual(log.getAndClearEntries(), ([100"handle change: editor.onDidType \"abc\"",101"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,1 -> 1,1]\",\"rangeLength\":0,\"text\":\"a\",\"rangeOffset\":0}],\"eol\":\"\\n\",\"versionId\":2,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",102"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,2 -> 1,2]\",\"rangeLength\":0,\"text\":\"b\",\"rangeOffset\":1}],\"eol\":\"\\n\",\"versionId\":3,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",103"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,3 -> 1,3]\",\"rangeLength\":0,\"text\":\"c\",\"rangeOffset\":2}],\"eol\":\"\\n\",\"versionId\":4,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",104"handle change: editor.selections {\"selection\":\"[1,4 -> 1,4]\",\"modelVersionId\":4,\"oldSelections\":[\"[1,1 -> 1,1]\"],\"oldModelVersionId\":1,\"source\":\"keyboard\",\"reason\":0}",105"running derived: selection: [1,4 -> 1,4], value: 4"106]));107108editor.setPosition(new Position(1, 5), "test");109110assert.deepStrictEqual(log.getAndClearEntries(), ([111"handle change: editor.selections {\"selection\":\"[1,5 -> 1,5]\",\"modelVersionId\":4,\"oldSelections\":[\"[1,4 -> 1,4]\"],\"oldModelVersionId\":4,\"source\":\"test\",\"reason\":0}",112"running derived: selection: [1,5 -> 1,5], value: 4"113]));114}));115116test("listener interaction (unforced)", () => {117let derived: IObservable<string>;118let log: Log;119withEditorSetupTestFixture(120(editor, disposables) => {121disposables.add(122editor.onDidChangeModelContent(() => {123log.log(">>> before get");124derived.get();125log.log("<<< after get");126})127);128},129(args) => {130const editor = args.editor;131derived = args.derived;132log = args.log;133134editor.trigger("keyboard", "type", { text: "a" });135assert.deepStrictEqual(log.getAndClearEntries(), ([136">>> before get",137"<<< after get",138"handle change: editor.onDidType \"a\"",139"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,1 -> 1,1]\",\"rangeLength\":0,\"text\":\"a\",\"rangeOffset\":0}],\"eol\":\"\\n\",\"versionId\":2,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",140"handle change: editor.selections {\"selection\":\"[1,2 -> 1,2]\",\"modelVersionId\":2,\"oldSelections\":[\"[1,1 -> 1,1]\"],\"oldModelVersionId\":1,\"source\":\"keyboard\",\"reason\":0}",141"running derived: selection: [1,2 -> 1,2], value: 2"142]));143}144);145});146147test("listener interaction ()", () => {148let derived: IObservable<string>;149let log: Log;150withEditorSetupTestFixture(151(editor, disposables) => {152disposables.add(153editor.onDidChangeModelContent(() => {154log.log(">>> before forceUpdate");155observableCodeEditor(editor).forceUpdate();156157log.log(">>> before get");158derived.get();159log.log("<<< after get");160})161);162},163(args) => {164const editor = args.editor;165derived = args.derived;166log = args.log;167168editor.trigger("keyboard", "type", { text: "a" });169170assert.deepStrictEqual(log.getAndClearEntries(), ([171">>> before forceUpdate",172">>> before get",173"handle change: editor.versionId undefined",174"running derived: selection: [1,2 -> 1,2], value: 2",175"<<< after get",176"handle change: editor.onDidType \"a\"",177"handle change: editor.versionId {\"changes\":[{\"range\":\"[1,1 -> 1,1]\",\"rangeLength\":0,\"text\":\"a\",\"rangeOffset\":0}],\"eol\":\"\\n\",\"versionId\":2,\"detailedReasons\":[{\"metadata\":{\"source\":\"cursor\",\"kind\":\"type\",\"detailedSource\":\"keyboard\"}}],\"detailedReasonsChangeLengths\":[1]}",178"handle change: editor.selections {\"selection\":\"[1,2 -> 1,2]\",\"modelVersionId\":2,\"oldSelections\":[\"[1,1 -> 1,1]\"],\"oldModelVersionId\":1,\"source\":\"keyboard\",\"reason\":0}",179"running derived: selection: [1,2 -> 1,2], value: 2"180]));181}182);183});184});185186class Log {187private readonly entries: string[] = [];188public log(message: string): void {189this.entries.push(message);190}191192public getAndClearEntries(): string[] {193const entries = [...this.entries];194this.entries.length = 0;195return entries;196}197}198199function formatChange(change: unknown) {200return JSON.stringify(201change,202(key, value) => {203if (value instanceof Range) {204return value.toString();205}206if (207value === false ||208(Array.isArray(value) && value.length === 0)209) {210return undefined;211}212return value;213}214);215}216217function observableName(obs: IObservable<any>, obsEditor: ObservableCodeEditor): string {218switch (obs) {219case obsEditor.selections:220return "editor.selections";221case obsEditor.versionId:222return "editor.versionId";223case obsEditor.onDidType:224return "editor.onDidType";225default:226return "unknown";227}228}229230231