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, ast_decl_flags(data.decl), 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) if v != nil { 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 }