searchCode

tcmts.cli.pages.searchCode · #6mgpjuvf · term · runs on the server

uses tcmts.cli.pages.searchableTexts, which uses node:fs

Code search, as an editor's find in files: every line of the codebase's definitions (as their documents show them, with current names) and pages that has query in it, by definition: its text as written, or a regular expression written /…/ ([textPattern]), under a namespace (or a definition) if it's given. It stops at 500 definitions or 5000 matches: truncated says there was more to search.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { searchableTexts } from "#codebase/tcmts/cli/pages";
import type { SearchLine, SearchOptions, SearchResults } from "#codebase/tcmts/cli/pages";
import { textPattern } from "#codebase/tcmts/cli/pattern";

export function searchCode(
  cb: Codebase,
  query: string,
  opts: SearchOptions,
  viewUri: (hash: string) => string,
): SearchResults {
  const none: SearchResults = { hits: [], matches: 0, truncated: false };
  if (!query) return none;
  const pattern = textPattern(query, opts);
  if (pattern.error !== undefined) return { ...none, error: pattern.error };
  const under = opts.under;
  const out = none;
  for (const t of searchableTexts(cb)) {
    if (under && t.path !== under && !t.path.startsWith(`${under}.`)) continue;
    if (out.matches >= 5000 || out.hits.length >= 500) {
      out.truncated = true;
      break;
    }
    const lines = t.text.split("\n");
    const found: SearchLine[] = [];
    for (let i = t.first; i < lines.length; i++) {
      const line = lines[i]!;
      const ranges = pattern.find(line);
      if (!ranges.length) continue;
      // shown from a little before the first match, without the indentation
      const indent = line.length - line.trimStart().length;
      const from = Math.max(indent, ranges[0]![0] - 40);
      const text = line.slice(from, from + 240);
      found.push({
        line: i,
        column: ranges[0]![0],
        text,
        ranges: ranges
          .map(([a, b]): [number, number] => [a - from, Math.min(b - from, text.length)])
          .filter(([a]) => a < text.length),
      });
      out.matches += ranges.length;
    }
    if (!found.length) continue;
    out.hits.push({
      path: t.path,
      hash: t.hash,
      kind: t.kind,
      ...(t.kind === "file" ? {} : { uri: viewUri(t.hash) }),
      first: t.first,
      lines: found,
    });
  }
  return out;
}

uses

used by