287 lines
7.2 KiB
Go
287 lines
7.2 KiB
Go
package langsvc
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"aego/script/abi"
|
|
"aego/script/workspace"
|
|
)
|
|
|
|
const (
|
|
StubPrefix = "stub:"
|
|
StubBuiltin = "aego/builtin"
|
|
StubComponents = "engine/components"
|
|
)
|
|
|
|
func StubPath(module string) string {
|
|
return StubPrefix + module + ".aes"
|
|
}
|
|
|
|
type stubWriter struct {
|
|
b strings.Builder
|
|
line int
|
|
lines map[string]int
|
|
locs map[string]string
|
|
}
|
|
|
|
func newStubWriter(module, pkg string) *stubWriter {
|
|
w := &stubWriter{lines: map[string]int{}, locs: map[string]string{}}
|
|
w.write("// Library stub for " + module + ", generated from the engine bindings. Read-only.")
|
|
w.write("// Ctrl+Click a declaration to open its Go source when it is known.")
|
|
w.lines[""] = w.line
|
|
w.write("package " + pkg)
|
|
w.write("")
|
|
return w
|
|
}
|
|
|
|
func (w *stubWriter) write(s string) {
|
|
w.b.WriteString(s)
|
|
w.b.WriteByte('\n')
|
|
w.line += strings.Count(s, "\n") + 1
|
|
}
|
|
|
|
func (w *stubWriter) comment(prefix, text string) {
|
|
if text == "" {
|
|
return
|
|
}
|
|
for _, l := range strings.Split(text, "\n") {
|
|
if l == "" {
|
|
w.write(prefix + "//")
|
|
continue
|
|
}
|
|
w.write(prefix + "// " + l)
|
|
}
|
|
}
|
|
|
|
func (w *stubWriter) symbol(key, decl, doc, example, loc string) {
|
|
w.comment("", doc)
|
|
if example != "" {
|
|
if doc != "" {
|
|
w.write("//")
|
|
}
|
|
w.write("// Example:")
|
|
for _, l := range strings.Split(example, "\n") {
|
|
w.write("// " + l)
|
|
}
|
|
}
|
|
w.lines[key] = w.line
|
|
if loc != "" {
|
|
w.locs[key] = loc
|
|
}
|
|
w.write(decl)
|
|
w.write("")
|
|
}
|
|
|
|
func (w *stubWriter) stub() *Stub {
|
|
return &Stub{Text: w.b.String(), Lines: w.lines, Locs: w.locs}
|
|
}
|
|
|
|
func (s *Service) Stub(path string) (*Stub, bool) {
|
|
snap := s.Latest()
|
|
if snap == nil {
|
|
return nil, false
|
|
}
|
|
s.smu.Lock()
|
|
defer s.smu.Unlock()
|
|
if s.stubTab != snap.Bindings || s.stubDocs != snap.Docs {
|
|
s.stubs = map[string]*Stub{}
|
|
s.stubTab, s.stubDocs = snap.Bindings, snap.Docs
|
|
}
|
|
if st, ok := s.stubs[path]; ok {
|
|
return st, true
|
|
}
|
|
st, ok := StubFor(snap, path)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
s.stubs[path] = st
|
|
return st, true
|
|
}
|
|
|
|
func StubFor(snap *workspace.Snapshot, path string) (*Stub, bool) {
|
|
module := strings.TrimSuffix(strings.TrimPrefix(path, StubPrefix), ".aes")
|
|
var st *Stub
|
|
switch {
|
|
case module == StubBuiltin:
|
|
st = builtinStub(snap)
|
|
case module == StubComponents:
|
|
st = componentStub(snap)
|
|
case snap.Bindings.Module(module):
|
|
st = moduleStub(snap, module)
|
|
default:
|
|
return nil, false
|
|
}
|
|
st.Path = path
|
|
return st, true
|
|
}
|
|
|
|
func StubPaths(snap *workspace.Snapshot) []string {
|
|
tab := snap.Bindings
|
|
seen := map[string]bool{}
|
|
var mods []string
|
|
add := func(m string) {
|
|
if !seen[m] {
|
|
seen[m] = true
|
|
mods = append(mods, m)
|
|
}
|
|
}
|
|
for _, f := range tab.FuncList() {
|
|
add(f.Module)
|
|
}
|
|
for _, e := range tab.EnumList() {
|
|
add(e.Module)
|
|
}
|
|
for _, k := range tab.ConstList() {
|
|
add(k.Module)
|
|
}
|
|
for _, s := range tab.StructList() {
|
|
add(s.Module)
|
|
}
|
|
sort.Strings(mods)
|
|
out := []string{StubPath(StubBuiltin), StubPath(StubComponents)}
|
|
for _, m := range mods {
|
|
out = append(out, StubPath(m))
|
|
}
|
|
return out
|
|
}
|
|
|
|
func structDecl(sd *abi.StructDef) string {
|
|
var b strings.Builder
|
|
b.WriteString("type " + sd.Name + " struct {")
|
|
for _, f := range sd.Fields {
|
|
b.WriteString("\n\t" + f.Name + " " + abiType(f.Type))
|
|
}
|
|
b.WriteString("\n}")
|
|
return b.String()
|
|
}
|
|
|
|
func hostStructDoc(snap *workspace.Snapshot, module, name string) string {
|
|
if s, ok := snap.Docs.StructDoc(module, name); ok {
|
|
return s.Doc
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func eventDoc(snap *workspace.Snapshot, name string) string {
|
|
text := "Engine event. Declare a script method with exactly this signature to receive it; only the engine calls it."
|
|
if e, ok := snap.Docs.EventDoc(name); ok && e.Doc != "" {
|
|
text = e.Doc + "\n" + text
|
|
}
|
|
return text
|
|
}
|
|
|
|
func moduleStub(snap *workspace.Snapshot, module string) *Stub {
|
|
tab := snap.Bindings
|
|
w := newStubWriter(module, module[strings.LastIndexByte(module, '/')+1:])
|
|
for _, sd := range tab.StructList() {
|
|
if sd.Module == module {
|
|
w.symbol("type:"+sd.Name, structDecl(sd), hostStructDoc(snap, module, sd.Name), "", "")
|
|
}
|
|
}
|
|
for _, fn := range tab.FuncList() {
|
|
if fn.Module != module {
|
|
continue
|
|
}
|
|
f := funcDoc(snap.Docs, module, fn.Name)
|
|
w.symbol("func:"+fn.Name, hostSig("func "+fn.Name, fn.Sig, f.Params), f.Doc, f.Example, f.Loc)
|
|
}
|
|
for _, e := range tab.EnumList() {
|
|
if e.Module != module {
|
|
continue
|
|
}
|
|
var b strings.Builder
|
|
b.WriteString("enum " + e.Name + " {")
|
|
for _, m := range e.Members {
|
|
b.WriteString("\n\t" + m.Name)
|
|
}
|
|
b.WriteString("\n}")
|
|
w.symbol("enum:"+e.Name, b.String(), enumDoc(snap, module, e.Name), "", "")
|
|
}
|
|
for _, k := range tab.ConstList() {
|
|
if k.Module != module {
|
|
continue
|
|
}
|
|
doc := ""
|
|
if mc, ok := snap.Docs.ConstDoc(module, k.Name); ok {
|
|
doc = mc.Doc
|
|
}
|
|
w.symbol("const:"+k.Name, "const "+k.Name+" "+abiType(k.Val.Type)+" = "+constValue(k.Val), doc, "", "")
|
|
}
|
|
return w.stub()
|
|
}
|
|
|
|
func enumDoc(snap *workspace.Snapshot, module, name string) string {
|
|
if snap.Docs == nil {
|
|
return ""
|
|
}
|
|
for _, e := range snap.Docs.Enums {
|
|
if e.Module == module && e.Name == name {
|
|
return e.Doc
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func builtinStub(snap *workspace.Snapshot) *Stub {
|
|
w := newStubWriter("the builtin types", "builtin")
|
|
for _, name := range builtinOrder {
|
|
decl := builtinDecl[name]
|
|
if decl == "" {
|
|
decl = "type " + name
|
|
}
|
|
w.symbol("type:"+name, decl, builtinText(snap, name), "", "")
|
|
}
|
|
w.symbol("func:len", "func len(v string | []T) int", builtinText(snap, "len"), "", "")
|
|
w.symbol("func:append", "func append(s []T, v T) []T", builtinText(snap, "append"), "", "")
|
|
w.symbol("method:NodeRef.Get", "func (NodeRef) Get<Component>() ComponentRef<Component>",
|
|
"Get returns the component of the given type on the node, or nil.", "sr := node.Get<SpriteRenderer>()", "")
|
|
w.symbol("method:NodeRef.Has", "func (NodeRef) Has<Component>() bool",
|
|
"Has reports whether the node has a component of the given type.", "if node.Has<Camera2D>() {", "")
|
|
for _, e := range snap.Bindings.EventList() {
|
|
var names []string
|
|
if d, ok := snap.Docs.EventDoc(e.Name); ok {
|
|
names = d.Names
|
|
}
|
|
w.symbol("event:"+e.Name, hostSig("func "+e.Name, e.Sig(), names), eventDoc(snap, e.Name), "", "")
|
|
}
|
|
for _, me := range snap.Bindings.MethodList() {
|
|
md := methodDoc(snap.Docs, me.Recv, me.Name)
|
|
key := "method:" + me.Recv.String() + "." + me.Name
|
|
w.symbol(key, hostSig("func ("+me.Recv.String()+") "+me.Name, me.Sig, md.Params), md.Doc, md.Example, md.Loc)
|
|
}
|
|
return w.stub()
|
|
}
|
|
|
|
func componentStub(snap *workspace.Snapshot) *Stub {
|
|
w := newStubWriter("the engine and native components", "components")
|
|
for _, c := range snap.Bindings.AllComponents() {
|
|
doc := componentDoc(snap.Docs, c.Name)
|
|
w.comment("", doc)
|
|
w.lines["component:"+c.Name] = w.line
|
|
w.write("component " + c.Name + " {")
|
|
for _, f := range c.Fields {
|
|
w.comment("\t", fieldDoc(snap.Docs, c.Name, f.Name))
|
|
w.lines["field:"+c.Name+"."+f.Name] = w.line
|
|
ro := ""
|
|
if f.ReadOnly {
|
|
ro = " readonly"
|
|
}
|
|
w.write("\t" + f.Name + " " + abiType(f.Type) + ro)
|
|
}
|
|
for _, me := range snap.Bindings.ComponentMethods(c.Name) {
|
|
md := compMethodDoc(snap.Docs, c.Name, me.Name)
|
|
w.comment("\t", md.Doc)
|
|
key := "method:" + c.Name + "." + me.Name
|
|
w.lines[key] = w.line
|
|
if md.Loc != "" {
|
|
w.locs[key] = md.Loc
|
|
}
|
|
w.write("\t" + hostSig("func "+me.Name, me.Sig, md.Params))
|
|
}
|
|
w.write("}")
|
|
w.write("")
|
|
}
|
|
return w.stub()
|
|
}
|