238 lines
4.8 KiB
Go
238 lines
4.8 KiB
Go
// 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 }
|