aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorNathaniel Saxe <NathanielSaxophone@gmail.com>2026-02-07 15:14:36 -0500
committerNathaniel Saxe <NathanielSaxophone@gmail.com>2026-02-07 15:14:36 -0500
commit7d5b8ede0f4248773d71fbfffcf9cde40c9cc774 (patch)
treeb956e90afb65b994e512bbec0df665be60f0dc53 /src/server
parent342a0e1a401627d76dbc20d22bf9d0788b3760c2 (diff)
refactor switch case info into struct; handle multiple comma-separated variants in case block
Diffstat (limited to 'src/server')
-rw-r--r--src/server/action_populate_switch_cases.odin129
1 files changed, 74 insertions, 55 deletions
diff --git a/src/server/action_populate_switch_cases.odin b/src/server/action_populate_switch_cases.odin
index a733aa5..f038e7c 100644
--- a/src/server/action_populate_switch_cases.odin
+++ b/src/server/action_populate_switch_cases.odin
@@ -27,27 +27,32 @@ get_block_original_text :: proc(block: []^ast.Stmt, document_text: string) -> st
}
SwitchCaseInfo :: struct {
- name: string,
+ names: []string,
body_indentation: string,
body: string,
}
+SwitchBlockInfo :: struct {
+ existing_cases: []SwitchCaseInfo,
+ all_covered_case_names: []string,
+ all_case_names: []string,
+ switch_indentation: string,
+ is_enum: bool,
+}
get_switch_cases_info :: proc(
document: ^Document,
ast_context: ^AstContext,
position_context: ^DocumentPositionContext,
) -> (
- existing_cases: []SwitchCaseInfo,
- all_case_names: []string,
- switch_indentation: string,
- is_enum: bool,
+ info: SwitchBlockInfo,
ok: bool,
) {
if (position_context.switch_stmt == nil && position_context.switch_type_stmt == nil) ||
(position_context.switch_stmt != nil && position_context.switch_stmt.cond == nil) {
- return nil, nil, "", false, false
+ return {}, false
}
switch_block: ^ast.Block_Stmt
found_switch_block: bool
+ is_enum: bool
if position_context.switch_stmt != nil {
switch_block, found_switch_block = position_context.switch_stmt.body.derived.(^ast.Block_Stmt)
is_enum = true
@@ -56,36 +61,40 @@ get_switch_cases_info :: proc(
switch_block, found_switch_block = position_context.switch_type_stmt.body.derived.(^ast.Block_Stmt)
}
if !found_switch_block {
- return nil, nil, "", false, false
+ return {}, false
}
- switch_indentation = get_line_indentation(string(document.text), switch_block.pos.offset)
+ switch_indentation := get_line_indentation(string(document.text), switch_block.pos.offset)
existing_cases_in_order := make([dynamic]SwitchCaseInfo, context.temp_allocator)
+ all_covered_names := make([dynamic]string, context.temp_allocator)
for stmt in switch_block.stmts {
if case_clause, ok := stmt.derived.(^ast.Case_Clause); ok {
- case_name := ""
+ case_names := make([dynamic]string, context.temp_allocator)
for clause in case_clause.list {
if is_enum {
- if name, ok := get_used_switch_name(clause); ok {
- case_name = name
- break
+ if name, ok := get_used_switch_name(clause); ok && name != "" {
+ append(&case_names, name)
+ append(&all_covered_names, name)
}
} else {
reset_ast_context(ast_context)
if symbol, ok := resolve_type_expression(ast_context, clause); ok {
- case_name = get_qualified_union_case_name(&symbol, ast_context, position_context)
+ name := get_qualified_union_case_name(&symbol, ast_context, position_context)
//TODO: this 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
- if case_name == "" {
- case_name = get_signature(ast_context, symbol)
+ if name == "" {
+ name = get_signature(ast_context, symbol)
+ }
+ if name != "" {
+ append(&case_names, name)
+ append(&all_covered_names, name)
}
- break
}
}
}
- if case_name != "" {
+ if len(case_names) > 0 {
case_info := SwitchCaseInfo {
- name = case_name,
- body = get_block_original_text(case_clause.body, string(document.text)),
+ names = case_names[:],
+ body = get_block_original_text(case_clause.body, string(document.text)),
}
append(&existing_cases_in_order, case_info)
}
@@ -94,20 +103,27 @@ get_switch_cases_info :: proc(
if is_enum {
enum_value, was_super_enum, unwrap_ok := unwrap_enum(ast_context, position_context.switch_stmt.cond)
if !unwrap_ok {
- return nil, nil, "", true, false
+ return {}, false
}
- return existing_cases_in_order[:], enum_value.names, switch_indentation, !was_super_enum, true
+ return SwitchBlockInfo {
+ existing_cases = existing_cases_in_order[:],
+ all_covered_case_names = all_covered_names[:],
+ all_case_names = enum_value.names,
+ switch_indentation = switch_indentation,
+ is_enum = !was_super_enum,
+ },
+ true
} else {
st := position_context.switch_type_stmt
if st == nil {
- return nil, nil, "", false, false
+ return {}, false
}
reset_ast_context(ast_context)
union_value, unwrap_ok := unwrap_union(ast_context, st.tag.derived.(^ast.Assign_Stmt).rhs[0])
if !unwrap_ok {
- return nil, nil, "", false, false
+ return {}, false
}
- case_names := make([]string, len(union_value.types), context.temp_allocator)
+ all_case_names := make([]string, len(union_value.types), context.temp_allocator)
for t, i in union_value.types {
reset_ast_context(ast_context)
if symbol, ok := resolve_type_expression(ast_context, t); ok {
@@ -117,21 +133,25 @@ get_switch_cases_info :: proc(
if case_name == "" {
case_name = get_signature(ast_context, symbol)
}
- case_names[i] = case_name
+ all_case_names[i] = case_name
} else {
- case_names[i] = "invalid type expression"
+ all_case_names[i] = "invalid type expression"
}
}
- return existing_cases_in_order[:], case_names, switch_indentation, false, true
+ return SwitchBlockInfo {
+ existing_cases = existing_cases_in_order[:],
+ all_covered_case_names = all_covered_names[:],
+ all_case_names = all_case_names,
+ switch_indentation = switch_indentation,
+ is_enum = false,
+ },
+ true
}
}
create_populate_switch_cases_edit :: proc(
position_context: ^DocumentPositionContext,
- existing_cases: []SwitchCaseInfo,
- switch_indentation: string,
- all_case_names: []string,
- is_enum: bool,
+ info: SwitchBlockInfo,
) -> (
TextEdit,
bool,
@@ -142,29 +162,38 @@ create_populate_switch_cases_edit :: proc(
}
//entirety of the switch block
range: common.Range
- if is_enum {
+ if info.is_enum {
range = common.get_token_range(position_context.switch_stmt.body.stmt_base, position_context.file.src)
} else {
range = common.get_token_range(position_context.switch_type_stmt.body.stmt_base, position_context.file.src)
}
replacement_builder := strings.builder_make()
- dot := is_enum ? "." : ""
+ dot := info.is_enum ? "." : ""
b := &replacement_builder
fmt.sbprintln(b, "{")
- existing_case_names := map[string]struct{}{}
- for case_info in existing_cases {
- existing_case_names[case_info.name] = {}
- fmt.sbprintln(b, switch_indentation, "case ", dot, case_info.name, ":", sep = "")
+ for case_info in info.existing_cases {
+ fmt.sbprint(b, info.switch_indentation, "case ", sep = "")
+ for name, i in case_info.names {
+ fmt.sbprint(b, dot, name, sep = "")
+ if i != len(case_info.names) - 1 {
+ fmt.sbprint(b, ", ", sep = "")
+ }
+ }
+ fmt.sbprintln(b, ":", sep = "")
case_body := case_info.body
if case_body != "" {
fmt.sbprintln(b, case_info.body)
}
}
- for name in all_case_names {
+ existing_case_names := map[string]struct{}{}
+ for name in info.all_covered_case_names {
+ existing_case_names[name] = {}
+ }
+ for name in info.all_case_names {
if name in existing_case_names {continue} //covered by prev loop
- fmt.sbprintln(b, switch_indentation, "case ", dot, name, ":", sep = "")
+ fmt.sbprintln(b, info.switch_indentation, "case ", dot, name, ":", sep = "")
}
- fmt.sbprint(b, switch_indentation, "}", sep = "")
+ fmt.sbprint(b, info.switch_indentation, "}", sep = "")
return TextEdit{range = range, newText = strings.to_string(replacement_builder)}, true
}
@(private = "package")
@@ -175,32 +204,22 @@ add_populate_switch_cases_action :: proc(
uri: string,
actions: ^[dynamic]CodeAction,
) {
- existing_cases, all_case_names, switch_indentation, is_enum, ok := get_switch_cases_info(
- document,
- ast_context,
- position_context,
- )
+ info, ok := get_switch_cases_info(document, ast_context, position_context)
if !ok {return}
all_cases_covered := true
{
existing_case_names := map[string]struct{}{}
- for case_info in existing_cases {
- existing_case_names[case_info.name] = {}
+ for name in info.all_covered_case_names {
+ existing_case_names[name] = {}
}
- for name in all_case_names {
+ for name in info.all_case_names {
if name not_in existing_case_names {
all_cases_covered = false
}
}
}
if all_cases_covered {return} //action not needed
- edit, edit_ok := create_populate_switch_cases_edit(
- position_context,
- existing_cases,
- switch_indentation,
- all_case_names,
- is_enum,
- )
+ edit, edit_ok := create_populate_switch_cases_edit(position_context, info)
if !edit_ok {return}
textEdits := make([dynamic]TextEdit, context.temp_allocator)
append(&textEdits, edit)