diff options
| author | Brad Lewis <22850972+BradLewis@users.noreply.github.com> | 2025-08-23 19:29:40 -0400 |
|---|---|---|
| committer | Brad Lewis <22850972+BradLewis@users.noreply.github.com> | 2025-08-24 08:55:24 -0400 |
| commit | 9ccd1aa0c7f2ac0d8982750a9ac5c9d5c1e1ea34 (patch) | |
| tree | 8d5c65e5ee7ac3cf098fbed83c66f916d12d1197 | |
| parent | 118db4f7667c91b24fcc21353df7c3cd7a5830b3 (diff) | |
Add configuration for enabling pointer matching on completion
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | misc/ols.schema.json | 12 | ||||
| -rw-r--r-- | src/common/config.odin | 1 | ||||
| -rw-r--r-- | src/server/completion.odin | 53 | ||||
| -rw-r--r-- | src/server/requests.odin | 5 | ||||
| -rw-r--r-- | src/server/types.odin | 1 | ||||
| -rw-r--r-- | src/testing/testing.odin | 19 | ||||
| -rw-r--r-- | tests/completions_test.odin | 9 |
8 files changed, 75 insertions, 27 deletions
@@ -88,6 +88,8 @@ Options: `enable_references`: Turns on finding references for a symbol. _(Enabled by default)_ +`enable_completion_matching`: Attempt to match types and pointers when passing arguments to procedures. _(Enabled by default)_ + `odin_command`: Specify the location to your Odin executable, rather than relying on the environment path. `odin_root_override`: Allows you to specify a custom `ODIN_ROOT` that `ols` will use to look for `odin` core libraries when implementing custom runtimes. diff --git a/misc/ols.schema.json b/misc/ols.schema.json index 8f1ffe7..2d287da 100644 --- a/misc/ols.schema.json +++ b/misc/ols.schema.json @@ -65,10 +65,20 @@ "description": "Automatically import packages that aren't in your import on completion", "default": true }, - "enable_references": { "type": "boolean" }, + "enable_references": { + "type": "boolean", + "description": "Turns on finding references for a symbol.", + "default": true + }, + "enable_completion_matching": { + "type": "boolean", + "description": "Attempt to match types and pointers when passing arguments to procedures.", + "default": true + }, "enable_fake_methods": { "type": "boolean", "description": "Turn on fake methods completion." + "default": false }, "disable_parser_errors": { "type": "boolean" }, "verbose": { diff --git a/src/common/config.odin b/src/common/config.odin index 103004a..e45e2b6 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -34,6 +34,7 @@ Config :: struct { enable_procedure_snippet: bool, enable_checker_only_saved: bool, enable_auto_import: bool, + enable_completion_matching: bool, disable_parser_errors: bool, thread_count: int, file_log: bool, diff --git a/src/server/completion.odin b/src/server/completion.odin index 51a9911..a8a2718 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -48,6 +48,7 @@ get_completion_list :: proc( document: ^Document, position: common.Position, completion_context: CompletionContext, + config: ^common.Config, ) -> ( CompletionList, bool, @@ -165,20 +166,27 @@ get_completion_list :: proc( case .Comp_Lit: is_incomplete = get_comp_lit_completion(&ast_context, &position_context, &results) case .Identifier: - is_incomplete = get_identifier_completion(&ast_context, &position_context, &results) + is_incomplete = get_identifier_completion(&ast_context, &position_context, &results, config) case .Implicit: is_incomplete = get_implicit_completion(&ast_context, &position_context, &results) case .Selector: - is_incomplete = get_selector_completion(&ast_context, &position_context, &results) + is_incomplete = get_selector_completion(&ast_context, &position_context, &results, config) case .Switch_Type: is_incomplete = get_type_switch_completion(&ast_context, &position_context, &results) case .Directive: is_incomplete = get_directive_completion(&ast_context, &position_context, &results) case .Package: - is_incomplete = get_package_completion(&ast_context, &position_context, &results) + is_incomplete = get_package_completion(&ast_context, &position_context, &results, config) } - items := convert_completion_results(&ast_context, &position_context, results[:], completion_type, arg_symbol) + items := convert_completion_results( + &ast_context, + &position_context, + results[:], + completion_type, + arg_symbol, + config, + ) list.items = items list.isIncomplete = is_incomplete return list, true @@ -237,6 +245,7 @@ convert_completion_results :: proc( results: []CompletionResult, completion_type: Completion_Type, symbol: Maybe(Symbol), + config: ^common.Config, ) -> []CompletionItem { slice.sort_by(results[:], proc(i, j: CompletionResult) -> bool { @@ -257,7 +266,7 @@ convert_completion_results :: proc( for result in top_results { result := result if item, ok := result.completion_item.?; ok { - if common.config.enable_label_details { + if config.enable_label_details { item.labelDetails = CompletionItemLabelDetails { description = item.detail, } @@ -283,7 +292,7 @@ convert_completion_results :: proc( //Skip procedures when the position is in proc decl if position_in_proc_decl(position_context) && result.symbol.type == .Function && - common.config.enable_procedure_context { + config.enable_procedure_context { continue } @@ -325,11 +334,13 @@ convert_completion_results :: proc( documentation = write_hover_content(ast_context, result.symbol), } - if s, ok := symbol.(Symbol); ok && (completion_type == .Selector || completion_type == .Identifier) { - handle_pointers(ast_context, position_context, result.symbol, s, &item, completion_type) + if config.enable_completion_matching { + if s, ok := symbol.(Symbol); ok && (completion_type == .Selector || completion_type == .Identifier) { + handle_pointers(ast_context, position_context, result.symbol, s, &item, completion_type) + } } - if common.config.enable_label_details { + if config.enable_label_details { // detail = left // description = right details := CompletionItemLabelDetails{} @@ -348,7 +359,7 @@ convert_completion_results :: proc( // hack for sublime text's issue // remove when this issue is fixed: https://github.com/sublimehq/sublime_text/issues/6033 // or if this PR gets merged: https://github.com/sublimelsp/LSP/pull/2293 - if common.config.client_name == "Sublime Text LSP" { + if config.client_name == "Sublime Text LSP" { if strings.contains(details.detail, "..") && strings.contains(details.detail, "#") { s, _ := strings.replace_all(details.detail, "..", "ꓸꓸ", allocator = context.temp_allocator) details.detail = s @@ -358,7 +369,7 @@ convert_completion_results :: proc( } item.kind = symbol_type_to_completion_kind(result.symbol.type) - if result.symbol.type == .Function && common.config.enable_snippets && common.config.enable_procedure_snippet { + if result.symbol.type == .Function && config.enable_snippets && config.enable_procedure_snippet { item.insertText = fmt.tprintf("%v($0)", item.label) item.insertTextFormat = .Snippet item.deprecated = .Deprecated in result.symbol.flags @@ -371,7 +382,7 @@ convert_completion_results :: proc( } if completion_type == .Identifier { - append_non_imported_packages(ast_context, position_context, &items) + append_non_imported_packages(ast_context, position_context, &items, config) } return items[:] @@ -689,6 +700,7 @@ get_selector_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, results: ^[dynamic]CompletionResult, + config: ^common.Config, ) -> bool { ast_context.current_package = ast_context.document_package @@ -734,7 +746,7 @@ get_selector_completion :: proc( } } - if common.config.enable_fake_method { + if config.enable_fake_method { append_method_completion(ast_context, selector, position_context, results, receiver) } @@ -1521,6 +1533,7 @@ get_identifier_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, results: ^[dynamic]CompletionResult, + config: ^common.Config, ) -> bool { lookup_name := "" is_incomplete := true @@ -1682,7 +1695,7 @@ get_identifier_completion :: proc( } } - if common.config.enable_snippets { + if config.enable_snippets { for k, v in snippets { if score, ok := common.fuzzy_match(matcher, k); ok == 1 { symbol := Symbol { @@ -1700,6 +1713,7 @@ get_package_completion :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, results: ^[dynamic]CompletionResult, + config: ^common.Config, ) -> bool { is_incomplete := false @@ -1724,13 +1738,13 @@ get_package_completion :: proc( if colon_index + 1 < len(without_quotes) { absolute_path = filepath.join( elems = { - common.config.collections[c], + config.collections[c], filepath.dir(without_quotes[colon_index + 1:], context.temp_allocator), }, allocator = context.temp_allocator, ) } else { - absolute_path = common.config.collections[c] + absolute_path = config.collections[c] } } else { import_file_dir := filepath.dir(position_context.import_stmt.pos.file, context.temp_allocator) @@ -1740,7 +1754,7 @@ get_package_completion :: proc( if !strings.contains(position_context.import_stmt.fullpath, "/") && !strings.contains(position_context.import_stmt.fullpath, ":") { - for key, _ in common.config.collections { + for key, _ in config.collections { item := CompletionItem { detail = "collection", label = key, @@ -1902,9 +1916,10 @@ append_non_imported_packages :: proc( ast_context: ^AstContext, position_context: ^DocumentPositionContext, items: ^[dynamic]CompletionItem, + config: ^common.Config, ) { // Keep these as is for now with the completion items as they are a special case - if !common.config.enable_auto_import { + if !config.enable_auto_import { return } @@ -1914,7 +1929,7 @@ append_non_imported_packages :: proc( continue } for pkg in pkgs { - fullpath := path.join({common.config.collections[collection], pkg}) + fullpath := path.join({config.collections[collection], pkg}) found := false for doc_pkg in ast_context.imports { diff --git a/src/server/requests.odin b/src/server/requests.odin index 0572a33..c564a31 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -365,6 +365,8 @@ read_ols_initialize_options :: proc(config: ^common.Config, ols_config: OlsConfi ols_config.enable_procedure_context.(bool) or_else config.enable_procedure_context config.enable_snippets = ols_config.enable_snippets.(bool) or_else config.enable_snippets config.enable_references = ols_config.enable_references.(bool) or_else config.enable_references + config.enable_completion_matching = + ols_config.enable_completion_matching.(bool) or_else config.enable_completion_matching config.verbose = ols_config.verbose.(bool) or_else config.verbose config.file_log = ols_config.file_log.(bool) or_else config.file_log @@ -612,6 +614,7 @@ request_initialize :: proc( config.enable_procedure_context = false config.enable_snippets = false config.enable_references = true + config.enable_completion_matching = true config.verbose = false config.file_log = false config.odin_command = "" @@ -901,7 +904,7 @@ request_completion :: proc( } list: CompletionList - list, ok = get_completion_list(document, completition_params.position, completition_params.context_) + list, ok = get_completion_list(document, completition_params.position, completition_params.context_, config) if !ok { return .InternalError diff --git a/src/server/types.odin b/src/server/types.odin index 0eda132..4795f11 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -417,6 +417,7 @@ OlsConfig :: struct { enable_procedure_snippet: Maybe(bool), enable_checker_only_saved: Maybe(bool), enable_auto_import: Maybe(bool), + enable_completion_matching: Maybe(bool), disable_parser_errors: Maybe(bool), verbose: Maybe(bool), file_log: Maybe(bool), diff --git a/src/testing/testing.odin b/src/testing/testing.odin index ca3fa1e..1b40c6b 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -174,7 +174,7 @@ expect_completion_labels :: proc(t: ^testing.T, src: ^Source, trigger_character: triggerCharacter = trigger_character, } - completion_list, ok := server.get_completion_list(src.document, src.position, completion_context) + completion_list, ok := server.get_completion_list(src.document, src.position, completion_context, &src.config) if !ok { log.error("Failed get_completion_list") @@ -202,7 +202,8 @@ expect_completion_labels :: proc(t: ^testing.T, src: ^Source, trigger_character: } expect_completion_docs :: proc( - t: ^testing.T, src: ^Source, + t: ^testing.T, + src: ^Source, trigger_character: string, expect_details: []string, expect_excluded: []string = nil, @@ -226,7 +227,7 @@ expect_completion_docs :: proc( triggerCharacter = trigger_character, } - completion_list, ok := server.get_completion_list(src.document, src.position, completion_context) + completion_list, ok := server.get_completion_list(src.document, src.position, completion_context, &src.config) if !ok { log.error("Failed get_completion_list") @@ -261,7 +262,12 @@ expect_completion_docs :: proc( } } -expect_completion_insert_text :: proc(t: ^testing.T, src: ^Source, trigger_character: string, expect_inserts: []string) { +expect_completion_insert_text :: proc( + t: ^testing.T, + src: ^Source, + trigger_character: string, + expect_inserts: []string, +) { setup(src) defer teardown(src) @@ -269,7 +275,7 @@ expect_completion_insert_text :: proc(t: ^testing.T, src: ^Source, trigger_chara triggerCharacter = trigger_character, } - completion_list, ok := server.get_completion_list(src.document, src.position, completion_context) + completion_list, ok := server.get_completion_list(src.document, src.position, completion_context, &src.config) if !ok { log.error("Failed get_completion_list") @@ -512,7 +518,8 @@ expect_inlay_hints :: proc(t: ^testing.T, src: ^Source, expected_hints: []server return } - testing.expectf(t, + testing.expectf( + t, len(expected_hints) == len(hints), "\nExpected %d inlay hints, but received %d", len(expected_hints), diff --git a/tests/completions_test.odin b/tests/completions_test.odin index b496774..0a33bb3 100644 --- a/tests/completions_test.odin +++ b/tests/completions_test.odin @@ -4471,6 +4471,9 @@ ast_completions_handle_pointers_basic_types :: proc(t: ^testing.T) { bar(f{*}) } `, + config = { + enable_completion_matching = true, + } } test.expect_completion_insert_text(t, &source, "", {"&foo"}) } @@ -4489,6 +4492,9 @@ ast_completions_handle_pointers_struct :: proc(t: ^testing.T) { bar(f{*}) } `, + config = { + enable_completion_matching = true, + } } test.expect_completion_insert_text(t, &source, "", {"&foo"}) } @@ -4503,6 +4509,9 @@ ast_completions_handle_pointers_append :: proc(t: ^testing.T) { append(fo{*}) } `, + config = { + enable_completion_matching = true, + } } test.expect_completion_insert_text(t, &source, "", {"&foos"}) } |