Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import type { Node as PMNode } from '@tiptap/pm/model'
|
|
|
|
import { createSearchRegex } from './regex.js'
|
|
import { searchTextblock } from './textblock-search.js'
|
|
import type { SearchOptions, SearchResult, TextblockSearchTarget } from './types.js'
|
|
|
|
export type { SearchOptions, SearchResult, TextblockSearchTarget } from './types.js'
|
|
export { createSearchRegex } from './regex.js'
|
|
|
|
/**
|
|
* Searches textblocks for a term and returns their matched ranges.
|
|
* @param textblocks The textblocks and their document positions.
|
|
* @param term The search term, treated as regex source when `useRegex` is enabled.
|
|
* @param options Case sensitivity, regex mode, and whole word matching.
|
|
* @returns The list of matched ranges in textblock order.
|
|
*/
|
|
export function searchTextblocks(
|
|
textblocks: readonly TextblockSearchTarget[],
|
|
term: string,
|
|
options: SearchOptions,
|
|
): SearchResult[] {
|
|
const regex = createSearchRegex(term, options)
|
|
|
|
if (!regex) {
|
|
return []
|
|
}
|
|
|
|
return textblocks.flatMap(({ node, pos }) => searchTextblock(regex, node, pos))
|
|
}
|
|
|
|
/**
|
|
* Searches all textblocks of a document for a term and returns the matched ranges.
|
|
* Matches may span multiple text nodes (e.g. across marks), but never leave a textblock.
|
|
* @param doc The document to search in.
|
|
* @param term The search term, treated as regex source when `useRegex` is enabled.
|
|
* @param options Case sensitivity, regex mode, and whole word matching.
|
|
* @returns The list of matched ranges in document order.
|
|
*/
|
|
export function searchDocument(doc: PMNode, term: string, options: SearchOptions): SearchResult[] {
|
|
const textblocks: TextblockSearchTarget[] = []
|
|
|
|
doc.descendants((node, pos) => {
|
|
if (!node.isTextblock) {
|
|
return true
|
|
}
|
|
|
|
textblocks.push({ node, pos })
|
|
|
|
return false
|
|
})
|
|
|
|
return searchTextblocks(textblocks, term, options)
|
|
}
|
|
|
|
/**
|
|
* Finds the index of the first result at or after a position, wrapping around
|
|
* to the first result when the position is behind the last result.
|
|
* @param results The current search results.
|
|
* @param from The position to search from.
|
|
* @returns The result index, or `null` when there are no results.
|
|
*/
|
|
export function findNextIndex(results: SearchResult[], from: number): number | null {
|
|
if (results.length === 0) {
|
|
return null
|
|
}
|
|
|
|
const index = results.findIndex(result => result.from >= from)
|
|
|
|
return index === -1 ? 0 : index
|
|
}
|