| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- import type { CliLanguage, SgResult } from './types';
- export function formatSearchResult(result: SgResult): string {
- if (result.error) {
- return `Error: ${result.error}`;
- }
- if (result.matches.length === 0) {
- return 'No matches found.';
- }
- const lines: string[] = [];
- // Group matches by file
- const byFile = new Map<string, typeof result.matches>();
- for (const match of result.matches) {
- const existing = byFile.get(match.file) || [];
- existing.push(match);
- byFile.set(match.file, existing);
- }
- for (const [file, matches] of byFile) {
- lines.push(`\n${file}:`);
- for (const match of matches) {
- const startLine = match.range.start.line + 1;
- const text =
- match.text.length > 100
- ? `${match.text.substring(0, 100)}...`
- : match.text;
- lines.push(` ${startLine}: ${text.replace(/\n/g, '\\n')}`);
- }
- }
- const fileCount = byFile.size;
- const summary = `Found ${result.totalMatches} matches in ${fileCount} files`;
- if (result.truncated) {
- lines.push(`\n${summary} (output truncated: ${result.truncatedReason})`);
- } else {
- lines.push(`\n${summary}`);
- }
- return lines.join('\n');
- }
- export function formatReplaceResult(
- result: SgResult,
- isDryRun: boolean,
- ): string {
- if (result.error) {
- return `Error: ${result.error}`;
- }
- if (result.matches.length === 0) {
- return 'No matches found for replacement.';
- }
- const lines: string[] = [];
- const mode = isDryRun ? '[DRY RUN]' : '[APPLIED]';
- // Group by file
- const byFile = new Map<string, typeof result.matches>();
- for (const match of result.matches) {
- const existing = byFile.get(match.file) || [];
- existing.push(match);
- byFile.set(match.file, existing);
- }
- for (const [file, matches] of byFile) {
- lines.push(`\n${file}:`);
- for (const match of matches) {
- const startLine = match.range.start.line + 1;
- const original =
- match.text.length > 60
- ? `${match.text.substring(0, 60)}...`
- : match.text;
- const replacement = match.replacement
- ? match.replacement.length > 60
- ? `${match.replacement.substring(0, 60)}...`
- : match.replacement
- : '[no replacement]';
- lines.push(
- ` ${startLine}: "${original.replace(/\n/g, '\\n')}" → "${replacement.replace(/\n/g, '\\n')}"`,
- );
- }
- }
- const fileCount = byFile.size;
- lines.push(
- `\n${mode} ${result.totalMatches} replacements in ${fileCount} files`,
- );
- if (isDryRun) {
- lines.push('\nTo apply changes, run with dryRun=false');
- }
- return lines.join('\n');
- }
- export function getEmptyResultHint(
- pattern: string,
- lang: CliLanguage,
- ): string | null {
- const src = pattern.trim();
- if (lang === 'python') {
- if (src.startsWith('class ') && src.endsWith(':')) {
- const withoutColon = src.slice(0, -1);
- return `Hint: Remove trailing colon. Try: "${withoutColon}"`;
- }
- if (
- (src.startsWith('def ') || src.startsWith('async def ')) &&
- src.endsWith(':')
- ) {
- const withoutColon = src.slice(0, -1);
- return `Hint: Remove trailing colon. Try: "${withoutColon}"`;
- }
- }
- if (['javascript', 'typescript', 'tsx'].includes(lang)) {
- if (/^(export\s+)?(async\s+)?function\s+\$[A-Z_]+\s*$/i.test(src)) {
- return `Hint: Function patterns need params and body. Try "function $NAME($$$) { $$$ }"`;
- }
- }
- return null;
- }
|