diff --git a/handlers.v b/handlers.v index aa6077fe..52d8e8b4 100644 --- a/handlers.v +++ b/handlers.v @@ -43,7 +43,34 @@ fn (mut app App) operation_at_pos(method Method, request Request) Response { '' } } - result := app.run_v_line_info(method, path, line_info) + mut result := app.run_v_line_info(method, path, line_info) + if method == .completion { + // Check the character immediately before the cursor. + // If it is not '.', the user is not doing member access, so augment + // the compiler result with V keywords and builtins. + cursor_line := request.params.position.line + content := app.open_files[path] or { '' } + lines := content.split_into_lines() + trigger_char := if cursor_line < lines.len && col > 0 { + line := lines[cursor_line] + char_col := col - 1 + if char_col < line.len { + line[char_col].ascii_str() + } else { + '' + } + } else { + '' + } + if trigger_char != '.' { + mut details := []Detail{} + if result is []Detail { + details = result as []Detail + } + details << make_keyword_completions() + result = details + } + } log(result.str()) return Response{ id: request.id @@ -1075,3 +1102,31 @@ fn (mut app App) search_symbol_in_project(working_dir string, symbol string) []L } return locations } + +const v_keywords = ['asm', 'as', 'assert', 'atomic', 'break', 'const', 'continue', 'defer', 'dump', + 'else', 'enum', 'false', 'fn', 'for', 'go', 'goto', 'if', 'ilike', 'implements', 'import', + 'in', 'interface', 'is', 'isreftype', 'like', 'lock', 'match', 'module', 'mut', 'nil', 'none', + 'or', 'pub', 'return', 'rlock', 'select', 'shared', 'sizeof', 'spawn', 'static', 'struct', + 'true', 'type', 'typeof', 'union', 'unsafe', 'volatile'] + +const v_builtins = ['close', 'copy', 'eprintln', 'eprint', 'error', 'error_with_code', 'exit', + 'flush_stderr', 'flush_stdout', 'free', 'isnil', 'panic', 'print', 'println'] + +fn make_keyword_completions() []Detail { + mut items := []Detail{} + for kw in v_keywords { + items << Detail{ + kind: 14 // Keyword + label: kw + detail: kw + } + } + for b in v_builtins { + items << Detail{ + kind: 3 // Function + label: b + detail: b + } + } + return items +} diff --git a/handlers_test.v b/handlers_test.v index 662ee24f..b776f65a 100644 --- a/handlers_test.v +++ b/handlers_test.v @@ -2496,6 +2496,102 @@ greeting := get_greeting() } // ============================================================================ +// Tests for keyword/builtin completion augmentation +// ============================================================================ + +fn test_make_keyword_completions_not_empty() { + items := make_keyword_completions() + assert items.len > 0 +} + +fn test_make_keyword_completions_contains_fn_keyword() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'fn' in labels +} + +fn test_make_keyword_completions_fn_has_keyword_kind() { + items := make_keyword_completions() + fn_items := items.filter(it.label == 'fn') + assert fn_items.len > 0 + assert fn_items[0].kind == 14 // Keyword +} + +fn test_make_keyword_completions_contains_println_builtin() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'println' in labels +} + +fn test_make_keyword_completions_println_has_function_kind() { + items := make_keyword_completions() + println_items := items.filter(it.label == 'println') + assert println_items.len > 0 + assert println_items[0].kind == 3 // Function +} + +fn test_make_keyword_completions_contains_struct_keyword() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'struct' in labels +} + +fn test_make_keyword_completions_contains_for_keyword() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'for' in labels +} + +fn test_make_keyword_completions_contains_mut_keyword() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'mut' in labels +} + +fn test_make_keyword_completions_contains_atomic() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'atomic' in labels +} + +fn test_make_keyword_completions_dump_is_keyword_kind() { + items := make_keyword_completions() + dump_items := items.filter(it.label == 'dump') + assert dump_items.len > 0 + assert dump_items[0].kind == 14 // Keyword, not Function +} + +fn test_make_keyword_completions_sizeof_is_keyword_kind() { + items := make_keyword_completions() + sizeof_items := items.filter(it.label == 'sizeof') + assert sizeof_items.len > 0 + assert sizeof_items[0].kind == 14 // Keyword +} + +fn test_make_keyword_completions_no_len() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'len' !in labels +} + +fn test_make_keyword_completions_no_cap() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'cap' !in labels +} + +fn test_make_keyword_completions_no_delete() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'delete' !in labels +} + +fn test_make_keyword_completions_contains_error_with_code() { + items := make_keyword_completions() + labels := items.map(it.label) + assert 'error_with_code' in labels +} + // Tests for get_import_completions // ============================================================================