@@ -21,15 +21,17 @@ const GO_QUERIES = {
2121 name: (identifier) @name) @definition
2222 ` ,
2323
24- // Method declarations with receivers
24+ // Method declarations with receivers (handles both regular and generic types)
2525 methods : `
2626 (method_declaration
2727 receiver: (parameter_list
2828 (parameter_declaration
2929 name: (identifier)? @receiver_name
3030 type: [
3131 (pointer_type (type_identifier) @receiver_type)
32+ (pointer_type (generic_type (type_identifier) @receiver_type))
3233 (type_identifier) @receiver_type
34+ (generic_type (type_identifier) @receiver_type)
3335 ])) @receiver
3436 name: (field_identifier) @name) @definition
3537 ` ,
@@ -196,6 +198,9 @@ export class GoScanner implements Scanner {
196198 const exported = this . isExported ( name ) ;
197199 const snippet = this . truncateSnippet ( fullText ) ;
198200
201+ // Check for generics
202+ const { isGeneric, typeParameters } = this . extractTypeParameters ( signature ) ;
203+
199204 documents . push ( {
200205 id : `${ file } :${ name } :${ startLine } ` ,
201206 text : this . buildEmbeddingText ( 'function' , name , signature , docstring ) ,
@@ -210,7 +215,10 @@ export class GoScanner implements Scanner {
210215 exported,
211216 docstring,
212217 snippet,
213- custom : isTestFile ? { isTest : true } : undefined ,
218+ custom : {
219+ ...( isTestFile ? { isTest : true } : { } ) ,
220+ ...( isGeneric ? { isGeneric, typeParameters } : { } ) ,
221+ } ,
214222 } ,
215223 } ) ;
216224 }
@@ -240,7 +248,9 @@ export class GoScanner implements Scanner {
240248
241249 const methodName = nameCapture . node . text ;
242250 const receiverType = receiverTypeCapture ?. node . text || 'Unknown' ;
243- const name = `${ receiverType } .${ methodName } ` ;
251+ // Strip type parameters from receiver for cleaner name (Stack[T] -> Stack)
252+ const baseReceiverType = receiverType . replace ( / \[ .* \] / , '' ) ;
253+ const name = `${ baseReceiverType } .${ methodName } ` ;
244254 const startLine = defCapture . node . startPosition . row + 1 ;
245255 const endLine = defCapture . node . endPosition . row + 1 ;
246256 const fullText = defCapture . node . text ;
@@ -253,6 +263,12 @@ export class GoScanner implements Scanner {
253263 const receiverText = receiverCapture ?. node . text || '' ;
254264 const receiverPointer = receiverText . includes ( '*' ) ;
255265
266+ // Check for generics (receiver has type params like Stack[T])
267+ const receiverHasGenerics = receiverType . includes ( '[' ) ;
268+ const { isGeneric : signatureHasGenerics , typeParameters } =
269+ this . extractTypeParameters ( signature ) ;
270+ const isGeneric = receiverHasGenerics || signatureHasGenerics ;
271+
256272 documents . push ( {
257273 id : `${ file } :${ name } :${ startLine } ` ,
258274 text : this . buildEmbeddingText ( 'method' , name , signature , docstring ) ,
@@ -268,9 +284,10 @@ export class GoScanner implements Scanner {
268284 docstring,
269285 snippet,
270286 custom : {
271- receiver : receiverType ,
287+ receiver : baseReceiverType ,
272288 receiverPointer,
273289 ...( isTestFile ? { isTest : true } : { } ) ,
290+ ...( isGeneric ? { isGeneric, typeParameters } : { } ) ,
274291 } ,
275292 } ,
276293 } ) ;
@@ -301,7 +318,13 @@ export class GoScanner implements Scanner {
301318 const startLine = defCapture . node . startPosition . row + 1 ;
302319 const endLine = defCapture . node . endPosition . row + 1 ;
303320 const fullText = defCapture . node . text ;
304- const signature = `type ${ name } struct` ;
321+
322+ // Check for generics in the full declaration text
323+ const { isGeneric, typeParameters } = this . extractTypeParameters ( fullText ) ;
324+ const signature = isGeneric
325+ ? `type ${ name } [${ typeParameters ?. join ( ', ' ) } ] struct`
326+ : `type ${ name } struct` ;
327+
305328 const docstring = extractGoDocComment ( sourceText , startLine ) ;
306329 const exported = this . isExported ( name ) ;
307330 const snippet = this . truncateSnippet ( fullText ) ;
@@ -320,7 +343,10 @@ export class GoScanner implements Scanner {
320343 exported,
321344 docstring,
322345 snippet,
323- custom : isTestFile ? { isTest : true } : undefined ,
346+ custom : {
347+ ...( isTestFile ? { isTest : true } : { } ) ,
348+ ...( isGeneric ? { isGeneric, typeParameters } : { } ) ,
349+ } ,
324350 } ,
325351 } ) ;
326352 }
@@ -350,7 +376,13 @@ export class GoScanner implements Scanner {
350376 const startLine = defCapture . node . startPosition . row + 1 ;
351377 const endLine = defCapture . node . endPosition . row + 1 ;
352378 const fullText = defCapture . node . text ;
353- const signature = `type ${ name } interface` ;
379+
380+ // Check for generics in the full declaration text
381+ const { isGeneric, typeParameters } = this . extractTypeParameters ( fullText ) ;
382+ const signature = isGeneric
383+ ? `type ${ name } [${ typeParameters ?. join ( ', ' ) } ] interface`
384+ : `type ${ name } interface` ;
385+
354386 const docstring = extractGoDocComment ( sourceText , startLine ) ;
355387 const exported = this . isExported ( name ) ;
356388 const snippet = this . truncateSnippet ( fullText ) ;
@@ -369,7 +401,10 @@ export class GoScanner implements Scanner {
369401 exported,
370402 docstring,
371403 snippet,
372- custom : isTestFile ? { isTest : true } : undefined ,
404+ custom : {
405+ ...( isTestFile ? { isTest : true } : { } ) ,
406+ ...( isGeneric ? { isGeneric, typeParameters } : { } ) ,
407+ } ,
373408 } ,
374409 } ) ;
375410 }
@@ -498,6 +533,34 @@ export class GoScanner implements Scanner {
498533 return fullText . slice ( 0 , braceIndex ) . trim ( ) ;
499534 }
500535
536+ /**
537+ * Extract type parameters from a generic declaration.
538+ * Returns { isGeneric, typeParameters } where typeParameters is an array like ["T any", "K comparable"]
539+ */
540+ private extractTypeParameters ( signature : string ) : {
541+ isGeneric : boolean ;
542+ typeParameters ?: string [ ] ;
543+ } {
544+ // Match type parameters in brackets: func Name[T any, K comparable](...) or type Name[T any] struct
545+ // Look for [ before ( for functions, or [ after type name for types
546+ const typeParamMatch = signature . match ( / \[ ( [ ^ \] ] + ) \] / ) ;
547+ if ( ! typeParamMatch ) {
548+ return { isGeneric : false } ;
549+ }
550+
551+ const params = typeParamMatch [ 1 ] ;
552+ // Split by comma, but handle constraints like "~int | ~string"
553+ const typeParameters = params
554+ . split ( / , \s * / )
555+ . map ( ( p ) => p . trim ( ) )
556+ . filter ( ( p ) => p . length > 0 ) ;
557+
558+ return {
559+ isGeneric : true ,
560+ typeParameters,
561+ } ;
562+ }
563+
501564 /**
502565 * Build embedding text for vector search
503566 */
0 commit comments