278 lines
8.7 KiB
Go
278 lines
8.7 KiB
Go
package langsvc
|
|
|
|
import (
|
|
"sort"
|
|
"strconv"
|
|
|
|
"aego/script/ast"
|
|
"aego/script/checker"
|
|
"aego/script/resolver"
|
|
"aego/script/source"
|
|
"aego/script/types"
|
|
"aego/script/workspace"
|
|
)
|
|
|
|
func (s *Service) Hover(file source.FileID, off uint32) (Hover, bool) {
|
|
return s.HoverIn(s.Latest(), file, off)
|
|
}
|
|
|
|
func (s *Service) HoverIn(snap *workspace.Snapshot, file source.FileID, off uint32) (Hover, bool) {
|
|
if snap == nil {
|
|
return Hover{}, false
|
|
}
|
|
f := snap.Files[file]
|
|
if f == nil {
|
|
return Hover{}, false
|
|
}
|
|
if id, stack := identAt(f, off); id != nil {
|
|
if o := object(snap.Analysis, id); o != nil {
|
|
return Hover{Signature: describe(o), Doc: objectDoc(snap, o), Ref: objectRef(snap, o), Span: id.Sp}, true
|
|
}
|
|
if h, ok := hoverMember(snap, id, stack); ok {
|
|
h.Span = id.Sp
|
|
return h, true
|
|
}
|
|
if n := len(stack); n > 0 {
|
|
if a, ok := stack[n-1].(*ast.Attr); ok && a.Name == id {
|
|
if d := builtinText(snap, "@"+id.Name); d != "" {
|
|
return Hover{Signature: "@" + id.Name, Doc: d, Ref: KeyRef("@" + id.Name), Span: id.Sp}, true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return keywordHover(snap, file, off)
|
|
}
|
|
|
|
func hoverMember(snap *workspace.Snapshot, id *ast.Ident, stack []ast.Node) (Hover, bool) {
|
|
a, docs := snap.Analysis, snap.Docs
|
|
if k := componentArg(a, id, stack); k != nil {
|
|
return Hover{Signature: "component " + k.Component.Name, Doc: componentDoc(docs, k.Component.Name), Ref: k.Component.Name}, true
|
|
}
|
|
sel := selectorWith(stack, id)
|
|
if sel == nil {
|
|
return Hover{}, false
|
|
}
|
|
if call := callOf(stack, sel); call != nil {
|
|
if k := a.Calls[call]; k != nil {
|
|
if h, ok := asyncCallHover(snap, k); ok {
|
|
return h, true
|
|
}
|
|
switch k.Kind {
|
|
case checker.CallHost:
|
|
f := funcDoc(docs, k.Module, k.Name)
|
|
return Hover{Signature: hostSig("func "+k.Module+"."+k.Name, k.Sig, f.Params), Doc: hostDoc(f.Doc, f.Example), Ref: k.Module + "." + k.Name}, true
|
|
case checker.CallHostMethod:
|
|
recv := a.TypeOf(k.Recv)
|
|
kind := types.ToABI(recv).Kind
|
|
me := methodDoc(docs, kind, k.Name)
|
|
return Hover{
|
|
Signature: hostSig("func ("+recv.String()+") "+k.Name, k.Sig, me.Params),
|
|
Doc: hostDoc(me.Doc, me.Example),
|
|
Ref: StubBuiltin + "." + kind.String() + "." + k.Name,
|
|
}, true
|
|
case checker.CallComponentMethod:
|
|
me := compMethodDoc(docs, k.Component.Name, k.Name)
|
|
return Hover{
|
|
Signature: hostSig("func ("+k.Component.Short+") "+k.Name, k.Sig, me.Params),
|
|
Doc: hostDoc(me.Doc, me.Example),
|
|
Ref: k.Component.Name + "." + k.Name,
|
|
}, true
|
|
case checker.CallGetComponent:
|
|
return Hover{
|
|
Signature: "func (NodeRef) Get<" + k.Component.Name + ">() ComponentRef<" + k.Component.Short + ">",
|
|
Doc: componentDoc(docs, k.Component.Name),
|
|
Ref: k.Component.Name,
|
|
}, true
|
|
case checker.CallHasComponent:
|
|
return Hover{
|
|
Signature: "func (NodeRef) Has<" + k.Component.Name + ">() bool",
|
|
Doc: componentDoc(docs, k.Component.Name),
|
|
Ref: k.Component.Name,
|
|
}, true
|
|
}
|
|
}
|
|
}
|
|
x := a.Selections[sel]
|
|
if x == nil {
|
|
if t := a.TypeOf(sel); t.Kind == types.KStruct && t.Host {
|
|
if sd, ok := snap.Bindings.Struct(t.Pkg, t.Local); ok {
|
|
return Hover{Signature: structDecl(sd), Doc: hostStructDoc(snap, t.Pkg, t.Local), Ref: t.Pkg + "." + t.Local}, true
|
|
}
|
|
}
|
|
return Hover{}, false
|
|
}
|
|
switch x.Kind {
|
|
case checker.SelComponentField:
|
|
comp := ""
|
|
if t := a.TypeOf(sel.X); t.Component != nil {
|
|
comp = t.Component.Name
|
|
}
|
|
return Hover{
|
|
Signature: "field " + x.Comp.Name + " " + abiType(x.Comp.Type),
|
|
Doc: fieldDoc(docs, comp, x.Comp.Name),
|
|
Ref: comp + "." + x.Comp.Name,
|
|
}, true
|
|
case checker.SelField, checker.SelLane:
|
|
h := Hover{Signature: "field " + x.Field.Name + " " + typeString(x.Field.Type)}
|
|
if d := fieldDecl(a, x.Field); d != nil {
|
|
h.Doc = d.Doc
|
|
}
|
|
return h, true
|
|
case checker.SelEnumMember:
|
|
return Hover{Signature: "const " + sel.Sel.Name + " = " + strconv.FormatInt(x.Value, 10)}, true
|
|
case checker.SelHostConst:
|
|
if c, ok := a.Surface.Const(x.Object.Target, sel.Sel.Name); ok {
|
|
doc := ""
|
|
if mc, ok := docs.ConstDoc(x.Object.Target, sel.Sel.Name); ok {
|
|
doc = mc.Doc
|
|
}
|
|
return Hover{
|
|
Signature: "const " + x.Object.Target + "." + sel.Sel.Name + " " + abiType(c.Type) + " = " + constValue(c),
|
|
Doc: doc,
|
|
Ref: x.Object.Target + "." + sel.Sel.Name,
|
|
}, true
|
|
}
|
|
case checker.SelSignal:
|
|
return Hover{Signature: signalDecl(x.Signal), Doc: x.Signal.Decl.Doc}, true
|
|
}
|
|
return Hover{}, false
|
|
}
|
|
|
|
func (s *Service) Definition(file source.FileID, off uint32) (Location, bool) {
|
|
return s.DefinitionIn(s.Latest(), file, off)
|
|
}
|
|
|
|
func (s *Service) DefinitionIn(snap *workspace.Snapshot, file source.FileID, off uint32) (Location, bool) {
|
|
if snap == nil {
|
|
return Location{}, false
|
|
}
|
|
f := snap.Files[file]
|
|
if f == nil {
|
|
return Location{}, false
|
|
}
|
|
id, stack := identAt(f, off)
|
|
if id == nil {
|
|
return Location{}, false
|
|
}
|
|
a := snap.Analysis
|
|
builtin := StubPath(StubBuiltin)
|
|
comps := StubPath(StubComponents)
|
|
if o := object(a, id); o != nil {
|
|
switch {
|
|
case o.Ident != nil:
|
|
return loc(snap, o.Ident.Sp), true
|
|
case o.Kind == resolver.ObjPkg && o.Host:
|
|
return Location{Native: true, Stub: StubPath(o.Target)}, true
|
|
case o.Kind == resolver.ObjPkg && o.Target != "":
|
|
if rp := a.Symbols.Packages[o.Target]; rp != nil && len(rp.Files) > 0 && rp.Files[0].Package != nil {
|
|
return loc(snap, rp.Files[0].Package.Sp), true
|
|
}
|
|
case o.Kind == resolver.ObjType, o.Kind == resolver.ObjGeneric:
|
|
return Location{Native: true, Stub: builtin, Symbol: "type:" + o.Name}, true
|
|
case o.Kind == resolver.ObjNode:
|
|
return Location{Native: true, Stub: builtin, Symbol: "type:NodeRef"}, true
|
|
case o.Kind == resolver.ObjBuiltin:
|
|
return Location{Native: true, Stub: builtin, Symbol: "func:" + o.Name}, true
|
|
}
|
|
return Location{}, false
|
|
}
|
|
if k := componentArg(a, id, stack); k != nil {
|
|
return Location{Native: true, Stub: comps, Symbol: "component:" + k.Component.Name}, true
|
|
}
|
|
sel := selectorWith(stack, id)
|
|
if sel == nil {
|
|
return Location{}, false
|
|
}
|
|
if call := callOf(stack, sel); call != nil {
|
|
if k := a.Calls[call]; k != nil {
|
|
switch k.Kind {
|
|
case checker.CallHost:
|
|
return Location{Native: true, Stub: StubPath(k.Module), Symbol: "func:" + k.Name}, true
|
|
case checker.CallHostMethod:
|
|
kind := types.ToABI(a.TypeOf(k.Recv)).Kind
|
|
return Location{Native: true, Stub: builtin, Symbol: "method:" + kind.String() + "." + k.Name}, true
|
|
case checker.CallComponentMethod:
|
|
return Location{Native: true, Stub: comps, Symbol: "method:" + k.Component.Name + "." + k.Name}, true
|
|
case checker.CallGetComponent, checker.CallHasComponent:
|
|
return Location{Native: true, Stub: comps, Symbol: "component:" + k.Component.Name}, true
|
|
}
|
|
}
|
|
}
|
|
if x := a.Selections[sel]; x != nil {
|
|
switch x.Kind {
|
|
case checker.SelField:
|
|
if d := fieldDecl(a, x.Field); d != nil {
|
|
return loc(snap, d.Name.Sp), true
|
|
}
|
|
case checker.SelComponentField:
|
|
comp := ""
|
|
if t := a.TypeOf(sel.X); t.Component != nil {
|
|
comp = t.Component.Name
|
|
}
|
|
return Location{Native: true, Stub: comps, Symbol: "field:" + comp + "." + x.Comp.Name}, true
|
|
case checker.SelHostConst:
|
|
return Location{Native: true, Stub: StubPath(x.Object.Target), Symbol: "const:" + sel.Sel.Name}, true
|
|
}
|
|
}
|
|
if t := a.TypeOf(sel); t.Kind == types.KStruct && t.Host {
|
|
return Location{Native: true, Stub: StubPath(t.Pkg), Symbol: "type:" + t.Local}, true
|
|
}
|
|
return Location{}, false
|
|
}
|
|
|
|
func (s *Service) References(file source.FileID, off uint32) []Location {
|
|
return s.ReferencesIn(s.Latest(), file, off)
|
|
}
|
|
|
|
func (s *Service) ReferencesIn(snap *workspace.Snapshot, file source.FileID, off uint32) []Location {
|
|
if snap == nil {
|
|
return nil
|
|
}
|
|
f := snap.Files[file]
|
|
if f == nil {
|
|
return nil
|
|
}
|
|
id, stack := identAt(f, off)
|
|
if id == nil {
|
|
return nil
|
|
}
|
|
a := snap.Analysis
|
|
var out []Location
|
|
if o := object(a, id); o != nil {
|
|
if fld := structField(a, o); fld != nil {
|
|
out = fieldRefs(snap, fld)
|
|
} else {
|
|
for _, r := range a.References {
|
|
if r.Object == o {
|
|
out = append(out, loc(snap, r.Ident.Sp))
|
|
}
|
|
}
|
|
}
|
|
} else if sel := selectorWith(stack, id); sel != nil {
|
|
if x := a.Selections[sel]; x != nil && x.Kind == checker.SelField {
|
|
out = fieldRefs(snap, x.Field)
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool {
|
|
if out[i].Path != out[j].Path {
|
|
return out[i].Path < out[j].Path
|
|
}
|
|
return out[i].Span.Start < out[j].Span.Start
|
|
})
|
|
return out
|
|
}
|
|
|
|
func fieldRefs(snap *workspace.Snapshot, f *types.Field) []Location {
|
|
a := snap.Analysis
|
|
var out []Location
|
|
if d := fieldDecl(a, f); d != nil {
|
|
out = append(out, loc(snap, d.Name.Sp))
|
|
}
|
|
for sel, x := range a.Selections {
|
|
if x.Field == f && x.Kind == checker.SelField {
|
|
out = append(out, loc(snap, sel.Sel.Sp))
|
|
}
|
|
}
|
|
return out
|
|
}
|