/** * Utility functions for the select signal dialog search, filtering and tree building. * * The dialog may show databases with thousands of messages and tens of thousands of signals, so all * search work is split into two steps: an index that is built once per set of databases and a match * pass that runs per search term. The match pass never lower cases a database name again and visits * every message and signal exactly once. * * @packageDocumentation */ import { IMessageDatabase } from '../hooks'; import { FrameDefinition, SignalDefinition } from '../models'; /** * Maximum number of signals that a search is allowed to expand automatically. * * Every expanded message mounts one tree item per matching signal. A short search term matches * almost everything in a large database, so expanding all matches would mount tens of thousands of * tree items and make the search field unusable. When the matches exceed this limit, the matching * messages are still listed but left collapsed for the user to expand. * * @internal */ export declare const MAX_AUTO_EXPANDED_SIGNALS = 500; /** * A signal with its name pre-lowercased for searching. * @internal */ export interface IndexedSignal { /** The signal definition. */ signal: SignalDefinition; /** The lower cased signal name. */ searchName: string; } /** * A message with its name pre-lowercased for searching, including its indexed signals. * @internal */ export interface IndexedMessage { /** The message definition. */ message: FrameDefinition; /** The lower cased message name. */ searchName: string; /** The indexed signals of the message, duplicate qualified names excluded. */ signals: IndexedSignal[]; /** The signals to show when the message name itself matches, i.e. {@link signals} undecorated. */ allSignals: SignalDefinition[]; } /** * A database with all its messages indexed for searching. * @internal */ export interface IndexedDatabase { /** The database the messages belong to. */ database: IMessageDatabase; /** The indexed messages of the database, messages without signals excluded. */ messages: IndexedMessage[]; } /** * A message that matches the current search term, together with the signals to show for it. * @internal */ export interface MessageMatch { /** The matching message definition. */ message: FrameDefinition; /** The signals to show, all signals of the message when the message name itself matches. */ signals: SignalDefinition[]; } /** * The result of matching all indexed databases against a search term. * @internal */ export interface MessageMatches { /** The matching messages per database, keyed by the database object of the index. */ matchesByDatabase: Map; /** The total number of signals in all matches, used to decide whether to auto expand. */ signalCount: number; } /** * A database node of the dialog tree. * @internal */ export interface TreeDatabase { /** The name of the database, also used as a part of the tree item identifier. */ name: string; /** The messages to show below the database node. */ messages: MessageMatch[]; } /** * A source node of the dialog tree. * @internal */ export interface TreeSource { /** The identifier of the source, also used as the tree item identifier. */ sourceId: string; /** The display name of the source. */ sourceName: string; /** The databases to show below the source node. */ databases: TreeDatabase[]; } /** * One item of the tree view, as the data a RichTreeView takes rather than a rendered tree item. * @internal */ export interface SignalTreeItem { /** The tree item identifier, `sourceId` for a source and `sourceId/qualifiedName` below it. */ id: string; /** The text the item shows. */ label: string; /** * False for the source and database nodes, which only organize the tree and are not themselves * selectable. */ selectable: boolean; /** The items below this one, left out for a signal. */ children?: SignalTreeItem[]; } /** * Builds a search index over the given databases. * * Lower casing every message and signal name once here keeps the per keystroke match pass down to * plain substring comparisons. * * Messages and signals whose qualified name repeats one already indexed are dropped. The tree gives * an item the id `sourceId/qualifiedName`, and the tree view throws when two items claim the same id, * so a database that reports the same message twice - or a message that reports the same signal twice, * which read-only signals of a J1939 database have been seen to do - would otherwise take the whole * dialog down rather than show a duplicated row. * * @param databases - The databases to index. * @returns One indexed database per input database, messages without signals and duplicates excluded. * @internal */ export declare function buildSearchIndex(databases: IMessageDatabase[]): IndexedDatabase[]; /** * Matches all indexed databases against a search term. * * A message matches when its own name contains the search term, in which case all its signals are * shown, or when at least one of its signals matches, in which case only the matching signals are * shown. An empty search term matches everything. * * @param index - The search index built by {@link buildSearchIndex}. * @param searchTerm - The lower cased search term. * @returns The matching messages per database and the total number of matching signals. * @internal */ export declare function matchMessages(index: IndexedDatabase[], searchTerm: string): MessageMatches; /** * Builds the source and database nodes of the dialog tree. * * The message matches are shared between all sources using the same database, so a database that is * used by several sources is only searched once. * * @param sourcesByIdentifier - The databases of each source, keyed by source identifier. * @param sourceIdFilter - Optional source identifier to limit the tree to a single source. * @param matches - The matches returned by {@link matchMessages}. * @returns The source nodes to render, each with its database nodes. * @internal */ export declare function buildTreeSources(sourcesByIdentifier: Map, sourceIdFilter: string | undefined, matches: MessageMatches): TreeSource[]; /** * Turns the tree sources into the item data the tree view renders from. * * The tree view is given data rather than rendered tree items so that a search only has to rebuild * plain objects, and so that only the items the user has expanded are ever mounted. Building one * React element per message and signal instead meant paying for the whole match set on every * keystroke, however little of it was on screen. * * @param treeSources - The tree sources built by {@link buildTreeSources}. * @returns One item per source, with the database, message and signal items below it. * @internal */ export declare function buildTreeItems(treeSources: TreeSource[]): SignalTreeItem[]; /** * Gets the tree items that a search should expand automatically. * * Sources and databases holding matches are always expanded. The matching messages are only expanded * while the number of matching signals stays within {@link MAX_AUTO_EXPANDED_SIGNALS}. * * @param treeSources - The tree sources built by {@link buildTreeSources}. * @param searchTerm - The lower cased search term. An empty term expands nothing. * @param signalCount - The total number of matching signals, from {@link matchMessages}. * @returns The identifiers of the tree items to expand. * @internal */ export declare function getAutoExpandedItems(treeSources: TreeSource[], searchTerm: string, signalCount: number): string[];