diff --git a/README.md b/README.md index 622ce417..f8a7d9ba 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ Options: - `enable_inlay_hints_implicit_return`: Turn on inlay hints for implicit return values. +- `enable_inlay_hints_optional_result`: Adds inlay hints for unhandled optional result value. (#optional_ok and #optional_allocator_error) + - `enable_semantic_tokens`: Turns on syntax highlighting. - `enable_snippets`: Turns on builtin snippets diff --git a/misc/ols.schema.json b/misc/ols.schema.json index 1fc126a9..75900a19 100644 --- a/misc/ols.schema.json +++ b/misc/ols.schema.json @@ -61,6 +61,11 @@ "description": "Turn on inlay hints for implicit return values.", "default": false }, + "enable_inlay_hints_optional_result": { + "type": "boolean", + "description": "Adds inlay hints for unhandled optional result value. (#optional_ok and #optional_allocator_error)", + "default": false + }, "enable_procedure_snippet": { "type": "boolean", "description": "Use snippets when completing procedures—adds parenthesis after the name.", diff --git a/src/common/config.odin b/src/common/config.odin index 693f8ea4..49b3b44a 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -31,6 +31,7 @@ Config :: struct { enable_inlay_hints_params: bool, enable_inlay_hints_default_params: bool, enable_inlay_hints_implicit_return: bool, + enable_inlay_hints_optional_result: bool, enable_procedure_context: bool, enable_snippets: bool, enable_references: bool, diff --git a/src/server/inlay_hints.odin b/src/server/inlay_hints.odin index a62a708c..78daa211 100644 --- a/src/server/inlay_hints.odin +++ b/src/server/inlay_hints.odin @@ -2,7 +2,6 @@ package server import "core:strings" import "core:fmt" -import "core:log" import "core:odin/ast" import "src:common" @@ -64,6 +63,7 @@ get_inlay_hints :: proc( add_param_hints(node, data) add_return_hints(node, data) + add_result_hints(node, data) return visitor }, @@ -338,5 +338,52 @@ get_inlay_hints :: proc( return true } + /* + Adds inlay hints for unhandled optional result value. + #optional_ok and #optional_allocator_error + */ + add_result_hints :: proc ( + node: ^ast.Node, + data: ^Visitor_Data, + ) -> (ok: bool) { + + if !data.config.enable_inlay_hints_optional_result do return + + // a = foo() | a := foo() + lhs, rhs: []^ast.Expr + #partial switch v in node.derived { + case ^ast.Assign_Stmt: lhs, rhs = v.lhs, v.rhs + case ^ast.Value_Decl: lhs, rhs = v.names, v.values + case: return + } + + // optional result can only be handled when there is a single rhs expr + if len(rhs) != 1 do return + + call := rhs[0].derived.(^ast.Call_Expr) or_return + + symbol_and_node := data.symbols[uintptr(call.expr)] or_return // could not resolve symbol + proc_symbol := symbol_and_node.symbol.value.(SymbolProcedureValue) or_return // not a procedure call, e.g. type cast + + if .Optional_Ok not_in proc_symbol.tags && + .Optional_Allocator_Error not_in proc_symbol.tags { + return + } + + // check if all results are handled + results_len: int + for field in proc_symbol.return_types { + results_len += len(field.names) + } + if len(lhs) >= results_len do return + + // a, b[[, _]] := foo() + last := lhs[len(lhs)-1] + range := common.get_token_range(last^, string(data.document.text)) + append(&data.hints, InlayHint{range.end, .Parameter, ", _"}) + + return true + } + return data.hints[:], true } diff --git a/src/server/requests.odin b/src/server/requests.odin index 20e6a30c..651a90df 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -487,6 +487,8 @@ read_ols_initialize_options :: proc(config: ^common.Config, ols_config: OlsConfi ols_config.enable_inlay_hints_default_params.(bool) or_else config.enable_inlay_hints_default_params config.enable_inlay_hints_implicit_return = ols_config.enable_inlay_hints_implicit_return.(bool) or_else config.enable_inlay_hints_implicit_return + config.enable_inlay_hints_optional_result = + ols_config.enable_inlay_hints_optional_result.(bool) or_else config.enable_inlay_hints_optional_result config.enable_fake_method = ols_config.enable_fake_methods.(bool) or_else config.enable_fake_method config.enable_overload_resolution = @@ -801,9 +803,12 @@ request_initialize :: proc( tokenModifiers = semantic_token_modifier_names, }, }, - inlayHintProvider = (config.enable_inlay_hints_params || + inlayHintProvider = ( + config.enable_inlay_hints_params || config.enable_inlay_hints_default_params || - config.enable_inlay_hints_implicit_return), + config.enable_inlay_hints_implicit_return || + config.enable_inlay_hints_optional_result + ), documentSymbolProvider = config.enable_document_symbols, hoverProvider = config.enable_hover, documentFormattingProvider = config.enable_format, diff --git a/src/server/types.odin b/src/server/types.odin index 8c8574b7..c3bfbfb6 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -429,6 +429,7 @@ OlsConfig :: struct { enable_inlay_hints_params: Maybe(bool), enable_inlay_hints_default_params: Maybe(bool), enable_inlay_hints_implicit_return: Maybe(bool), + enable_inlay_hints_optional_result: Maybe(bool), enable_semantic_tokens: Maybe(bool), enable_unused_imports_reporting: Maybe(bool), enable_procedure_context: Maybe(bool), diff --git a/tests/inlay_hints_test.odin b/tests/inlay_hints_test.odin index c1c078b6..d32772a6 100644 --- a/tests/inlay_hints_test.odin +++ b/tests/inlay_hints_test.odin @@ -1,6 +1,5 @@ package tests -import "core:fmt" import "core:testing" import test "src:testing" @@ -227,4 +226,31 @@ ast_inlay_hints_implicit_return_values :: proc(t: ^testing.T) { } test.expect_inlay_hints(t, &source) -} \ No newline at end of file +} + +@(test) +ast_inlay_hints_optional_result :: proc(t: ^testing.T) { + source := test.Source { + main = `package test + + foo :: proc () -> (res: int, ok: bool) #optional_ok { + return + } + bar :: proc () -> (a, b: int, ok: bool) #optional_ok { + return + } + main :: proc () { + res[[, _]] := foo() + res[[, _]] = foo() + a, b[[, _]] := bar() + } + `, + packages = {}, + config = { + enable_inlay_hints_optional_result = true, + }, + } + + test.expect_inlay_hints(t, &source) +} +