Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

terminal/starbind: add online help for starlark #3388

Merged
merged 1 commit into from
Jun 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions _scripts/gen-starlark-bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/token"
"go/types"
Expand Down Expand Up @@ -99,9 +100,11 @@ type binding struct {

argNames []string
argTypes []string

docStr string
}

func processServerMethods(serverMethods []*types.Func) []binding {
func processServerMethods(serverMethods []*types.Func, funcDeclByPos map[token.Pos]*ast.FuncDecl) []binding {
bindings := make([]binding, len(serverMethods))
for i, fn := range serverMethods {
sig, _ := fn.Type().(*types.Signature)
Expand Down Expand Up @@ -133,13 +136,30 @@ func processServerMethods(serverMethods []*types.Func) []binding {
retType = "rpc2.StateOut"
}

docStr := ""

if decl := funcDeclByPos[fn.Pos()]; decl != nil && decl.Doc != nil {
docs := []string{}
for _, cmnt := range decl.Doc.List {
docs = append(docs, strings.TrimPrefix(strings.TrimPrefix(cmnt.Text, "//"), " "))
}

// fix name of the function in the first line of the documentation
if fields := strings.SplitN(docs[0], " ", 2); len(fields) == 2 && fields[0] == fn.Name() {
docs[0] = name + " " + fields[1]
}

docStr = strings.Join(docs, "\n")
}

bindings[i] = binding{
name: name,
fn: fn,
argType: sig.Params().At(0).Type().String(),
retType: retType,
argNames: argNames,
argTypes: argTypes,
docStr: docStr,
}
}
return bindings
Expand All @@ -159,8 +179,9 @@ func genMapping(bindings []binding) []byte {
fmt.Fprintf(buf, "// DO NOT EDIT: auto-generated using _scripts/gen-starlark-bindings.go\n\n")
fmt.Fprintf(buf, "package starbind\n\n")
fmt.Fprintf(buf, "import ( \"go.starlark.net/starlark\" \n \"github.com/go-delve/delve/service/api\" \n \"github.com/go-delve/delve/service/rpc2\" \n \"fmt\" )\n\n")
fmt.Fprintf(buf, "func (env *Env) starlarkPredeclare() starlark.StringDict {\n")
fmt.Fprintf(buf, "r := starlark.StringDict{}\n\n")
fmt.Fprintf(buf, "func (env *Env) starlarkPredeclare() (starlark.StringDict, map[string]string) {\n")
fmt.Fprintf(buf, "r := starlark.StringDict{}\n")
fmt.Fprintf(buf, "doc := make(map[string]string)\n\n")

for _, binding := range bindings {
fmt.Fprintf(buf, "r[%q] = starlark.NewBuiltin(%q, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {", binding.name, binding.name)
Expand Down Expand Up @@ -209,9 +230,24 @@ func genMapping(bindings []binding) []byte {
fmt.Fprintf(buf, "return env.interfaceToStarlarkValue(rpcRet), nil\n")

fmt.Fprintf(buf, "})\n")

// builtin documentation
docstr := new(strings.Builder)
fmt.Fprintf(docstr, "builtin %s(", binding.name)
for i, argname := range binding.argNames {
if i != 0 {
fmt.Fprintf(docstr, ", ")
}
fmt.Fprintf(docstr, argname)
}
fmt.Fprintf(docstr, ")")
if binding.docStr != "" {
fmt.Fprintf(docstr, "\n\n%s", binding.docStr)
}
fmt.Fprintf(buf, "doc[%q] = %q\n", binding.name, docstr.String())
}

fmt.Fprintf(buf, "return r\n")
fmt.Fprintf(buf, "return r, doc\n")
fmt.Fprintf(buf, "}\n")

return buf.Bytes()
Expand Down Expand Up @@ -301,14 +337,23 @@ func main() {
}

var serverMethods []*types.Func
funcDeclByPos := make(map[token.Pos]*ast.FuncDecl)
packages.Visit(pkgs, func(pkg *packages.Package) bool {
if pkg.PkgPath == "github.com/go-delve/delve/service/rpc2" {
serverMethods = getSuitableMethods(pkg.Types, "RPCServer")
}
for _, file := range pkg.Syntax {
ast.Inspect(file, func(n ast.Node) bool {
if n, ok := n.(*ast.FuncDecl); ok {
funcDeclByPos[n.Name.Pos()] = n
}
return true
})
}
return true
}, nil)

bindings := processServerMethods(serverMethods)
bindings := processServerMethods(serverMethods, funcDeclByPos)

switch kind {
case "go":
Expand Down
58 changes: 57 additions & 1 deletion pkg/terminal/starbind/starlark.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"runtime"
"sort"
"strings"
"sync"

Expand All @@ -28,6 +29,7 @@ const (
dlvContextName = "dlv_context"
curScopeBuiltinName = "cur_scope"
defaultLoadConfigBuiltinName = "default_load_config"
helpBuiltinName = "help"
)

func init() {
Expand Down Expand Up @@ -71,7 +73,13 @@ func New(ctx Context, out EchoWriter) *Env {
// Make the "time" module available to Starlark scripts.
starlark.Universe["time"] = startime.Module

env.env = env.starlarkPredeclare()
var doc map[string]string
env.env, doc = env.starlarkPredeclare()

builtindoc := func(name, args, descr string) {
doc[name] = name + args + "\n\n" + name + " " + descr
}

env.env[dlvCommandBuiltinName] = starlark.NewBuiltin(dlvCommandBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
if err := isCancelled(thread); err != nil {
return starlark.None, err
Expand All @@ -90,6 +98,8 @@ func New(ctx Context, out EchoWriter) *Env {
}
return starlark.None, decorateError(thread, err)
})
builtindoc(dlvCommandBuiltinName, "(Command)", "interrupts, continues and steps through the program.")

env.env[readFileBuiltinName] = starlark.NewBuiltin(readFileBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
if len(args) != 1 {
return nil, decorateError(thread, fmt.Errorf("wrong number of arguments"))
Expand All @@ -104,6 +114,8 @@ func New(ctx Context, out EchoWriter) *Env {
}
return starlark.String(string(buf)), nil
})
builtindoc(readFileBuiltinName, "(Path)", "reads a file.")

env.env[writeFileBuiltinName] = starlark.NewBuiltin(writeFileBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
if len(args) != 2 {
return nil, decorateError(thread, fmt.Errorf("wrong number of arguments"))
Expand All @@ -115,12 +127,56 @@ func New(ctx Context, out EchoWriter) *Env {
err := ioutil.WriteFile(string(path), []byte(args[1].String()), 0640)
return starlark.None, decorateError(thread, err)
})
builtindoc(writeFileBuiltinName, "(Path, Text)", "writes text to the specified file.")

env.env[curScopeBuiltinName] = starlark.NewBuiltin(curScopeBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
return env.interfaceToStarlarkValue(env.ctx.Scope()), nil
})
builtindoc(curScopeBuiltinName, "()", "returns the current scope.")

env.env[defaultLoadConfigBuiltinName] = starlark.NewBuiltin(defaultLoadConfigBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
return env.interfaceToStarlarkValue(env.ctx.LoadConfig()), nil
})
builtindoc(defaultLoadConfigBuiltinName, "()", "returns the default load configuration.")

env.env[helpBuiltinName] = starlark.NewBuiltin(helpBuiltinName, func(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
switch len(args) {
case 0:
fmt.Fprintln(env.out, "Available builtins:")
bins := make([]string, 0, len(env.env))
for name, value := range env.env {
switch value.(type) {
case *starlark.Builtin:
bins = append(bins, name)
}
}
sort.Strings(bins)
for _, bin := range bins {
fmt.Fprintf(env.out, "\t%s\n", bin)
}
case 1:
switch x := args[0].(type) {
case *starlark.Builtin:
if doc[x.Name()] != "" {
fmt.Fprintf(env.out, "%s\n", doc[x.Name()])
} else {
fmt.Fprintf(env.out, "no help for builtin %s\n", x.Name())
}
case *starlark.Function:
fmt.Fprintf(env.out, "user defined function %s\n", x.Name())
if doc := x.Doc(); doc != "" {
fmt.Fprintln(env.out, doc)
}
default:
fmt.Fprintf(env.out, "no help for object of type %T\n", args[0])
}
default:
fmt.Fprintln(env.out, "wrong number of arguments ", len(args))
}
return starlark.None, nil
})
builtindoc(helpBuiltinName, "(Object)", "prints help for Object.")

return env
}

Expand Down
Loading