2017-06-01 15:18:06 +01:00
|
|
|
/*
|
2024-09-09 14:57:16 +01:00
|
|
|
Copyright 2017-2024 New Vector Ltd.
|
2017-06-01 17:29:40 +01:00
|
|
|
Copyright 2016 Aviral Dasgupta
|
2017-06-01 15:18:06 +01:00
|
|
|
|
2025-01-06 11:18:54 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
2024-09-09 14:57:16 +01:00
|
|
|
Please see LICENSE files in the repository root for full details.
|
2017-06-01 15:18:06 +01:00
|
|
|
*/
|
|
|
|
|
|
2021-06-18 16:13:55 +01:00
|
|
|
import { ReactElement } from "react";
|
2023-08-04 08:36:16 +01:00
|
|
|
import { Room } from "matrix-js-sdk/src/matrix";
|
2021-06-18 16:13:55 +01:00
|
|
|
|
2016-06-01 16:54:21 +05:30
|
|
|
import CommandProvider from "./CommandProvider";
|
2016-06-12 17:02:46 +05:30
|
|
|
import RoomProvider from "./RoomProvider";
|
|
|
|
|
import UserProvider from "./UserProvider";
|
2016-06-17 04:58:09 +05:30
|
|
|
import EmojiProvider from "./EmojiProvider";
|
2017-11-02 18:54:25 +00:00
|
|
|
import NotifProvider from "./NotifProvider";
|
2021-06-18 16:13:55 +01:00
|
|
|
import { timeout } from "../utils/promise";
|
2021-06-29 13:11:58 +01:00
|
|
|
import AutocompleteProvider, { ICommand } from "./AutocompleteProvider";
|
2021-05-18 09:39:01 +01:00
|
|
|
import SpaceProvider from "./SpaceProvider";
|
2021-11-18 12:47:11 +00:00
|
|
|
import { TimelineRenderingType } from "../contexts/RoomContext";
|
2023-02-24 15:28:40 +00:00
|
|
|
import { filterBoolean } from "../utils/arrays";
|
2016-09-13 15:41:52 +05:30
|
|
|
|
2020-04-20 19:00:54 +01:00
|
|
|
export interface ISelectionRange {
|
|
|
|
|
beginning?: boolean; // whether the selection is in the first block of the editor or not
|
|
|
|
|
start: number; // byte offset relative to the start anchor of the current editor selection.
|
|
|
|
|
end: number; // byte offset relative to the end anchor of the current editor selection.
|
|
|
|
|
}
|
2016-09-13 15:41:52 +05:30
|
|
|
|
2020-04-20 19:00:54 +01:00
|
|
|
export interface ICompletion {
|
2022-11-30 11:32:56 +00:00
|
|
|
type?: "at-room" | "command" | "community" | "room" | "user";
|
2020-06-18 14:32:43 +01:00
|
|
|
completion: string;
|
2020-04-20 19:00:54 +01:00
|
|
|
completionId?: string;
|
2023-04-17 08:31:58 +01:00
|
|
|
component: ReactElement;
|
2020-06-18 14:32:43 +01:00
|
|
|
range: ISelectionRange;
|
|
|
|
|
command?: string;
|
2020-04-20 19:00:54 +01:00
|
|
|
suffix?: string;
|
2017-07-20 16:49:23 +01:00
|
|
|
// If provided, apply a LINK entity to the completion with the
|
|
|
|
|
// data = { url: href }.
|
2020-06-18 14:32:43 +01:00
|
|
|
href?: string;
|
2020-04-20 19:00:54 +01:00
|
|
|
}
|
2016-06-01 16:54:21 +05:30
|
|
|
|
2016-06-12 17:02:46 +05:30
|
|
|
const PROVIDERS = [UserProvider, RoomProvider, EmojiProvider, NotifProvider, CommandProvider, SpaceProvider];
|
2016-06-01 16:54:21 +05:30
|
|
|
|
2016-09-13 15:41:52 +05:30
|
|
|
// Providers will get rejected if they take longer than this.
|
|
|
|
|
const PROVIDER_COMPLETION_TIMEOUT = 3000;
|
|
|
|
|
|
2020-04-20 19:00:54 +01:00
|
|
|
export interface IProviderCompletions {
|
|
|
|
|
completions: ICompletion[];
|
|
|
|
|
provider: AutocompleteProvider;
|
2023-02-24 15:28:40 +00:00
|
|
|
command: Partial<ICommand>;
|
2020-04-20 19:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
2017-11-02 17:51:08 +00:00
|
|
|
export default class Autocompleter {
|
2022-12-16 12:29:59 +00:00
|
|
|
public room: Room;
|
|
|
|
|
public providers: AutocompleteProvider[];
|
2020-04-20 19:00:54 +01:00
|
|
|
|
2022-12-16 12:29:59 +00:00
|
|
|
public constructor(room: Room, renderingType: TimelineRenderingType = TimelineRenderingType.Room) {
|
2017-11-02 17:51:08 +00:00
|
|
|
this.room = room;
|
2019-01-11 13:54:11 +00:00
|
|
|
this.providers = PROVIDERS.map((Prov) => {
|
2021-11-18 12:47:11 +00:00
|
|
|
return new Prov(room, renderingType);
|
2017-11-02 17:51:08 +00:00
|
|
|
});
|
|
|
|
|
}
|
2016-09-13 15:41:52 +05:30
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
public destroy(): void {
|
2017-11-02 17:51:08 +00:00
|
|
|
this.providers.forEach((p) => {
|
|
|
|
|
p.destroy();
|
|
|
|
|
});
|
|
|
|
|
}
|
2016-09-13 15:41:52 +05:30
|
|
|
|
2022-12-16 12:29:59 +00:00
|
|
|
public async getCompletions(
|
2021-05-12 12:18:56 +01:00
|
|
|
query: string,
|
|
|
|
|
selection: ISelectionRange,
|
|
|
|
|
force = false,
|
|
|
|
|
limit = -1,
|
|
|
|
|
): Promise<IProviderCompletions[]> {
|
2017-11-02 18:11:18 +00:00
|
|
|
/* Note: This intentionally waits for all providers to return,
|
2017-11-02 17:51:08 +00:00
|
|
|
otherwise, we run into a condition where new completions are displayed
|
|
|
|
|
while the user is interacting with the list, which makes it difficult
|
|
|
|
|
to predict whether an action will actually do what is intended
|
|
|
|
|
*/
|
2020-04-20 19:00:54 +01:00
|
|
|
// list of results from each provider, each being a list of completions or null if it times out
|
2023-02-03 15:27:47 +00:00
|
|
|
const completionsList: Array<ICompletion[] | null> = await Promise.all(
|
2023-01-12 13:25:14 +00:00
|
|
|
this.providers.map(async (provider): Promise<ICompletion[] | null> => {
|
2022-05-03 22:04:37 +01:00
|
|
|
return timeout(
|
2021-05-12 12:18:56 +01:00
|
|
|
provider.getCompletions(query, selection, force, limit),
|
|
|
|
|
null,
|
|
|
|
|
PROVIDER_COMPLETION_TIMEOUT,
|
|
|
|
|
);
|
2019-11-14 13:52:17 +00:00
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// map then filter to maintain the index for the map-operation, for this.providers to line up
|
2023-02-24 15:28:40 +00:00
|
|
|
return filterBoolean(
|
|
|
|
|
completionsList.map((completions, i) => {
|
2019-11-14 13:52:17 +00:00
|
|
|
if (!completions || !completions.length) return;
|
2017-11-02 17:51:08 +00:00
|
|
|
|
|
|
|
|
return {
|
2019-11-14 13:52:17 +00:00
|
|
|
completions,
|
2017-11-02 17:51:08 +00:00
|
|
|
provider: this.providers[i],
|
|
|
|
|
|
|
|
|
|
/* the currently matched "command" the completer tried to complete
|
|
|
|
|
* we pass this through so that Autocomplete can figure out when to
|
|
|
|
|
* re-show itself once hidden.
|
|
|
|
|
*/
|
|
|
|
|
command: this.providers[i].getCurrentCommand(query, selection, force),
|
|
|
|
|
};
|
2023-02-24 15:28:40 +00:00
|
|
|
}),
|
|
|
|
|
);
|
2017-11-02 17:51:08 +00:00
|
|
|
}
|
2016-06-01 16:54:21 +05:30
|
|
|
}
|