@@ -310,7 +310,6 @@ func goNameExtractor(node *sitter.Node, source []byte) string {
310310 }
311311 return name
312312 case "type_declaration" , "var_declaration" , "const_declaration" :
313- // These contain spec children (type_spec, var_spec, const_spec) with name fields
314313 for i := 0 ; i < int (node .ChildCount ()); i ++ {
315314 child := node .Child (i )
316315 nameNode := child .ChildByFieldName ("name" )
@@ -319,11 +318,27 @@ func goNameExtractor(node *sitter.Node, source []byte) string {
319318 }
320319 }
321320 return ""
321+ case "import_declaration" :
322+ return summarizeImport (node , source )
322323 default :
323324 return defaultNameExtractor (node , source )
324325 }
325326}
326327
328+ // summarizeImport produces a concise name for an import declaration by
329+ // extracting the imported package paths.
330+ func summarizeImport (node * sitter.Node , source []byte ) string {
331+ var paths []string
332+ collectImportPaths (node , source , & paths )
333+ if len (paths ) == 0 {
334+ return node .Content (source )
335+ }
336+ if len (paths ) <= 3 {
337+ return strings .Join (paths , ", " )
338+ }
339+ return fmt .Sprintf ("%s, %s, ... (%d packages)" , paths [0 ], paths [1 ], len (paths ))
340+ }
341+
327342// extractReceiverType extracts the type name from a Go method receiver.
328343func extractReceiverType (receiver * sitter.Node , source []byte ) string {
329344 for i := 0 ; i < int (receiver .ChildCount ()); i ++ {
@@ -338,6 +353,24 @@ func extractReceiverType(receiver *sitter.Node, source []byte) string {
338353 return receiver .Content (source )
339354}
340355
356+ // collectImportPaths extracts package path strings from an import node tree.
357+ func collectImportPaths (node * sitter.Node , source []byte , paths * []string ) {
358+ if node .Type () == "interpreted_string_literal" || node .Type () == "raw_string_literal" {
359+ // Strip quotes
360+ content := node .Content (source )
361+ content = strings .Trim (content , "\" '`" )
362+ // Use short form: last path component
363+ if idx := strings .LastIndex (content , "/" ); idx >= 0 {
364+ content = content [idx + 1 :]
365+ }
366+ * paths = append (* paths , content )
367+ return
368+ }
369+ for i := 0 ; i < int (node .ChildCount ()); i ++ {
370+ collectImportPaths (node .Child (i ), source , paths )
371+ }
372+ }
373+
341374// jsNameExtractor handles JS/TS-specific naming (variable declarations, exports).
342375func jsNameExtractor (node * sitter.Node , source []byte ) string {
343376 switch node .Type () {
@@ -454,7 +487,12 @@ func diffDeclarations(config *languageConfig, base, head []declaration, indent s
454487 case inBase && ! inHead :
455488 changes = append (changes , fmt .Sprintf ("%s%s %s: removed" , indent , baseDecl .Kind , baseDecl .Name ))
456489 case ! inBase && inHead :
457- changes = append (changes , fmt .Sprintf ("%s%s %s: added" , indent , headDecl .Kind , headDecl .Name ))
490+ sig := declarationSignature (headDecl .Text )
491+ if sig != "" && sig != headDecl .Name {
492+ changes = append (changes , fmt .Sprintf ("%s%s %s: added\n %s %s" , indent , headDecl .Kind , headDecl .Name , indent , sig ))
493+ } else {
494+ changes = append (changes , fmt .Sprintf ("%s%s %s: added" , indent , headDecl .Kind , headDecl .Name ))
495+ }
458496 case baseDecl .Text != headDecl .Text :
459497 detail := modifiedDetail (config , baseDecl , headDecl , indent , depth )
460498 changes = append (changes , fmt .Sprintf ("%s%s %s: modified\n %s" , indent , baseDecl .Kind , baseDecl .Name , detail ))
@@ -466,15 +504,35 @@ func diffDeclarations(config *languageConfig, base, head []declaration, indent s
466504
467505// indexDeclarations creates a lookup map from declaration key to declaration.
468506// The key combines kind and name to handle same-name declarations of different kinds.
507+ // Import and package declarations use kind-only keys since they're typically
508+ // singletons and their "name" changes when contents change.
469509func indexDeclarations (decls []declaration ) map [string ]declaration {
470510 result := make (map [string ]declaration , len (decls ))
511+ importCount := 0
471512 for _ , d := range decls {
472- key := d .Kind + ":" + d .Name
513+ var key string
514+ switch d .Kind {
515+ case "import_declaration" , "import_statement" , "import_from_statement" ,
516+ "package_clause" , "package_declaration" :
517+ key = fmt .Sprintf ("%s:%d" , d .Kind , importCount )
518+ importCount ++
519+ default :
520+ key = d .Kind + ":" + d .Name
521+ }
473522 result [key ] = d
474523 }
475524 return result
476525}
477526
527+ // declarationSignature returns the first line of a declaration, which typically
528+ // contains the signature (e.g., "func hello(name string) error {").
529+ func declarationSignature (text string ) string {
530+ if idx := strings .Index (text , "\n " ); idx >= 0 {
531+ return strings .TrimSpace (text [:idx ])
532+ }
533+ return strings .TrimSpace (text )
534+ }
535+
478536// modifiedDetail produces the detail output for a modified declaration. If the
479537// declaration contains sub-declarations (e.g. methods in a class) and we haven't
480538// hit the depth limit, it recurses to show which children changed. Otherwise it
0 commit comments