419 lines
9.6 KiB
Go
419 lines
9.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/scanner"
|
|
"go/token"
|
|
"log"
|
|
)
|
|
|
|
func parse_decl_list(fset *token.FileSet, data []byte) ([]ast.Decl, error) {
|
|
var buf bytes.Buffer
|
|
buf.WriteString("package p;")
|
|
buf.Write(data)
|
|
file, err := parser.ParseFile(fset, "", buf.Bytes(), parser.AllErrors)
|
|
if err != nil {
|
|
return file.Decls, err
|
|
}
|
|
return file.Decls, nil
|
|
}
|
|
|
|
func log_parse_error(intro string, err error) {
|
|
if el, ok := err.(scanner.ErrorList); ok {
|
|
log.Printf("%s:", intro)
|
|
for _, er := range el {
|
|
log.Printf(" %s", er)
|
|
}
|
|
} else {
|
|
log.Printf("%s: %s", intro, err)
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// auto_complete_file
|
|
//-------------------------------------------------------------------------
|
|
|
|
type auto_complete_file struct {
|
|
name string
|
|
package_name string
|
|
|
|
decls map[string]*decl
|
|
packages []package_import
|
|
filescope *scope
|
|
scope *scope
|
|
|
|
cursor int // for current file buffer only
|
|
fset *token.FileSet
|
|
context *package_lookup_context
|
|
}
|
|
|
|
func new_auto_complete_file(name string, context *package_lookup_context) *auto_complete_file {
|
|
p := new(auto_complete_file)
|
|
p.name = name
|
|
p.cursor = -1
|
|
p.fset = token.NewFileSet()
|
|
p.context = context
|
|
return p
|
|
}
|
|
|
|
func (f *auto_complete_file) offset(p token.Pos) int {
|
|
const fixlen = len("package p;")
|
|
return f.fset.Position(p).Offset - fixlen
|
|
}
|
|
|
|
// this one is used for current file buffer exclusively
|
|
func (f *auto_complete_file) process_data(data []byte) {
|
|
cur, filedata, block := rip_off_decl(data, f.cursor)
|
|
file, err := parser.ParseFile(f.fset, "", filedata, parser.AllErrors)
|
|
if err != nil && *g_debug {
|
|
log_parse_error("Error parsing input file (outer block)", err)
|
|
}
|
|
f.package_name = package_name(file)
|
|
|
|
f.decls = make(map[string]*decl)
|
|
f.packages = collect_package_imports(f.name, file.Decls, f.context)
|
|
f.filescope = new_scope(nil)
|
|
f.scope = f.filescope
|
|
|
|
for _, d := range file.Decls {
|
|
anonymify_ast(d, 0, f.filescope)
|
|
}
|
|
|
|
// process all top-level declarations
|
|
for _, decl := range file.Decls {
|
|
append_to_top_decls(f.decls, decl, f.scope)
|
|
}
|
|
if block != nil {
|
|
// process local function as top-level declaration
|
|
decls, err := parse_decl_list(f.fset, block)
|
|
if err != nil && *g_debug {
|
|
log_parse_error("Error parsing input file (inner block)", err)
|
|
}
|
|
|
|
for _, d := range decls {
|
|
anonymify_ast(d, 0, f.filescope)
|
|
}
|
|
|
|
for _, decl := range decls {
|
|
append_to_top_decls(f.decls, decl, f.scope)
|
|
}
|
|
|
|
// process function internals
|
|
f.cursor = cur
|
|
for _, decl := range decls {
|
|
f.process_decl_locals(decl)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func (f *auto_complete_file) process_decl_locals(decl ast.Decl) {
|
|
switch t := decl.(type) {
|
|
case *ast.FuncDecl:
|
|
if f.cursor_in(t.Body) {
|
|
s := f.scope
|
|
f.scope = new_scope(f.scope)
|
|
|
|
f.process_field_list(t.Recv, s)
|
|
f.process_field_list(t.Type.Params, s)
|
|
f.process_field_list(t.Type.Results, s)
|
|
f.process_block_stmt(t.Body)
|
|
}
|
|
default:
|
|
v := new(func_lit_visitor)
|
|
v.ctx = f
|
|
ast.Walk(v, decl)
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) process_decl(decl ast.Decl) {
|
|
if t, ok := decl.(*ast.GenDecl); ok && f.offset(t.TokPos) > f.cursor {
|
|
return
|
|
}
|
|
prevscope := f.scope
|
|
foreach_decl(decl, func(data *foreach_decl_struct) {
|
|
class := ast_decl_class(data.decl)
|
|
if class != decl_type {
|
|
f.scope, prevscope = advance_scope(f.scope)
|
|
}
|
|
for i, name := range data.names {
|
|
typ, v, vi := data.type_value_index(i)
|
|
|
|
d := new_decl_full(name.Name, class, 0, typ, v, vi, prevscope)
|
|
if d == nil {
|
|
return
|
|
}
|
|
|
|
f.scope.add_named_decl(d)
|
|
}
|
|
})
|
|
}
|
|
|
|
func (f *auto_complete_file) process_block_stmt(block *ast.BlockStmt) {
|
|
if block != nil && f.cursor_in(block) {
|
|
f.scope, _ = advance_scope(f.scope)
|
|
|
|
for _, stmt := range block.List {
|
|
f.process_stmt(stmt)
|
|
}
|
|
|
|
// hack to process all func literals
|
|
v := new(func_lit_visitor)
|
|
v.ctx = f
|
|
ast.Walk(v, block)
|
|
}
|
|
}
|
|
|
|
type func_lit_visitor struct {
|
|
ctx *auto_complete_file
|
|
}
|
|
|
|
func (v *func_lit_visitor) Visit(node ast.Node) ast.Visitor {
|
|
if t, ok := node.(*ast.FuncLit); ok && v.ctx.cursor_in(t.Body) {
|
|
s := v.ctx.scope
|
|
v.ctx.scope = new_scope(v.ctx.scope)
|
|
|
|
v.ctx.process_field_list(t.Type.Params, s)
|
|
v.ctx.process_field_list(t.Type.Results, s)
|
|
v.ctx.process_block_stmt(t.Body)
|
|
|
|
return nil
|
|
}
|
|
return v
|
|
}
|
|
|
|
func (f *auto_complete_file) process_stmt(stmt ast.Stmt) {
|
|
switch t := stmt.(type) {
|
|
case *ast.DeclStmt:
|
|
f.process_decl(t.Decl)
|
|
case *ast.AssignStmt:
|
|
f.process_assign_stmt(t)
|
|
case *ast.IfStmt:
|
|
if f.cursor_in_if_head(t) {
|
|
f.process_stmt(t.Init)
|
|
} else if f.cursor_in_if_stmt(t) {
|
|
f.scope, _ = advance_scope(f.scope)
|
|
f.process_stmt(t.Init)
|
|
f.process_block_stmt(t.Body)
|
|
f.process_stmt(t.Else)
|
|
}
|
|
case *ast.BlockStmt:
|
|
f.process_block_stmt(t)
|
|
case *ast.RangeStmt:
|
|
f.process_range_stmt(t)
|
|
case *ast.ForStmt:
|
|
if f.cursor_in_for_head(t) {
|
|
f.process_stmt(t.Init)
|
|
} else if f.cursor_in(t.Body) {
|
|
f.scope, _ = advance_scope(f.scope)
|
|
|
|
f.process_stmt(t.Init)
|
|
f.process_block_stmt(t.Body)
|
|
}
|
|
case *ast.SwitchStmt:
|
|
f.process_switch_stmt(t)
|
|
case *ast.TypeSwitchStmt:
|
|
f.process_type_switch_stmt(t)
|
|
case *ast.SelectStmt:
|
|
f.process_select_stmt(t)
|
|
case *ast.LabeledStmt:
|
|
f.process_stmt(t.Stmt)
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) process_select_stmt(a *ast.SelectStmt) {
|
|
if !f.cursor_in(a.Body) {
|
|
return
|
|
}
|
|
var prevscope *scope
|
|
f.scope, prevscope = advance_scope(f.scope)
|
|
|
|
var last_cursor_after *ast.CommClause
|
|
for _, s := range a.Body.List {
|
|
if cc := s.(*ast.CommClause); f.cursor > f.offset(cc.Colon) {
|
|
last_cursor_after = cc
|
|
}
|
|
}
|
|
|
|
if last_cursor_after != nil {
|
|
if last_cursor_after.Comm != nil {
|
|
//if lastCursorAfter.Lhs != nil && lastCursorAfter.Tok == token.DEFINE {
|
|
if astmt, ok := last_cursor_after.Comm.(*ast.AssignStmt); ok && astmt.Tok == token.DEFINE {
|
|
vname := astmt.Lhs[0].(*ast.Ident).Name
|
|
v := new_decl_var(vname, nil, astmt.Rhs[0], -1, prevscope)
|
|
f.scope.add_named_decl(v)
|
|
}
|
|
}
|
|
for _, s := range last_cursor_after.Body {
|
|
f.process_stmt(s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) process_type_switch_stmt(a *ast.TypeSwitchStmt) {
|
|
if !f.cursor_in(a.Body) {
|
|
return
|
|
}
|
|
var prevscope *scope
|
|
f.scope, prevscope = advance_scope(f.scope)
|
|
|
|
f.process_stmt(a.Init)
|
|
// type var
|
|
var tv *decl
|
|
if a, ok := a.Assign.(*ast.AssignStmt); ok {
|
|
lhs := a.Lhs
|
|
rhs := a.Rhs
|
|
if lhs != nil && len(lhs) == 1 {
|
|
tvname := lhs[0].(*ast.Ident).Name
|
|
tv = new_decl_var(tvname, nil, rhs[0], -1, prevscope)
|
|
}
|
|
}
|
|
|
|
var last_cursor_after *ast.CaseClause
|
|
for _, s := range a.Body.List {
|
|
if cc := s.(*ast.CaseClause); f.cursor > f.offset(cc.Colon) {
|
|
last_cursor_after = cc
|
|
}
|
|
}
|
|
|
|
if last_cursor_after != nil {
|
|
if tv != nil {
|
|
if last_cursor_after.List != nil && len(last_cursor_after.List) == 1 {
|
|
tv.typ = last_cursor_after.List[0]
|
|
tv.value = nil
|
|
}
|
|
f.scope.add_named_decl(tv)
|
|
}
|
|
for _, s := range last_cursor_after.Body {
|
|
f.process_stmt(s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) process_switch_stmt(a *ast.SwitchStmt) {
|
|
if !f.cursor_in(a.Body) {
|
|
return
|
|
}
|
|
f.scope, _ = advance_scope(f.scope)
|
|
|
|
f.process_stmt(a.Init)
|
|
var last_cursor_after *ast.CaseClause
|
|
for _, s := range a.Body.List {
|
|
if cc := s.(*ast.CaseClause); f.cursor > f.offset(cc.Colon) {
|
|
last_cursor_after = cc
|
|
}
|
|
}
|
|
if last_cursor_after != nil {
|
|
for _, s := range last_cursor_after.Body {
|
|
f.process_stmt(s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) process_range_stmt(a *ast.RangeStmt) {
|
|
if !f.cursor_in(a.Body) {
|
|
return
|
|
}
|
|
var prevscope *scope
|
|
f.scope, prevscope = advance_scope(f.scope)
|
|
|
|
if a.Tok == token.DEFINE {
|
|
if t, ok := a.Key.(*ast.Ident); ok {
|
|
d := new_decl_var(t.Name, nil, a.X, 0, prevscope)
|
|
if d != nil {
|
|
d.flags |= decl_rangevar
|
|
f.scope.add_named_decl(d)
|
|
}
|
|
}
|
|
|
|
if a.Value != nil {
|
|
if t, ok := a.Value.(*ast.Ident); ok {
|
|
d := new_decl_var(t.Name, nil, a.X, 1, prevscope)
|
|
if d != nil {
|
|
d.flags |= decl_rangevar
|
|
f.scope.add_named_decl(d)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f.process_block_stmt(a.Body)
|
|
}
|
|
|
|
func (f *auto_complete_file) process_assign_stmt(a *ast.AssignStmt) {
|
|
if a.Tok != token.DEFINE || f.offset(a.TokPos) > f.cursor {
|
|
return
|
|
}
|
|
|
|
names := make([]*ast.Ident, len(a.Lhs))
|
|
for i, name := range a.Lhs {
|
|
id, ok := name.(*ast.Ident)
|
|
if !ok {
|
|
// something is wrong, just ignore the whole stmt
|
|
return
|
|
}
|
|
names[i] = id
|
|
}
|
|
|
|
var prevscope *scope
|
|
f.scope, prevscope = advance_scope(f.scope)
|
|
|
|
pack := decl_pack{names, nil, a.Rhs}
|
|
for i, name := range pack.names {
|
|
typ, v, vi := pack.type_value_index(i)
|
|
d := new_decl_var(name.Name, typ, v, vi, prevscope)
|
|
if d == nil {
|
|
continue
|
|
}
|
|
|
|
f.scope.add_named_decl(d)
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) process_field_list(field_list *ast.FieldList, s *scope) {
|
|
if field_list != nil {
|
|
decls := ast_field_list_to_decls(field_list, decl_var, 0, s, false)
|
|
for _, d := range decls {
|
|
f.scope.add_named_decl(d)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *auto_complete_file) cursor_in_if_head(s *ast.IfStmt) bool {
|
|
if f.cursor > f.offset(s.If) && f.cursor <= f.offset(s.Body.Lbrace) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (f *auto_complete_file) cursor_in_if_stmt(s *ast.IfStmt) bool {
|
|
if f.cursor > f.offset(s.If) {
|
|
// magic -10 comes from auto_complete_file.offset method, see
|
|
// len() expr in there
|
|
if f.offset(s.End()) == -10 || f.cursor < f.offset(s.End()) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (f *auto_complete_file) cursor_in_for_head(s *ast.ForStmt) bool {
|
|
if f.cursor > f.offset(s.For) && f.cursor <= f.offset(s.Body.Lbrace) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (f *auto_complete_file) cursor_in(block *ast.BlockStmt) bool {
|
|
if f.cursor == -1 || block == nil {
|
|
return false
|
|
}
|
|
|
|
if f.cursor > f.offset(block.Lbrace) && f.cursor <= f.offset(block.Rbrace) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|