diff --git a/src/editor/model.ts b/src/editor/model.ts index 65058c68cb5..78c749c94e5 100644 --- a/src/editor/model.ts +++ b/src/editor/model.ts @@ -99,10 +99,10 @@ export default class EditorModel { private insertPart(index: number, part: Part): void { this._parts.splice(index, 0, part); - if (this.activePartIdx && this.activePartIdx >= index) { + if (this.activePartIdx !== null && this.activePartIdx >= index) { ++this.activePartIdx; } - if (this.autoCompletePartIdx && this.autoCompletePartIdx >= index) { + if (this.autoCompletePartIdx !== null && this.autoCompletePartIdx >= index) { ++this.autoCompletePartIdx; } } @@ -111,12 +111,12 @@ export default class EditorModel { this._parts.splice(index, 1); if (index === this.activePartIdx) { this.activePartIdx = null; - } else if (this.activePartIdx && this.activePartIdx > index) { + } else if (this.activePartIdx !== null && this.activePartIdx > index) { --this.activePartIdx; } if (index === this.autoCompletePartIdx) { this.autoCompletePartIdx = null; - } else if (this.autoCompletePartIdx && this.autoCompletePartIdx > index) { + } else if (this.autoCompletePartIdx !== null && this.autoCompletePartIdx > index) { --this.autoCompletePartIdx; } } diff --git a/test/editor/mock.ts b/test/editor/mock.ts index 931e3cb5b3c..d1fcc45e96a 100644 --- a/test/editor/mock.ts +++ b/test/editor/mock.ts @@ -21,7 +21,7 @@ import { Caret } from "../../src/editor/caret"; import { PillPart, Part, PartCreator } from "../../src/editor/parts"; import DocumentPosition from "../../src/editor/position"; -class MockAutoComplete { +export class MockAutoComplete { public _updateCallback; public _partCreator; public _completions; @@ -44,7 +44,7 @@ class MockAutoComplete { }); if (matches.length === 1 && this._part && this._part.text.length > 1) { const match = matches[0]; - let pill; + let pill: PillPart; if (match.resourceId[0] === "@") { pill = this._partCreator.userPill(match.text, match.resourceId); } else { diff --git a/test/editor/model-test.ts b/test/editor/model-test.ts index cb475f696b2..89d88c1d38e 100644 --- a/test/editor/model-test.ts +++ b/test/editor/model-test.ts @@ -15,7 +15,7 @@ limitations under the License. */ import EditorModel from "../../src/editor/model"; -import { createPartCreator, createRenderer } from "./mock"; +import { createPartCreator, createRenderer, MockAutoComplete } from "./mock"; import DocumentOffset from "../../src/editor/offset"; import { PillPart } from "../../src/editor/parts"; import DocumentPosition from "../../src/editor/position"; @@ -186,8 +186,7 @@ describe("editor/model", function () { expect(model.parts[1].text).toBe("@a"); // this is a hacky mock function - // @ts-ignore - model.autoComplete.tryComplete(); // see MockAutoComplete + (model.autoComplete as unknown as MockAutoComplete).tryComplete(); expect(renderer.count).toBe(2); expect((renderer.caret as DocumentPosition).index).toBe(1); @@ -216,8 +215,7 @@ describe("editor/model", function () { expect(model.parts[1].text).toBe("#r"); // this is a hacky mock function - // @ts-ignore - model.autoComplete.tryComplete(); // see MockAutoComplete + (model.autoComplete as unknown as MockAutoComplete).tryComplete(); expect(renderer.count).toBe(2); expect((renderer.caret as DocumentPosition).index).toBe(1); @@ -236,8 +234,7 @@ describe("editor/model", function () { model.update("hello #r", "insertText", new DocumentOffset(8, true)); // this is a hacky mock function - // @ts-ignore - model.autoComplete.tryComplete(); // see MockAutoComplete + (model.autoComplete as unknown as MockAutoComplete).tryComplete(); model.update("hello #riot-dev!!", "insertText", new DocumentOffset(17, true)); expect(renderer.count).toBe(3); @@ -314,6 +311,45 @@ describe("editor/model", function () { expect(model.parts[0].type).toBe("plain"); expect(model.parts[0].text).toBe("foo@a"); }); + + it("should allow auto-completing multiple times with resets between them", () => { + const renderer = createRenderer(); + const pc = createPartCreator([{ resourceId: "#riot-dev" } as PillPart]); + const model = new EditorModel([pc.plain("")], pc, renderer); + + model.update("#r", "insertText", new DocumentOffset(8, true)); + + expect(renderer.count).toBe(1); + expect((renderer.caret as DocumentPosition).index).toBe(0); + expect((renderer.caret as DocumentPosition).offset).toBe(2); + expect(model.parts.length).toBe(1); + expect(model.parts[0].type).toBe("pill-candidate"); + expect(model.parts[0].text).toBe("#r"); + + // this is a hacky mock function + (model.autoComplete as unknown as MockAutoComplete).tryComplete(); + + expect(renderer.count).toBe(2); + expect((renderer.caret as DocumentPosition).index).toBe(0); + expect((renderer.caret as DocumentPosition).offset).toBe(9); + expect(model.parts.length).toBe(1); + expect(model.parts[0].type).toBe("room-pill"); + expect(model.parts[0].text).toBe("#riot-dev"); + + model.reset([]); + model.update("#r", "insertText", new DocumentOffset(8, true)); + + expect(model.parts.length).toBe(1); + expect(model.parts[0].type).toBe("pill-candidate"); + expect(model.parts[0].text).toBe("#r"); + + // this is a hacky mock function + (model.autoComplete as unknown as MockAutoComplete).tryComplete(); + + expect(model.parts.length).toBe(1); + expect(model.parts[0].type).toBe("room-pill"); + expect(model.parts[0].text).toBe("#riot-dev"); + }); }); describe("emojis", function () { it("regional emojis should be separated to prevent them to be converted to flag", () => {