1
1
import debug from "debug"
2
2
const dbg = debug ( "genaiscript:astgrep" )
3
+ const dbgLang = debug ( "genaiscript:astgrep:lang" )
3
4
4
5
import { CancellationOptions , checkCancelled } from "./cancellation"
5
6
import { CancelError , errorMessage } from "./error"
@@ -94,12 +95,10 @@ export async function astGrepFindFiles(
94
95
}
95
96
const diffFiles = diffResolve ( diff )
96
97
97
- dbg ( `finding files with ${ lang } %O` , matcher )
98
+ dbg ( `search %O` , matcher )
98
99
if ( diffFiles ?. length ) dbg ( `diff files: ${ diffFiles . length } ` )
99
100
const { findInFiles } = await import ( "@ast-grep/napi" )
100
101
checkCancelled ( cancellationToken )
101
- const sglang = await resolveLang ( lang )
102
- dbg ( `resolving language: ${ lang } ` )
103
102
104
103
let paths = await host . findFiles ( glob , options )
105
104
if ( ! paths ?. length ) {
@@ -129,6 +128,7 @@ export async function astGrepFindFiles(
129
128
const p = new Promise < number > ( async ( resolve , reject ) => {
130
129
let i = 0
131
130
let n : number = undefined
131
+ const sglang = await resolveLang ( lang )
132
132
n = await findInFiles (
133
133
sglang ,
134
134
{
@@ -230,7 +230,7 @@ export async function astGrepWriteRootEdits(
230
230
*/
231
231
export async function astGrepParse (
232
232
file : WorkspaceFile ,
233
- options ?: { lang ?: SgLang } & CancellationOptions
233
+ options ?: { lang ?: SgLang | Record < string , SgLang > } & CancellationOptions
234
234
) : Promise < SgRoot > {
235
235
const { cancellationToken } = options || { }
236
236
if ( file . encoding ) {
@@ -249,7 +249,6 @@ export async function astGrepParse(
249
249
dbg ( `parsing file: ${ filename } ` )
250
250
const { parseAsync } = await import ( "@ast-grep/napi" )
251
251
const lang = await resolveLang ( options ?. lang , filename )
252
- dbg ( `resolving language for file: ${ filename } ` )
253
252
if ( ! lang ) {
254
253
return undefined
255
254
}
@@ -259,65 +258,91 @@ export async function astGrepParse(
259
258
return root
260
259
}
261
260
262
- async function resolveLang ( lang : SgLang , filename ?: string ) {
261
+ async function resolveLang (
262
+ lang : SgLang | Record < string , SgLang > ,
263
+ filename ?: string
264
+ ) {
263
265
const { Lang } = await import ( "@ast-grep/napi" )
264
- if ( lang === "html" ) {
265
- return Lang . Html
266
- }
267
- if ( lang === "js" ) {
268
- return Lang . JavaScript
269
- }
270
- if ( lang === "ts" ) {
271
- return Lang . TypeScript
266
+
267
+ const norm = ( l : string ) => l . toLowerCase ( ) . replace ( / ^ \. / , "" )
268
+
269
+ // pre-compiled with ast-grep
270
+ const builtins : any = {
271
+ html : Lang . Html ,
272
+ htm : Lang . Html ,
273
+ cjs : Lang . JavaScript ,
274
+ mjs : Lang . JavaScript ,
275
+ js : Lang . JavaScript ,
276
+ cts : Lang . TypeScript ,
277
+ mts : Lang . TypeScript ,
278
+ ts : Lang . TypeScript ,
279
+ tsx : Lang . Tsx ,
280
+ css : Lang . Css ,
272
281
}
273
- if ( lang === "tsx" ) {
274
- return Lang . Tsx
282
+
283
+ const dynamics : any = {
284
+ h : "c" ,
285
+ c : "c" ,
286
+ cpp : "cpp" ,
287
+ hpp : "cpp" ,
288
+ hxx : "cpp" ,
289
+ cxx : "cpp" ,
290
+ cs : "csharp" ,
291
+ py : "python" ,
292
+ sql : "sql" ,
275
293
}
276
- if ( lang === "css" ) {
277
- return Lang . Css
294
+
295
+ const forbidden = [ "bin" , "exe" , "dll" ]
296
+
297
+ // user provided a string
298
+ if ( typeof lang === "string" ) {
299
+ lang = norm ( lang )
300
+ dbgLang ( `resolving language ${ lang } ` )
301
+ const builtin = builtins [ lang ]
302
+ if ( builtin ) return builtin
303
+ else return await loadDynamicLanguage ( lang )
278
304
}
279
- if ( lang ) {
280
- return await loadDynamicLanguage ( lang . toLowerCase ( ) )
305
+
306
+ if ( ! filename ) {
307
+ dbgLang ( `filename not provided` )
308
+ throw new Error ( "filename is required to resolve language" )
281
309
}
282
310
283
311
if ( filename ) {
284
- dbg ( `resolving language based on filename: ${ filename } ` )
285
- if ( / \. m ? j s $ / i. test ( filename ) ) {
286
- return Lang . JavaScript
287
- }
288
- if ( / \. m ? t s $ / i. test ( filename ) ) {
289
- return Lang . TypeScript
290
- }
291
- if ( / \. ( j | t ) s x $ / i. test ( filename ) ) {
292
- return Lang . Tsx
293
- }
294
- if ( / \. h t m l $ / i. test ( filename ) ) {
295
- return Lang . Html
296
- }
297
- if ( / \. c s s $ / i. test ( filename ) ) {
298
- return Lang . Css
299
- }
300
- return await loadDynamicLanguage (
301
- extname ( filename ) . slice ( 1 ) . toLowerCase ( )
302
- )
312
+ const ext = norm ( extname ( filename ) )
313
+ dbgLang ( `resolving language for ${ ext } ` )
314
+
315
+ // known builtins
316
+ const builtin = builtins [ ext ]
317
+ if ( builtin ) return builtin
318
+
319
+ // known dynamics
320
+ const dynamic = dynamics [ ext ]
321
+ if ( dynamic ) return await loadDynamicLanguage ( dynamic )
322
+
323
+ if ( forbidden . includes ( ext ) ) return undefined
324
+
325
+ // try our luck
326
+ return await loadDynamicLanguage ( ext )
303
327
}
304
328
329
+ dbgLang ( `language not resolved` , { lang, filename } )
305
330
throw new Error ( "language not resolved" )
306
331
}
307
332
308
333
const loadedDynamicLanguages = new Set < string > ( )
309
334
async function loadDynamicLanguage ( langName : string ) {
310
335
if ( ! loadedDynamicLanguages . has ( langName ) ) {
311
- dbg ( `loading language: ${ langName } ` )
336
+ dbgLang ( `loading language: ${ langName } ` )
312
337
const { registerDynamicLanguage } = await import ( "@ast-grep/napi" )
313
338
try {
314
339
const dynamicLang = ( await import ( `@ast-grep/lang-${ langName } ` ) )
315
340
. default
316
341
registerDynamicLanguage ( { [ langName ] : dynamicLang } )
317
342
loadedDynamicLanguages . add ( langName )
318
- dbg ( `language ${ langName } registered ` )
343
+ dbgLang ( `language ${ langName } registered ` )
319
344
} catch ( err ) {
320
- dbg ( `error loading language ${ langName } : ${ errorMessage ( err ) } ` )
345
+ dbgLang ( `error loading language ${ langName } : ${ errorMessage ( err ) } ` )
321
346
throw Error (
322
347
`@ast-grep/lang-${ langName } package failed to load, please install it using 'npm install -D @ast-grep/lang-${ langName } '`
323
348
)
0 commit comments