feat(ast-grep): add helpful hints for incomplete Python patterns
- Show hints when Python class/function patterns return empty results - Detect patterns ending with ':' that need body (class :, def ():) - Removed validation that could cause false positives - Hints only appear on empty results, not on successful matches
This commit is contained in:
@@ -10,44 +10,25 @@ function showOutputToUser(context: unknown, output: string): void {
|
|||||||
ctx.metadata?.({ metadata: { output } })
|
ctx.metadata?.({ metadata: { output } })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function getEmptyResultHint(pattern: string, lang: CliLanguage): string | null {
|
||||||
* JS/TS languages that require complete function declaration patterns
|
|
||||||
*/
|
|
||||||
const JS_TS_LANGUAGES = ["javascript", "typescript", "tsx"] as const
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates AST pattern for common incomplete patterns that will fail silently.
|
|
||||||
* Only validates JS/TS languages where function declarations require body.
|
|
||||||
*
|
|
||||||
* @throws Error with helpful message if pattern is incomplete
|
|
||||||
*/
|
|
||||||
function validatePatternForCli(pattern: string, lang: CliLanguage): void {
|
|
||||||
if (!JS_TS_LANGUAGES.includes(lang as (typeof JS_TS_LANGUAGES)[number])) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const src = pattern.trim()
|
const src = pattern.trim()
|
||||||
|
|
||||||
// Detect incomplete function declarations:
|
if (lang === "python") {
|
||||||
// - "function $NAME" (no params/body)
|
if (src.startsWith("class ") && src.endsWith(":")) {
|
||||||
// - "export function $NAME" (no params/body)
|
return `💡 Hint: Python class patterns need body. Try "class $NAME" or include body with $$$BODY`
|
||||||
// - "export async function $NAME" (no params/body)
|
|
||||||
// - "export default function $NAME" (no params/body)
|
|
||||||
// Pattern: ends with $METAVAR (uppercase, underscore, digits) without ( or {
|
|
||||||
const incompleteFunctionDecl =
|
|
||||||
/^(export\s+)?(default\s+)?(async\s+)?function\s+\$[A-Z_][A-Z0-9_]*\s*$/i.test(src)
|
|
||||||
|
|
||||||
if (incompleteFunctionDecl) {
|
|
||||||
throw new Error(
|
|
||||||
`Incomplete AST pattern for ${lang}: "${pattern}"\n\n` +
|
|
||||||
`ast-grep requires complete AST nodes. Function declarations must include parameters and body.\n\n` +
|
|
||||||
`Examples of correct patterns:\n` +
|
|
||||||
` - "export async function $NAME($$$) { $$$ }" (matches export async functions)\n` +
|
|
||||||
` - "function $NAME($$$) { $$$ }" (matches all function declarations)\n` +
|
|
||||||
` - "async function $NAME($$$) { $$$ }" (matches async functions)\n\n` +
|
|
||||||
`Your pattern "${pattern}" is missing the parameter list and body.`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
if ((src.startsWith("def ") || src.startsWith("async def ")) && src.endsWith(":")) {
|
||||||
|
return `💡 Hint: Python function patterns need body. Try "def $FUNC($$$):\\n $$$BODY"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ast_grep_search = tool({
|
export const ast_grep_search = tool({
|
||||||
@@ -66,8 +47,6 @@ export const ast_grep_search = tool({
|
|||||||
},
|
},
|
||||||
execute: async (args, context) => {
|
execute: async (args, context) => {
|
||||||
try {
|
try {
|
||||||
validatePatternForCli(args.pattern, args.lang as CliLanguage)
|
|
||||||
|
|
||||||
const matches = await runSg({
|
const matches = await runSg({
|
||||||
pattern: args.pattern,
|
pattern: args.pattern,
|
||||||
lang: args.lang as CliLanguage,
|
lang: args.lang as CliLanguage,
|
||||||
@@ -75,7 +54,16 @@ export const ast_grep_search = tool({
|
|||||||
globs: args.globs,
|
globs: args.globs,
|
||||||
context: args.context,
|
context: args.context,
|
||||||
})
|
})
|
||||||
const output = formatSearchResult(matches)
|
|
||||||
|
let output = formatSearchResult(matches)
|
||||||
|
|
||||||
|
if (matches.length === 0) {
|
||||||
|
const hint = getEmptyResultHint(args.pattern, args.lang as CliLanguage)
|
||||||
|
if (hint) {
|
||||||
|
output += `\n\n${hint}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showOutputToUser(context, output)
|
showOutputToUser(context, output)
|
||||||
return output
|
return output
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user