aboutsummaryrefslogtreecommitdiff
path: root/src/server/completion.odin
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/completion.odin')
-rw-r--r--src/server/completion.odin547
1 files changed, 287 insertions, 260 deletions
diff --git a/src/server/completion.odin b/src/server/completion.odin
index 8a0f371..31d75ca 100644
--- a/src/server/completion.odin
+++ b/src/server/completion.odin
@@ -410,6 +410,10 @@ score_completion_item :: proc(item: ^CompletionResult, curr_pkg: string) {
if item.symbol.pkg == curr_pkg {
item.score += 1
}
+
+ if strings.has_prefix(item.symbol.name, "_") {
+ item.score -= 0.5
+ }
}
@(private = "file")
@@ -560,80 +564,25 @@ get_attribute_completion :: proc(
}
-DIRECTIVE_NAME_LIST :: []string {
- // basic directives
- "file",
- "directory",
- "line",
- "procedure",
- "caller_location",
- "reverse",
- // call directives
- "location",
- "caller_expression",
- "exists",
- "load",
- "load_directory",
- "load_hash",
- "hash",
- "assert",
- "panic",
- "defined",
- "config",
- /* type helper */
- "type",
- /* struct type */
- "packed",
- "raw_union",
- "align",
- "all_or_none",
- /* union type */
- "no_nil",
- "shared_nil",
- /* array type */
- "simd",
- "soa",
- "sparse",
- /* ptr type */
- "relative",
- /* field flags */
- "no_alias",
- "c_vararg",
- "const",
- "any_int",
- "subtype",
- "by_ptr",
- "no_broadcast",
- "no_capture",
- /* swich flags */
- "partial",
- /* block flags */
- "bounds_check",
- "no_bounds_check",
- "type_assert",
- "no_type_assert",
- /* proc inlining */
- "force_inline",
- "force_no_inline",
- /* return values flags */
- "optional_ok",
- "optional_allocator_error",
-}
-
completion_items_directives: []CompletionResult
@(init)
_init_completion_items_directives :: proc "contextless" () {
context = runtime.default_context()
- completion_items_directives = slice.mapper(DIRECTIVE_NAME_LIST, proc(name: string) -> CompletionResult {
- return CompletionResult {
- completion_item = CompletionItem {
- detail = strings.concatenate({"#", name}) or_else name,
- label = name,
- kind = .Constant,
+ directives := make([dynamic]CompletionResult, 0, len(directive_docs), allocator = context.allocator)
+ for name, doc in directive_docs {
+ documentation := MarkupContent {
+ kind = "markdown",
+ value = doc,
+ }
+ append(
+ &directives,
+ CompletionResult {
+ completion_item = CompletionItem{label = name, kind = .Constant, documentation = documentation},
},
- }
- })
+ )
+ }
+ completion_items_directives = directives[:]
}
get_directive_completion :: proc(
@@ -785,7 +734,10 @@ get_selector_completion :: proc(
selector.type != .Field &&
selector.type != .Package &&
selector.type != .Enum &&
- selector.type != .Function {
+ selector.type != .Function &&
+ (selector.type == .Struct && .Variable not_in selector.flags) {
+ // We don't want completions for struct types, but we do want completions for constant variables.
+ // See tests `ast_global_non_mutable_completion` vs `ast_completion_global_selector_from_local_scope`
return is_incomplete
}
@@ -793,7 +745,7 @@ get_selector_completion :: proc(
field: string
- if position_context.field != nil {
+ if position_context.field != nil && position_in_node(position_context.field, position_context.position) {
#partial switch v in position_context.field.derived {
case ^ast.Ident:
field = v.name
@@ -820,99 +772,7 @@ get_selector_completion :: proc(
case SymbolFixedArrayValue:
is_incomplete = true
append_magic_array_like_completion(position_context, selector, results)
-
- containsColor := 1
- containsCoord := 1
-
- expr_len := 0
-
- if v.len != nil {
- if basic, ok := v.len.derived.(^ast.Basic_Lit); ok {
- if expr_len, ok = strconv.parse_int(basic.tok.text); !ok {
- expr_len = 0
- }
- }
- }
-
- if field != "" {
- for i := 0; i < len(field); i += 1 {
- c := field[i]
- if _, ok := swizzle_color_map[c]; ok {
- containsColor += 1
- } else if _, ok := swizzle_coord_map[c]; ok {
- containsCoord += 1
- } else {
- return is_incomplete
- }
- }
- }
-
- if containsColor == 1 && containsCoord == 1 {
- save := expr_len
- for k in swizzle_color_components {
- if expr_len <= 0 {
- break
- }
-
- expr_len -= 1
-
- item := CompletionItem {
- label = fmt.tprintf("%v%v", field, k),
- kind = .Property,
- detail = fmt.tprintf("%v%v: %v", field, k, node_to_string(v.expr)),
- }
- append(results, CompletionResult{completion_item = item})
- }
-
- expr_len = save
-
- for k in swizzle_coord_components {
- if expr_len <= 0 {
- break
- }
-
- expr_len -= 1
-
- item := CompletionItem {
- label = fmt.tprintf("%v%v", field, k),
- kind = .Property,
- detail = fmt.tprintf("%v%v: %v", field, k, node_to_string(v.expr)),
- }
- append(results, CompletionResult{completion_item = item})
- }
- }
-
- if containsColor > 1 {
- for k in swizzle_color_components {
- if expr_len <= 0 {
- break
- }
-
- expr_len -= 1
-
- item := CompletionItem {
- label = fmt.tprintf("%v%v", field, k),
- kind = .Property,
- detail = fmt.tprintf("%v%v: [%v]%v", field, k, containsColor, node_to_string(v.expr)),
- }
- append(results, CompletionResult{completion_item = item})
- }
- } else if containsCoord > 1 {
- for k in swizzle_coord_components {
- if expr_len <= 0 {
- break
- }
-
- expr_len -= 1
-
- item := CompletionItem {
- label = fmt.tprintf("%v%v", field, k),
- kind = .Property,
- detail = fmt.tprintf("%v%v: [%v]%v", field, k, containsCoord, node_to_string(v.expr)),
- }
- append(results, CompletionResult{completion_item = item})
- }
- }
+ add_fixed_array_selector_completions(v, field, results)
add_soa_field_completion(ast_context, selector, v.expr, v.len, results, selector.name)
case SymbolUnionValue:
is_incomplete = false
@@ -975,6 +835,9 @@ get_selector_completion :: proc(
remove_edit, rok := create_remove_edit(position_context, true)
if !rok {break}
+ // Sublime Text will remove the original `.` for some reason
+ is_sublime := config.client_name == "Sublime Text LSP"
+
for name in enumv.names {
append(
results,
@@ -995,7 +858,7 @@ get_selector_completion :: proc(
label = fmt.tprintf(".%s in", name),
kind = .EnumMember,
detail = in_text,
- insertText = in_text[1:],
+ insertText = is_sublime ? in_text : in_text[1:],
additionalTextEdits = remove_edit,
},
},
@@ -1008,7 +871,7 @@ get_selector_completion :: proc(
label = fmt.tprintf(".%s not_in", name),
kind = .EnumMember,
detail = not_in_text,
- insertText = not_in_text[1:],
+ insertText = is_sublime ? not_in_text : not_in_text[1:],
additionalTextEdits = remove_edit,
},
},
@@ -1019,6 +882,13 @@ get_selector_completion :: proc(
is_incomplete = false
for name, i in v.names {
if name == "_" {
+ if is_struct_field_using(v, i) {
+ if symbol, ok := resolve_type_expression(ast_context, v.types[i]); ok {
+ if value, ok := symbol.value.(SymbolFixedArrayValue); ok {
+ add_fixed_array_selector_completions(value, field, results)
+ }
+ }
+ }
continue
}
@@ -1039,6 +909,12 @@ get_selector_completion :: proc(
construct_struct_field_symbol(&symbol, selector.name, v, i)
append(results, CompletionResult{symbol = symbol})
+
+ if is_struct_field_using(v, i) {
+ if value, ok := symbol.value.(SymbolFixedArrayValue); ok {
+ add_fixed_array_selector_completions(value, field, results)
+ }
+ }
} else {
//just give some generic symbol with name.
item := CompletionItem {
@@ -1090,9 +966,17 @@ get_selector_completion :: proc(
case SymbolPackageValue:
is_incomplete = true
- pkg := selector.pkg
+ packages := make([dynamic]string, context.temp_allocator)
+ if is_builtin_pkg(selector.pkg) {
+ append(&packages, "$builtin")
+ for built in indexer.builtin_packages {
+ append(&packages, built)
+ }
+ } else {
+ append(&packages, selector.pkg)
+ }
- if searched, ok := fuzzy_search(field, {pkg}, ast_context.fullpath); ok {
+ if searched, ok := fuzzy_search(field, packages[:], ast_context.fullpath); ok {
for search in searched {
symbol := search.symbol
@@ -1132,6 +1016,105 @@ get_selector_completion :: proc(
return is_incomplete
}
+add_fixed_array_selector_completions :: proc(
+ v: SymbolFixedArrayValue,
+ field: string,
+ results: ^[dynamic]CompletionResult,
+) {
+ containsColor := 1
+ containsCoord := 1
+
+ expr_len := 0
+
+ if v.len != nil {
+ if basic, ok := v.len.derived.(^ast.Basic_Lit); ok {
+ if expr_len, ok = strconv.parse_int(basic.tok.text); !ok {
+ expr_len = 0
+ }
+ }
+ }
+
+ if field != "" {
+ for i := 0; i < len(field); i += 1 {
+ c := field[i]
+ if _, ok := swizzle_color_map[c]; ok {
+ containsColor += 1
+ } else if _, ok := swizzle_coord_map[c]; ok {
+ containsCoord += 1
+ } else {
+ return
+ }
+ }
+ }
+
+ if containsColor == 1 && containsCoord == 1 {
+ save := expr_len
+ for k in swizzle_color_components {
+ if expr_len <= 0 {
+ break
+ }
+
+ expr_len -= 1
+
+ item := CompletionItem {
+ label = fmt.tprintf("%v%v", field, k),
+ kind = .Property,
+ detail = fmt.tprintf("%v%v: %v", field, k, node_to_string(v.expr)),
+ }
+ append(results, CompletionResult{completion_item = item})
+ }
+
+ expr_len = save
+
+ for k in swizzle_coord_components {
+ if expr_len <= 0 {
+ break
+ }
+
+ expr_len -= 1
+
+ item := CompletionItem {
+ label = fmt.tprintf("%v%v", field, k),
+ kind = .Property,
+ detail = fmt.tprintf("%v%v: %v", field, k, node_to_string(v.expr)),
+ }
+ append(results, CompletionResult{completion_item = item})
+ }
+ }
+
+ if containsColor > 1 {
+ for k in swizzle_color_components {
+ if expr_len <= 0 {
+ break
+ }
+
+ expr_len -= 1
+
+ item := CompletionItem {
+ label = fmt.tprintf("%v%v", field, k),
+ kind = .Property,
+ detail = fmt.tprintf("%v%v: [%v]%v", field, k, containsColor, node_to_string(v.expr)),
+ }
+ append(results, CompletionResult{completion_item = item})
+ }
+ } else if containsCoord > 1 {
+ for k in swizzle_coord_components {
+ if expr_len <= 0 {
+ break
+ }
+
+ expr_len -= 1
+
+ item := CompletionItem {
+ label = fmt.tprintf("%v%v", field, k),
+ kind = .Property,
+ detail = fmt.tprintf("%v%v: [%v]%v", field, k, containsCoord, node_to_string(v.expr)),
+ }
+ append(results, CompletionResult{completion_item = item})
+ }
+ }
+}
+
get_implicit_completion :: proc(
ast_context: ^AstContext,
position_context: ^DocumentPositionContext,
@@ -1254,35 +1237,6 @@ get_implicit_completion :: proc(
}
}
- if position_context.assign != nil &&
- position_context.assign.lhs != nil &&
- len(position_context.assign.lhs) == 1 &&
- is_bitset_assignment_operator(position_context.assign.op.text) {
- //bitsets
- if symbol, ok := resolve_type_expression(ast_context, position_context.assign.lhs[0]); ok {
- set_ast_package_set_scoped(ast_context, symbol.pkg)
- if value, ok := unwrap_bitset(ast_context, symbol); ok {
- for name in value.names {
- if position_context.comp_lit != nil && field_exists_in_comp_lit(position_context.comp_lit, name) {
- continue
- }
-
- item := CompletionItem {
- label = name,
- kind = .EnumMember,
- detail = name,
- }
-
- append(results, CompletionResult{completion_item = item})
- }
-
- return is_incomplete
- }
- }
-
- reset_ast_context(ast_context)
- }
-
if position_context.comp_lit != nil &&
position_context.parent_binary != nil &&
is_bitset_binary_operator(position_context.binary.op.text) {
@@ -1380,53 +1334,6 @@ get_implicit_completion :: proc(
}
}
- if position_context.assign != nil && position_context.assign.rhs != nil && position_context.assign.lhs != nil {
- rhs_index: int
-
- for elem in position_context.assign.rhs {
- if position_in_node(elem, position_context.position) {
- break
- } else {
- //procedures are the only types that can return more than one value
- if symbol, ok := resolve_type_expression(ast_context, elem); ok {
- if procedure, ok := symbol.value.(SymbolProcedureValue); ok {
- if procedure.return_types == nil {
- return is_incomplete
- }
-
- rhs_index += len(procedure.return_types)
- } else {
- rhs_index += 1
- }
- }
- }
- }
-
- if len(position_context.assign.lhs) > rhs_index {
- if enum_value, unwrapped_super_enum, ok := unwrap_enum(
- ast_context,
- position_context.assign.lhs[rhs_index],
- ); ok {
- for name in enum_value.names {
- item := CompletionItem {
- label = name,
- kind = .EnumMember,
- detail = name,
- }
- if unwrapped_super_enum {
- add_implicit_selector_remove_edit(position_context, &item, name, enum_value.names)
- }
-
- append(results, CompletionResult{completion_item = item})
- }
-
- return is_incomplete
- }
- }
-
- reset_ast_context(ast_context)
- }
-
if position_context.returns != nil && position_context.function != nil {
return_index: int
@@ -1527,6 +1434,11 @@ get_implicit_completion :: proc(
if position_context.call != nil {
if call, ok := position_context.call.derived.(^ast.Call_Expr); ok {
parameter_index, parameter_ok := find_position_in_call_param(position_context, call^)
+ old := ast_context.resolve_specific_overload
+ ast_context.resolve_specific_overload = true
+ defer {
+ ast_context.resolve_specific_overload = old
+ }
if symbol, ok := resolve_type_expression(ast_context, call.expr); ok && parameter_ok {
set_ast_package_set_scoped(ast_context, symbol.pkg)
@@ -1559,6 +1471,8 @@ get_implicit_completion :: proc(
type = comp_lit.type
} else if selector, ok := arg_type.default_value.derived.(^ast.Selector_Expr); ok {
type = selector.expr
+ } else {
+ type = arg_type.default_value
}
}
@@ -1599,6 +1513,83 @@ get_implicit_completion :: proc(
reset_ast_context(ast_context)
}
+
+ if position_context.assign != nil &&
+ position_context.assign.lhs != nil &&
+ len(position_context.assign.lhs) == 1 &&
+ is_bitset_assignment_operator(position_context.assign.op.text) {
+ //bitsets
+ if symbol, ok := resolve_type_expression(ast_context, position_context.assign.lhs[0]); ok {
+ set_ast_package_set_scoped(ast_context, symbol.pkg)
+ if value, ok := unwrap_bitset(ast_context, symbol); ok {
+ for name in value.names {
+ if position_context.comp_lit != nil && field_exists_in_comp_lit(position_context.comp_lit, name) {
+ continue
+ }
+
+ item := CompletionItem {
+ label = name,
+ kind = .EnumMember,
+ detail = name,
+ }
+
+ append(results, CompletionResult{completion_item = item})
+ }
+
+ return is_incomplete
+ }
+ }
+
+ reset_ast_context(ast_context)
+ }
+
+ if position_context.assign != nil && position_context.assign.rhs != nil && position_context.assign.lhs != nil {
+ rhs_index: int
+
+ for elem in position_context.assign.rhs {
+ if position_in_node(elem, position_context.position) {
+ break
+ } else {
+ //procedures are the only types that can return more than one value
+ if symbol, ok := resolve_type_expression(ast_context, elem); ok {
+ if procedure, ok := symbol.value.(SymbolProcedureValue); ok {
+ if procedure.return_types == nil {
+ return is_incomplete
+ }
+
+ rhs_index += len(procedure.return_types)
+ } else {
+ rhs_index += 1
+ }
+ }
+ }
+ }
+
+ if len(position_context.assign.lhs) > rhs_index {
+ if enum_value, unwrapped_super_enum, ok := unwrap_enum(
+ ast_context,
+ position_context.assign.lhs[rhs_index],
+ ); ok {
+ for name in enum_value.names {
+ item := CompletionItem {
+ label = name,
+ kind = .EnumMember,
+ detail = name,
+ }
+ if unwrapped_super_enum {
+ add_implicit_selector_remove_edit(position_context, &item, name, enum_value.names)
+ }
+
+ append(results, CompletionResult{completion_item = item})
+ }
+
+ return is_incomplete
+ }
+ }
+
+ reset_ast_context(ast_context)
+ }
+
return is_incomplete
}
@@ -1821,6 +1812,12 @@ get_identifier_completion :: proc(
symbol := Symbol {
name = pkg.base,
type = .Package,
+ pkg = pkg.name,
+ value = SymbolPackageValue{},
+ }
+ try_build_package(symbol.pkg)
+ if resolved, ok := resolve_symbol_return(ast_context, symbol); ok {
+ symbol = resolved
}
if score, ok := common.fuzzy_match(matcher, symbol.name); ok == 1 {
@@ -1881,7 +1878,7 @@ get_package_completion :: proc(
c := without_quotes[0:colon_index]
if colon_index + 1 < len(without_quotes) {
- absolute_path = filepath.join(
+ absolute_path, _ = filepath.join(
elems = {
config.collections[c],
filepath.dir(without_quotes[colon_index + 1:], context.temp_allocator),
@@ -1894,7 +1891,7 @@ get_package_completion :: proc(
} else {
import_file_dir := filepath.dir(position_context.import_stmt.pos.file, context.temp_allocator)
import_dir := filepath.dir(without_quotes, context.temp_allocator)
- absolute_path = filepath.join(elems = {import_file_dir, import_dir}, allocator = context.temp_allocator)
+ absolute_path, _ = filepath.join(elems = {import_file_dir, import_dir}, allocator = context.temp_allocator)
}
if !strings.contains(position_context.import_stmt.fullpath, "/") &&
@@ -1944,7 +1941,7 @@ search_for_packages :: proc(fullpath: string) -> []string {
if files, err := os.read_dir(fh, 0, context.temp_allocator); err == 0 {
for file in files {
- if file.is_dir {
+ if file.type == .Directory {
append(&packages, file.fullpath)
}
}
@@ -1960,12 +1957,52 @@ get_used_switch_name :: proc(node: ^ast.Expr) -> (string, bool) {
return n.name, true
case ^ast.Selector_Expr:
return n.field.name, true
+ case ^ast.Implicit_Selector_Expr:
+ return n.field.name, true
case ^ast.Pointer_Type:
return get_used_switch_name(n.elem)
}
return "", false
}
+//handles pointers / packages
+get_qualified_union_case_name :: proc(
+ symbol: ^Symbol,
+ ast_context: ^AstContext,
+ position_context: ^DocumentPositionContext,
+) -> string {
+ sb := strings.builder_make(context.temp_allocator)
+ pointer_prefix := repeat("^", symbol.pointers, ast_context.allocator)
+ strings.write_string(&sb, pointer_prefix)
+ if symbol.pkg != ast_context.document_package {
+ strings.write_string(&sb, get_symbol_pkg_name(ast_context, symbol))
+ strings.write_string(&sb, ".")
+ }
+ strings.write_string(&sb, symbol.name)
+ #partial switch v in symbol.value {
+ case SymbolUnionValue:
+ write_poly_names(&sb, v.poly_names)
+ case SymbolStructValue:
+ write_poly_names(&sb, v.poly_names)
+ }
+
+ return strings.to_string(sb)
+}
+
+write_poly_names :: proc(sb: ^strings.Builder, poly_names: []string) {
+ if len(poly_names) > 0 {
+ strings.write_string(sb, "(")
+ for name, i in poly_names {
+ strings.write_string(sb, name)
+ if i != len(poly_names) - 1 {
+ strings.write_string(sb, ", ")
+ }
+ }
+ strings.write_string(sb, ")")
+ }
+}
+
+
get_type_switch_completion :: proc(
ast_context: ^AstContext,
position_context: ^DocumentPositionContext,
@@ -1994,7 +2031,8 @@ get_type_switch_completion :: proc(
if union_value, ok := unwrap_union(ast_context, assign.rhs[0]); ok {
for type, i in union_value.types {
if symbol, ok := resolve_type_expression(ast_context, union_value.types[i]); ok {
-
+ //TODO: using symbol.name is wrong for anonymous enums and structs, where the name field is "enum" or "struct" respectively but we want to use the full signature
+ //we also can't use the signature all the time because type aliases need to use specifically the alias name here and not the signature
name := symbol.name
if _, ok := used_unions[name]; ok {
continue
@@ -2003,19 +2041,8 @@ get_type_switch_completion :: proc(
item := CompletionItem {
kind = .EnumMember,
}
-
- if symbol.pkg == ast_context.document_package {
- item.label = fmt.aprintf("%v%v", repeat("^", symbol.pointers, context.temp_allocator), name)
- item.detail = item.label
- } else {
- item.label = fmt.aprintf(
- "%v%v.%v",
- repeat("^", symbol.pointers, context.temp_allocator),
- get_symbol_pkg_name(ast_context, &symbol),
- name,
- )
- item.detail = item.label
- }
+ item.label = get_qualified_union_case_name(&symbol, ast_context, position_context)
+ item.detail = item.label
if position_context.implicit_selector_expr != nil {
if remove_edit, ok := create_implicit_selector_remove_edit(position_context); ok {
item.additionalTextEdits = remove_edit