Fix autocomplete not resetting properly on message send (#10741)

This commit is contained in:
Michael Telatynski 2023-04-28 14:31:02 +01:00 committed by GitHub
parent 0d1020c66f
commit f819853cad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 13 deletions

View file

@ -99,10 +99,10 @@ export default class EditorModel {
private insertPart(index: number, part: Part): void { private insertPart(index: number, part: Part): void {
this._parts.splice(index, 0, part); this._parts.splice(index, 0, part);
if (this.activePartIdx && this.activePartIdx >= index) { if (this.activePartIdx !== null && this.activePartIdx >= index) {
++this.activePartIdx; ++this.activePartIdx;
} }
if (this.autoCompletePartIdx && this.autoCompletePartIdx >= index) { if (this.autoCompletePartIdx !== null && this.autoCompletePartIdx >= index) {
++this.autoCompletePartIdx; ++this.autoCompletePartIdx;
} }
} }
@ -111,12 +111,12 @@ export default class EditorModel {
this._parts.splice(index, 1); this._parts.splice(index, 1);
if (index === this.activePartIdx) { if (index === this.activePartIdx) {
this.activePartIdx = null; this.activePartIdx = null;
} else if (this.activePartIdx && this.activePartIdx > index) { } else if (this.activePartIdx !== null && this.activePartIdx > index) {
--this.activePartIdx; --this.activePartIdx;
} }
if (index === this.autoCompletePartIdx) { if (index === this.autoCompletePartIdx) {
this.autoCompletePartIdx = null; this.autoCompletePartIdx = null;
} else if (this.autoCompletePartIdx && this.autoCompletePartIdx > index) { } else if (this.autoCompletePartIdx !== null && this.autoCompletePartIdx > index) {
--this.autoCompletePartIdx; --this.autoCompletePartIdx;
} }
} }

View file

@ -21,7 +21,7 @@ import { Caret } from "../../src/editor/caret";
import { PillPart, Part, PartCreator } from "../../src/editor/parts"; import { PillPart, Part, PartCreator } from "../../src/editor/parts";
import DocumentPosition from "../../src/editor/position"; import DocumentPosition from "../../src/editor/position";
class MockAutoComplete { export class MockAutoComplete {
public _updateCallback; public _updateCallback;
public _partCreator; public _partCreator;
public _completions; public _completions;
@ -44,7 +44,7 @@ class MockAutoComplete {
}); });
if (matches.length === 1 && this._part && this._part.text.length > 1) { if (matches.length === 1 && this._part && this._part.text.length > 1) {
const match = matches[0]; const match = matches[0];
let pill; let pill: PillPart;
if (match.resourceId[0] === "@") { if (match.resourceId[0] === "@") {
pill = this._partCreator.userPill(match.text, match.resourceId); pill = this._partCreator.userPill(match.text, match.resourceId);
} else { } else {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import EditorModel from "../../src/editor/model"; import EditorModel from "../../src/editor/model";
import { createPartCreator, createRenderer } from "./mock"; import { createPartCreator, createRenderer, MockAutoComplete } from "./mock";
import DocumentOffset from "../../src/editor/offset"; import DocumentOffset from "../../src/editor/offset";
import { PillPart } from "../../src/editor/parts"; import { PillPart } from "../../src/editor/parts";
import DocumentPosition from "../../src/editor/position"; import DocumentPosition from "../../src/editor/position";
@ -186,8 +186,7 @@ describe("editor/model", function () {
expect(model.parts[1].text).toBe("@a"); expect(model.parts[1].text).toBe("@a");
// this is a hacky mock function // this is a hacky mock function
// @ts-ignore (model.autoComplete as unknown as MockAutoComplete).tryComplete();
model.autoComplete.tryComplete(); // see MockAutoComplete
expect(renderer.count).toBe(2); expect(renderer.count).toBe(2);
expect((renderer.caret as DocumentPosition).index).toBe(1); expect((renderer.caret as DocumentPosition).index).toBe(1);
@ -216,8 +215,7 @@ describe("editor/model", function () {
expect(model.parts[1].text).toBe("#r"); expect(model.parts[1].text).toBe("#r");
// this is a hacky mock function // this is a hacky mock function
// @ts-ignore (model.autoComplete as unknown as MockAutoComplete).tryComplete();
model.autoComplete.tryComplete(); // see MockAutoComplete
expect(renderer.count).toBe(2); expect(renderer.count).toBe(2);
expect((renderer.caret as DocumentPosition).index).toBe(1); 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)); model.update("hello #r", "insertText", new DocumentOffset(8, true));
// this is a hacky mock function // this is a hacky mock function
// @ts-ignore (model.autoComplete as unknown as MockAutoComplete).tryComplete();
model.autoComplete.tryComplete(); // see MockAutoComplete
model.update("hello #riot-dev!!", "insertText", new DocumentOffset(17, true)); model.update("hello #riot-dev!!", "insertText", new DocumentOffset(17, true));
expect(renderer.count).toBe(3); expect(renderer.count).toBe(3);
@ -314,6 +311,45 @@ describe("editor/model", function () {
expect(model.parts[0].type).toBe("plain"); expect(model.parts[0].type).toBe("plain");
expect(model.parts[0].text).toBe("foo@a"); 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 () { describe("emojis", function () {
it("regional emojis should be separated to prevent them to be converted to flag", () => { it("regional emojis should be separated to prevent them to be converted to flag", () => {