120 lines · 3.8 KB · TypeScript
RawDownload
import {  fileBasename,  formatFilePathPosition,  inlineCodeFilePathCandidate,  isRelativeFilePath,  normalizeMarkdownLinkDestination,  parseFileUrlHref,  parseMarkdownFileLink,  safeDecodeURIComponent,  splitFilePathPosition,  workspaceRelativeFilePath,} from "@t3tools/client-runtime/markdown-links"; import { formatWorkspaceRelativePath } from "./filePathDisplay";import { isTerminalLinkActivation, resolvePathLinkTarget } from "./terminal-links"; export { normalizeMarkdownLinkDestination }; const MARKDOWN_LINK_HREF_PATTERN =  /\[[^\]]*]\(\s*(?:<([^>\n]+)>|([^\s)]+))(?:\s+["'][^"']*["'])?\s*\)/g; export interface MarkdownFileLinkMeta {  filePath: string;  targetPath: string;  displayPath: string;  workspaceRelativePath: string | null;  basename: string;  line?: number;  column?: number;} export function extractMarkdownLinkHrefs(markdown: string): string[] {  const hrefs: string[] = [];  for (const match of markdown.matchAll(MARKDOWN_LINK_HREF_PATTERN)) {    const href = (match[1] ?? match[2])?.trim();    if (href) hrefs.push(href);  }  return hrefs;} export function shouldOpenMarkdownFileLinkInEditor(  event: Pick<MouseEvent, "metaKey" | "ctrlKey">,  platform?: string,): boolean {  return isTerminalLinkActivation(event, platform);} export function shouldOpenMarkdownFileLinkInBrowserByDefault(path: string): boolean {  return /\.pdf$/i.test(path.split(/[?#]/, 1)[0] ?? "");} export function isWindowsDrivePathHref(href: string): boolean {  return /^[A-Za-z]:[\\/]/.test(safeDecodeURIComponent(href));} export function rewriteMarkdownFileUriHref(href: string | undefined): string | null {  if (!href) return null;  const target = parseFileUrlHref(normalizeMarkdownLinkDestination(href));  return target ? `${target.path}${target.hash}` : null;} /** * `baseDir` anchors relative links; it defaults to the workspace root and is the * file's own directory when rendering a markdown file. `cwd` stays the workspace * root so the result still knows whether the target is inside it. */export function resolveMarkdownFileLinkTarget(  href: string | undefined,  cwd?: string,  baseDir: string | undefined = cwd,): string | null {  if (!href) return null;  const target = parseMarkdownFileLink(href);  if (!target) return null;   const pathWithPosition = formatFilePathPosition(target);  if (!isRelativeFilePath(pathWithPosition)) return pathWithPosition;  if (!baseDir) return null;  return resolvePathLinkTarget(pathWithPosition, baseDir);} /** * Inline code spans mostly hold identifiers, commands, and refs (`node.meta`, * `origin/main`) rather than deliberate link destinations, so auto-linking * them demands stronger path evidence than an explicit markdown link does: * an unambiguous path prefix, a file extension, or a :line suffix. */export function resolveInlineCodeFileLinkMeta(  codeText: string,  cwd?: string,  baseDir: string | undefined = cwd,): MarkdownFileLinkMeta | null {  const candidate = inlineCodeFilePathCandidate(codeText);  if (candidate === null) return null;   return resolveMarkdownFileLinkMeta(candidate, cwd, baseDir);} export function resolveMarkdownFileLinkMeta(  href: string | undefined,  cwd?: string,  baseDir: string | undefined = cwd,): MarkdownFileLinkMeta | null {  const targetPath = resolveMarkdownFileLinkTarget(href, cwd, baseDir);  if (!targetPath) return null;  return buildFileLinkMetaFromTarget(targetPath, cwd);} function buildFileLinkMetaFromTarget(targetPath: string, cwd?: string): MarkdownFileLinkMeta {  const { path, line, column } = splitFilePathPosition(targetPath);  return {    filePath: path,    targetPath,    displayPath: formatWorkspaceRelativePath(targetPath, cwd),    workspaceRelativePath: workspaceRelativeFilePath(path, cwd),    basename: fileBasename(path),    ...(line !== undefined ? { line } : {}),    ...(column !== undefined ? { column } : {}),  };}