// Multicall binary for codeintel tools. // Invoked via symlinks: code_outline, code_query, code_rewrite, code_symbols, // code_dependencies, codebase_overview. package main import ( "encoding/json" "fmt" "os" "path/filepath" "strconv" "strings" "ollie/tools/codeintel/internal/codeintel" ) func main() { name := filepath.Base(os.Args[0]) switch name { case "code_outline": cmdOutline() case "code_query": cmdQuery() case "code_rewrite": cmdRewrite() case "code_symbols": cmdSymbols() case "code_dependencies": cmdDependencies() case "codebase_overview": cmdOverview() default: fmt.Fprintf(os.Stderr, "codeintel: unknown tool %q\n", name) os.Exit(1) } } func fail(e error) { fmt.Fprintln(os.Stderr, "error:", e); os.Exit(1) } // flexInt unmarshals from JSON int or string. type flexInt int func (f *flexInt) UnmarshalJSON(data []byte) error { var i int if err := json.Unmarshal(data, &i); err == nil { *f = flexInt(i) return nil } var s string if err := json.Unmarshal(data, &s); err != nil { return err } n, err := strconv.Atoi(s) if err != nil { return err } *f = flexInt(n) return nil } // --- code_outline --- func cmdOutline() { var a struct { Path string `json:"path"` Language string `json:"language"` } if err := json.NewDecoder(os.Stdin).Decode(&a); err != nil { fail(err) } p, err := filepath.Abs(a.Path) if err != nil { fail(err) } d, err := codeintel.Parse(p, a.Language) if err != nil { fail(err) } defer d.Close() for _, s := range codeintel.Symbols(d) { fmt.Printf("%s:%d: %s [%s]\n", s.Path, s.Line, s.Name, s.Kind) } } // --- code_query --- func cmdQuery() { var a struct { Path string `json:"path"` Language string `json:"language"` Query string `json:"query"` } if err := json.NewDecoder(os.Stdin).Decode(&a); err != nil { fail(err) } p, err := filepath.Abs(a.Path) if err != nil { fail(err) } cs, err := codeintel.Query(p, a.Language, a.Query) if err != nil { fail(err) } for _, c := range cs { fmt.Printf("%s:%d: %s = %s\n", c.Name, c.Line, c.Name, c.Text) } } // --- code_rewrite --- func cmdRewrite() { var a struct { Path string `json:"path"` Language string `json:"language"` Query string `json:"query"` Replacement string `json:"replacement"` DryRun bool `json:"dry_run"` } if err := json.NewDecoder(os.Stdin).Decode(&a); err != nil { fail(err) } p, err := filepath.Abs(a.Path) if err != nil { fail(err) } out, err := codeintel.ReplaceMatches(p, a.Language, a.Query, a.Replacement) if err != nil { fail(err) } if a.DryRun { fmt.Print(string(out)) return } if err := os.WriteFile(p, out, 0644); err != nil { fail(err) } } // --- code_symbols --- func cmdSymbols() { var a struct { Path string `json:"path"` Query string `json:"query"` } if err := json.NewDecoder(os.Stdin).Decode(&a); err != nil { fail(err) } root, err := filepath.Abs(a.Path) if err != nil { fail(err) } ss, err := codeintel.MatchSymbols(root, a.Query) if err != nil { fail(err) } for _, s := range ss { fmt.Printf("%s:%d: %s [%s]\n", s.Path, s.Line, s.Name, s.Kind) } } // --- code_dependencies --- func cmdDependencies() { var a struct { Path string `json:"path"` Language string `json:"language"` } if err := json.NewDecoder(os.Stdin).Decode(&a); err != nil { fail(err) } p, err := filepath.Abs(a.Path) if err != nil { fail(err) } ds, err := codeintel.Dependencies(p, a.Language) if err != nil { fail(err) } for _, d := range ds { fmt.Println(d) } } // --- codebase_overview --- func cmdOverview() { var a struct { Path string `json:"path"` Offset flexInt `json:"offset"` Limit flexInt `json:"limit"` } if err := json.NewDecoder(os.Stdin).Decode(&a); err != nil { fail(err) } root, err := filepath.Abs(a.Path) if err != nil { fail(err) } files, err := codeintel.Files(root) if err != nil { fail(err) } start := int(a.Offset) limit := int(a.Limit) if start < 0 { fail(fmt.Errorf("offset must be non-negative")) } if limit < 0 { fail(fmt.Errorf("limit must be non-negative")) } if start >= len(files) { start = len(files) } end := len(files) if limit > 0 && start+limit < end { end = start + limit } fmt.Printf("# Codebase Overview\n\nPath: `%s`\n\n", root) if len(files) > 0 && (start > 0 || end < len(files)) { fmt.Printf("Files %d-%d of %d. Next offset: %d\n\n", start+1, end, len(files), end) } for _, f := range files[start:end] { d, err := codeintel.Parse(f.Path, f.Language) if err != nil { continue } fmt.Printf("## `%s` (%s)\n\n", relPath(root, f.Path), f.Language) for _, s := range codeintel.Symbols(d) { fmt.Printf("- %s`%s` — line %d\n", strings.Repeat(" ", min(s.Depth, 4)), s.Name, s.Line) } fmt.Println() d.Close() } } func relPath(root, path string) string { p, _ := filepath.Rel(root, path); return p }