interface MarkdownLinkRange { start: number end: number text: string url: string } const MARKDOWN_LINK_PATTERN = /\[([^\]\n]*)\]\(([^)\n]*)\)/g export function findMarkdownLinkRange( markdown: string, from: number, to: number ): MarkdownLinkRange | null { MARKDOWN_LINK_PATTERN.lastIndex = 0 for (const match of markdown.matchAll(MARKDOWN_LINK_PATTERN)) { const start = match.index if (start > 0 && markdown[start - 1] === '!') continue const fullMatch = match[0] const text = match[1] const url = match[2] const textStart = start + 1 const textEnd = textStart + text.length const urlStart = textEnd + 2 const urlEnd = urlStart + url.length const end = start + fullMatch.length const cursorInside = from === to && from >= start && from <= end const selectionInsideText = from >= textStart && to <= textEnd const selectionInsideUrl = from >= urlStart && to <= urlEnd const selectionCoversLink = from <= start && to >= end if (cursorInside || selectionInsideText || selectionInsideUrl || selectionCoversLink) { return { start, end, text, url } } } return null }