diff --git a/src/server/action.odin b/src/server/action.odin index d1608b5e..86ce19ac 100644 --- a/src/server/action.odin +++ b/src/server/action.odin @@ -26,6 +26,11 @@ CodeActionOptions :: struct { CodeActionParams :: struct { textDocument: TextDocumentIdentifier, range: common.Range, + context_: CodeActionContext, +} + +CodeActionContext :: struct { + only: []CodeActionKind, } CodeAction :: struct { @@ -35,7 +40,15 @@ CodeAction :: struct { edit: WorkspaceEdit, } -get_code_actions :: proc(document: ^Document, range: common.Range, config: ^common.Config) -> ([]CodeAction, bool) { +get_code_actions :: proc( + document: ^Document, + ctx: CodeActionContext, + range: common.Range, + config: ^common.Config, +) -> ( + []CodeAction, + bool, +) { ast_context := make_ast_context( document.ast, document.imports, @@ -45,7 +58,18 @@ get_code_actions :: proc(document: ^Document, range: common.Range, config: ^comm context.temp_allocator, ) + actions := make([dynamic]CodeAction, 0, context.allocator) + + for action in ctx.only { + //For some reason on vscode it returns "source", so check for both kinds + if action == "source" || action == "source.organizeImports" { + source_organize_imports(document, strings.clone(document.uri.uri, context.temp_allocator), config, &actions) + return actions[:], true + } + } + position_context, ok := get_document_position_context(document, range.start, .Hover) + if !ok { log.warn("Failed to get position context") return {}, false @@ -61,19 +85,72 @@ get_code_actions :: proc(document: ^Document, range: common.Range, config: ^comm get_locals(document.ast, position_context.function, &ast_context, &position_context) } - actions := make([dynamic]CodeAction, 0, context.allocator) - if position_context.selector_expr != nil { if selector, ok := position_context.selector_expr.derived.(^ast.Selector_Expr); ok { - add_missing_imports(&ast_context, selector, strings.clone(document.uri.uri), config, &actions) + add_missing_imports( + &ast_context, + selector, + strings.clone(document.uri.uri, context.temp_allocator), + config, + &actions, + ) } } else if position_context.import_stmt != nil { - remove_unused_imports(document, strings.clone(document.uri.uri), config, &actions) + remove_unused_imports(document, strings.clone(document.uri.uri, context.temp_allocator), config, &actions) } return actions[:], true } +source_organize_imports :: proc( + document: ^Document, + uri: string, + config: ^common.Config, + actions: ^[dynamic]CodeAction, +) { + unused_imports := find_unused_imports(document, context.temp_allocator) + + if len(unused_imports) == 0 { + return + } + + textEdits := make([dynamic]TextEdit, context.temp_allocator) + + for imp in unused_imports { + range := common.get_token_range(imp.import_decl, document.ast.src) + + import_edit := TextEdit { + range = range, + newText = "", + } + + if (range.start.line != 1) { + if column, ok := common.get_last_column(import_edit.range.start.line - 1, document.text); ok { + import_edit.range.start.line -= 1 + import_edit.range.start.character = column + } + + } + + append(&textEdits, import_edit) + } + + workspaceEdit: WorkspaceEdit + workspaceEdit.changes = make(map[string][]TextEdit, 0, context.temp_allocator) + workspaceEdit.changes[uri] = textEdits[:] + + append( + actions, + CodeAction { + kind = "source.organizeImports", + isPreferred = true, + title = fmt.tprint("organize imports"), + edit = workspaceEdit, + }, + ) +} + + remove_unused_imports :: proc( document: ^Document, uri: string, diff --git a/src/server/requests.odin b/src/server/requests.odin index 8f9be060..6a7c9cac 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -761,7 +761,7 @@ request_initialize :: proc( hoverProvider = config.enable_hover, documentFormattingProvider = config.enable_format, documentLinkProvider = {resolveProvider = false}, - codeActionProvider = {resolveProvider = false, codeActionKinds = {"refactor.rewrite"}}, + codeActionProvider = {resolveProvider = false, codeActionKinds = {"refactor.rewrite", "source.organizeImports"}}, }, }, id = id, @@ -1586,7 +1586,7 @@ request_code_action :: proc( } code_actions: []CodeAction - code_actions, ok = get_code_actions(document, code_action_params.range, config) + code_actions, ok = get_code_actions(document, code_action_params.context_, code_action_params.range, config) if !ok { return .InternalError } diff --git a/src/testing/testing.odin b/src/testing/testing.odin index 6a6b80da..02206294 100644 --- a/src/testing/testing.odin +++ b/src/testing/testing.odin @@ -466,7 +466,7 @@ expect_action :: proc(t: ^testing.T, src: ^Source, expect_action_names: []string start = src.position, end = src.position, } - actions, ok := server.get_code_actions(src.document, input_range, &src.config) + actions, ok := server.get_code_actions(src.document, {}, input_range, &src.config) if !ok { log.error("Failed to find actions") }