import { isNumber, isString } from '@sindresorhus/is'; import { parse } from './parser.ts'; import type { Fragment, FragmentPath, FragmentUpdater } from './types.ts'; export function findCodeFragment( input: string, path: FragmentPath, ): Fragment | null { const parsed = parse(input); if (!parsed) { return null; } const [ruleIndex, ...restPath] = path; let fragment: Fragment | undefined = parsed[ruleIndex]; for (const key of restPath) { if (!fragment) { break; } if (fragment.type === 'array' && isNumber(key)) { fragment = fragment.children[key]; } if (fragment.type === 'record' && isString(key)) { fragment = fragment.children[key]; } } return fragment ?? null; } export function patchCodeAtFragment( input: string, fragment: Fragment, updater: FragmentUpdater, ): string { const { value, offset } = fragment; const left = input.slice(0, offset); const right = input.slice(offset + value.length); return isString(updater) ? `${left}${updater}${right}` : `${left}${updater(value)}${right}`; } export function patchCodeAtFragments( input: string, fragments: Fragment[], updater: FragmentUpdater, ): string { const sortedFragments = fragments.sort( ({ offset: a }, { offset: b }) => b - a, ); let result = input; for (const fragment of sortedFragments) { result = patchCodeAtFragment(result, fragment, updater); } return result; } export function updateCode( input: string, path: FragmentPath, updater: FragmentUpdater, ): string { const fragment = findCodeFragment(input, path); if (!fragment) { return input; } return patchCodeAtFragment(input, fragment, updater); }