diff --git a/vlib/v/checker/autocomplete.v b/vlib/v/checker/autocomplete.v index 11a7bc9db0..60d8612be8 100644 --- a/vlib/v/checker/autocomplete.v +++ b/vlib/v/checker/autocomplete.v @@ -18,6 +18,9 @@ fn abs(a int) int { return a } +pub fn (mut c Checker) run_ac(ast_file &ast.File) { +} + fn (mut c Checker) ident_autocomplete(node ast.Ident) { // Mini LS hack (v -line-info "a.v:16") if c.pref.is_verbose { @@ -25,16 +28,14 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) { 'checker.ident_autocomplete() info.line_nr=${c.pref.linfo.line_nr} node.line_nr=${node.pos.line_nr} ' + ' node.col=${node.pos.col} pwd="${os.getwd()}" file="${c.file.path}", ' + //' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" expr="${c.pref.linfo.expr}"') - ' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" col="${c.pref.linfo.col}"') + ' pref.linfo.path="${c.pref.linfo.path}" node.name="${node.name}" node.mod="${node.mod}" col="${c.pref.linfo.col}"') } // Make sure this ident is on the same line as requeste, in the same file, and has the same name same_line := node.pos.line_nr in [c.pref.linfo.line_nr - 1, c.pref.linfo.line_nr + 1, c.pref.linfo.line_nr] if !same_line { return } - // same_name := c.pref.linfo.expr == node.name same_col := abs(c.pref.linfo.col - node.pos.col) < 3 - // if !same_name { if !same_col { return } @@ -42,6 +43,14 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) { if c.pref.linfo.path !in [c.file.path, abs_path] { return } + // Module autocomplete + // `os. ...` + if node.name == '' && node.mod != 'builtin' { + c.module_autocomplete(node) + return + } else if node.name == '' && node.mod == 'builtin' { + return + } mut sb := strings.new_builder(10) if node.kind == .unresolved { // println(node) @@ -67,6 +76,7 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) { */ mut fields := []ACFieldMethod{cap: 10} + mut methods := []ACFieldMethod{cap: 10} if sym.kind == .struct { // Add fields, but only if it's a struct. struct_info := sym.info as ast.Struct @@ -85,11 +95,13 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) { fields << ACFieldMethod{'cap', 'int'} } // array_info := sym.info as ast.Array + } else if sym.kind == .string { + fields << ACFieldMethod{'len', 'int'} } // Aliases and other types can have methods, add them for method in sym.methods { method_ret_type := c.table.sym(method.return_type) - fields << ACFieldMethod{build_method_summary(method), method_ret_type.name} + methods << ACFieldMethod{build_method_summary(method), method_ret_type.name} } fields.sort(a.name < b.name) for i, field in fields { @@ -99,6 +111,14 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) { sb.writeln(', ') } } + sb.writeln('\n\t], "methods":[') + + for i, method in methods { + sb.write_string('\t\t"${method.name}:${method.typ}"') + if i < methods.len - 1 { + sb.writeln(', ') + } + } sb.writeln('\n\t]\n}') res := sb.str().trim_space() if res != '' { @@ -108,12 +128,39 @@ fn (mut c Checker) ident_autocomplete(node ast.Ident) { } } +fn (mut c Checker) module_autocomplete(node ast.Ident) { + mut sb := strings.new_builder(10) + // println(c.table.fns) + sb.writeln('{"methods":[') + prefix := node.mod + '.' + mut empty := true + for _, f in c.table.fns { + mut name := f.name + if name.starts_with(prefix) { + empty = false + if name.contains('__static__') { + name = name.replace('__static__', '.') + } + name = name.after('.') // The user already typed `mod.`, so suggest the name without module + sb.writeln('"${name}:int" ,') + } + } + if !empty { + sb.go_back(2) // remove final , + } + sb.writeln(']}') + println(sb.str().trim_space()) +} + fn build_method_summary(method ast.Fn) string { mut s := method.name + '(' for i, param in method.params { + if i == 0 { + continue + } s += param.name if i < method.params.len - 1 { - s += ',' + s += ', ' } } return s + ')' diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index a3a252e460..98ab39bd80 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -444,6 +444,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) { abs_path := os.join_path(os.getwd(), file.path).replace('/./', '/') // TODO: join_path shouldn't have /./ if abs_path == c.pref.linfo.path { c.check_files([ast_files[i]]) + c.run_ac(ast_files[i]) exit(0) } } @@ -4054,7 +4055,8 @@ fn (mut c Checker) resolve_var_fn(func &ast.Fn, mut node ast.Ident, name string) fn (mut c Checker) ident(mut node ast.Ident) ast.Type { if c.pref.linfo.is_running { - // Mini LS hack (v -line-info "a.v:16") + // LS hack (v -line-info "a.v:16") + // TODO perf $if c.ident_autocomplete(node) } // TODO: move this diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 3902521a1c..e13ebd5487 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1490,8 +1490,25 @@ fn (mut p Parser) name_expr() ast.Expr { // prepend the full import mod = p.imports[p.tok.lit] } + if p.pref.linfo.is_running { + // VLS autocomplete for module fns: `os...` + // TODO perf $if + // p.module_autocomplete(node) + } + line_nr := p.tok.line_nr p.next() p.check(.dot) + if p.is_vls && p.tok.line_nr != line_nr { + // The user typed `os.`, we have to display all possible `os` functions. + // Turn this name expression into an Ident, since that is what expected + // by `Checker.ident_autocomplete()` + return ast.Ident{ + name: '' + mod: mod + pos: p.prev_tok.pos() + } + } + p.expr_mod = mod } lit0_is_capital := if p.tok.kind != .eof && p.tok.lit.len > 0 {