aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2025-09-22 22:04:24 +0200
committerDanielGavin <danielgavin5@hotmail.com>2025-09-22 22:04:24 +0200
commit28d540bb5f838815e23622d97cd5d3a55776414d (patch)
tree6fbe43699e08753379a2ab00982f0946321ec798 /src/server
parent1009de179a717c8b355acb8b1268fedc9b2d089c (diff)
Add new code action: remove unused imports
Diffstat (limited to 'src/server')
-rw-r--r--src/server/action.odin42
-rw-r--r--src/server/documents.odin3
-rw-r--r--src/server/format.odin4
-rw-r--r--src/server/imports.odin30
4 files changed, 69 insertions, 10 deletions
diff --git a/src/server/action.odin b/src/server/action.odin
index 0c3c74a..0c522bf 100644
--- a/src/server/action.odin
+++ b/src/server/action.odin
@@ -67,11 +67,53 @@ get_code_actions :: proc(document: ^Document, range: common.Range, config: ^comm
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)
}
+ } else if position_context.import_stmt != nil {
+ remove_missing_imports(document, strings.clone(document.uri.uri), config, &actions)
}
return actions[:], true
}
+remove_missing_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 = "",
+ }
+
+ append(&textEdits, import_edit)
+ }
+
+ workspaceEdit: WorkspaceEdit
+ workspaceEdit.changes = make(map[string][]TextEdit, 0, context.temp_allocator)
+ workspaceEdit.changes[uri] = textEdits[:]
+
+ append(
+ actions,
+ CodeAction {
+ kind = "refactor.rewrite",
+ isPreferred = true,
+ title = fmt.tprint("remove unused imports"),
+ edit = workspaceEdit,
+ },
+ )
+
+}
+
add_missing_imports :: proc(
ast_context: ^AstContext,
selector: ^ast.Selector_Expr,
diff --git a/src/server/documents.odin b/src/server/documents.odin
index dee90d5..cf8a344 100644
--- a/src/server/documents.odin
+++ b/src/server/documents.odin
@@ -29,6 +29,7 @@ Package :: struct {
base: string,
base_original: string,
original: string,
+ import_decl: ^ast.Import_Decl,
}
Document :: struct {
@@ -446,6 +447,7 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) {
import_: Package
import_.original = imp.fullpath
import_.name = strings.clone(path.join(elems = {dir, p}, allocator = context.temp_allocator))
+ import_.import_decl = imp
if imp.name.text != "" {
import_.base = imp.name.text
@@ -468,6 +470,7 @@ parse_imports :: proc(document: ^Document, config: ^common.Config) {
allocator = context.temp_allocator,
)
import_.name = path.clean(import_.name)
+ import_.import_decl = imp
if imp.name.text != "" {
import_.base = imp.name.text
diff --git a/src/server/format.odin b/src/server/format.odin
index d981b9c..9f30c49 100644
--- a/src/server/format.odin
+++ b/src/server/format.odin
@@ -29,10 +29,6 @@ get_complete_format :: proc(document: ^Document, config: ^common.Config) -> ([]T
return {}, true
}
- if config.enable_import_fixer {
- fix_imports(document)
- }
-
style := format.find_config_file_or_default(filepath.dir(document.fullpath, context.temp_allocator))
prnt := printer.make_printer(style, context.temp_allocator)
diff --git a/src/server/imports.odin b/src/server/imports.odin
index cd2271d..4da5a86 100644
--- a/src/server/imports.odin
+++ b/src/server/imports.odin
@@ -1,17 +1,35 @@
package server
+import "core:log"
import "core:mem"
import "core:odin/ast"
+import "base:runtime"
-fix_imports :: proc(document: ^Document) {
- arena: mem.Arena
- mem.arena_init(&arena, make([]byte, mem.Megabyte * 25))
- defer delete(arena.data)
+find_unused_imports :: proc(document: ^Document, allocator := context.temp_allocator) -> []Package {
+ arena: runtime.Arena
- context.allocator = mem.arena_allocator(&arena)
+ _ = runtime.arena_init(&arena, mem.Megabyte * 40, runtime.default_allocator())
- symbols_and_nodes := resolve_entire_file(document, .None)
+ defer runtime.arena_destroy(&arena)
+ context.allocator = runtime.arena_allocator(&arena)
+ symbols_and_nodes := resolve_entire_file_cached(document)
+
+ pkgs := make(map[string]bool, context.temp_allocator)
+
+ for _, v in symbols_and_nodes {
+ pkgs[v.symbol.pkg] = true
+ }
+
+ unused := make([dynamic]Package, allocator)
+
+ for imp in document.imports {
+ if imp.name not_in pkgs {
+ append(&unused, imp)
+ }
+ }
+
+ return unused[:]
}