Skip to content

Commit 8473740

Browse files
Improve import naming and added declaration output
Import declarations now show package names instead of opaque _import_declaration_N identifiers. Go imports extract short package names (e.g., 'fmt, http, context'). For large import blocks, shows first two and count (e.g., 'fmt, http, ... (5 packages)'). Import blocks are keyed by position rather than name, so changing the imported packages shows as 'modified' with inline diff instead of separate remove+add entries. Added declarations now show their signature (first line) to give context about what was added, rather than just the word 'added'.
1 parent 88a6a37 commit 8473740

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

pkg/github/structural_diff.go

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
328343
func 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).
342375
func 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.
469509
func 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

Comments
 (0)