aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/action.odin1
-rw-r--r--src/server/action_populate_switch_cases.odin184
-rw-r--r--src/server/documents.odin2
3 files changed, 78 insertions, 109 deletions
diff --git a/src/server/action.odin b/src/server/action.odin
index 64516a3..11e8f86 100644
--- a/src/server/action.odin
+++ b/src/server/action.odin
@@ -73,7 +73,6 @@ get_code_actions :: proc(document: ^Document, range: common.Range, config: ^comm
if position_context.switch_stmt != nil || position_context.switch_type_stmt != nil {
add_populate_switch_cases_action(
- document,
&ast_context,
&position_context,
strings.clone(document.uri.uri),
diff --git a/src/server/action_populate_switch_cases.odin b/src/server/action_populate_switch_cases.odin
index f038e7c..1e60f15 100644
--- a/src/server/action_populate_switch_cases.odin
+++ b/src/server/action_populate_switch_cases.odin
@@ -4,6 +4,7 @@ package server
import "core:fmt"
import "core:odin/ast"
+import "core:odin/tokenizer"
import "core:strings"
import "src:common"
@@ -17,6 +18,7 @@ get_line_start_offset :: proc(src: string, offset: int) -> int {
}
return line_start
}
+
get_block_original_text :: proc(block: []^ast.Stmt, document_text: string) -> string {
if len(block) == 0 {
return ""
@@ -26,54 +28,57 @@ get_block_original_text :: proc(block: []^ast.Stmt, document_text: string) -> st
return string(document_text[start:end])
}
-SwitchCaseInfo :: struct {
- 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,
+ names: []string,
+ existing_cases: map[string]struct{},
+ switch_indentation: string,
+ is_enum: bool,
+ pos: tokenizer.Pos,
}
+
get_switch_cases_info :: proc(
- document: ^Document,
ast_context: ^AstContext,
position_context: ^DocumentPositionContext,
) -> (
- info: SwitchBlockInfo,
- ok: bool,
+ SwitchBlockInfo,
+ bool,
) {
- if (position_context.switch_stmt == nil && position_context.switch_type_stmt == nil) ||
- (position_context.switch_stmt != nil && position_context.switch_stmt.cond == nil) {
+ if position_context.switch_stmt == nil && position_context.switch_type_stmt == nil {
return {}, false
}
+
+ if position_context.switch_stmt != nil && position_context.switch_stmt.cond == nil {
+ return {}, false
+ }
+
switch_block: ^ast.Block_Stmt
found_switch_block: bool
is_enum: bool
+ pos: tokenizer.Pos
if position_context.switch_stmt != nil {
switch_block, found_switch_block = position_context.switch_stmt.body.derived.(^ast.Block_Stmt)
is_enum = true
+ pos = position_context.switch_stmt.pos
}
+
if !found_switch_block && position_context.switch_type_stmt != nil {
switch_block, found_switch_block = position_context.switch_type_stmt.body.derived.(^ast.Block_Stmt)
+ pos = position_context.switch_type_stmt.pos
}
+
if !found_switch_block {
return {}, false
}
- 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)
+ switch_indentation := get_line_indentation(ast_context.file.src, switch_block.pos.offset)
+ existing_cases := make(map[string]struct{}, context.temp_allocator)
+
+
for stmt in switch_block.stmts {
if case_clause, ok := stmt.derived.(^ast.Case_Clause); ok {
- 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 && name != "" {
- append(&case_names, name)
- append(&all_covered_names, name)
+ existing_cases[name] = {}
}
} else {
reset_ast_context(ast_context)
@@ -85,19 +90,12 @@ get_switch_cases_info :: proc(
name = get_signature(ast_context, symbol)
}
if name != "" {
- append(&case_names, name)
- append(&all_covered_names, name)
+ existing_cases[name] = {}
}
}
}
}
- if len(case_names) > 0 {
- case_info := SwitchCaseInfo {
- names = case_names[:],
- body = get_block_original_text(case_clause.body, string(document.text)),
- }
- append(&existing_cases_in_order, case_info)
- }
+ pos = case_clause.stmt_base.end
}
}
if is_enum {
@@ -106,47 +104,47 @@ get_switch_cases_info :: proc(
return {}, false
}
return SwitchBlockInfo {
- existing_cases = existing_cases_in_order[:],
- all_covered_case_names = all_covered_names[:],
- all_case_names = enum_value.names,
+ names = enum_value.names,
+ existing_cases = existing_cases,
switch_indentation = switch_indentation,
is_enum = !was_super_enum,
+ pos = pos,
},
true
- } else {
- st := position_context.switch_type_stmt
- if st == nil {
- return {}, false
- }
+ }
+
+ st := position_context.switch_type_stmt
+ if st == nil {
+ 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 {}, false
+ }
+ all_case_names := make([]string, len(union_value.types), context.temp_allocator)
+ for t, i in union_value.types {
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 {}, false
- }
- 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 {
- case_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)
- }
- all_case_names[i] = case_name
- } else {
- all_case_names[i] = "invalid type expression"
+ if symbol, ok := resolve_type_expression(ast_context, t); ok {
+ case_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)
}
+ all_case_names[i] = case_name
+ } else {
+ all_case_names[i] = "invalid type expression"
}
- 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
}
+ return SwitchBlockInfo {
+ names = all_case_names,
+ existing_cases = existing_cases,
+ switch_indentation = switch_indentation,
+ is_enum = false,
+ pos = pos,
+ },
+ true
}
create_populate_switch_cases_edit :: proc(
@@ -160,65 +158,39 @@ create_populate_switch_cases_edit :: proc(
if position_context.switch_stmt == nil && position_context.switch_type_stmt == nil {
return {}, false
}
- //entirety of the switch block
- range: common.Range
- 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)
+
+ pos := info.pos
+ pos.line += 1
+ pos.column = 1
+
+ position := common.token_pos_to_position(pos, position_context.file.src)
+
+ range := common.Range {
+ start = position,
+ end = position,
}
+
replacement_builder := strings.builder_make()
dot := info.is_enum ? "." : ""
b := &replacement_builder
- fmt.sbprintln(b, "{")
- 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)
- }
- }
- 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
+ for name in info.names {
+ if name in info.existing_cases {continue}
fmt.sbprintln(b, info.switch_indentation, "case ", dot, name, ":", sep = "")
}
- fmt.sbprint(b, info.switch_indentation, "}", sep = "")
return TextEdit{range = range, newText = strings.to_string(replacement_builder)}, true
}
+
@(private = "package")
add_populate_switch_cases_action :: proc(
- document: ^Document,
ast_context: ^AstContext,
position_context: ^DocumentPositionContext,
uri: string,
actions: ^[dynamic]CodeAction,
) {
- info, ok := get_switch_cases_info(document, ast_context, position_context)
+ info, ok := get_switch_cases_info(ast_context, position_context)
if !ok {return}
- all_cases_covered := true
- {
- 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 not_in existing_case_names {
- all_cases_covered = false
- }
- }
- }
- if all_cases_covered {return} //action not needed
+
+ if len(info.existing_cases) == len(info.names) {return} //action not needed
edit, edit_ok := create_populate_switch_cases_edit(position_context, info)
if !edit_ok {return}
textEdits := make([dynamic]TextEdit, context.temp_allocator)
diff --git a/src/server/documents.odin b/src/server/documents.odin
index ed1fb52..b9a50dd 100644
--- a/src/server/documents.odin
+++ b/src/server/documents.odin
@@ -4,12 +4,10 @@ import "base:intrinsics"
import "core:fmt"
import "core:log"
-import "core:mem"
import "core:mem/virtual"
import "core:odin/ast"
import "core:odin/parser"
import "core:odin/tokenizer"
-import "core:os"
import "core:path/filepath"
import path "core:path/slashpath"
import "core:strings"