From d9b35a84badfd3e14f1cd9d41a0944ae7ec3b441 Mon Sep 17 00:00:00 2001 From: C P <13357398-ChriPar@users.noreply.gitlab.com> Date: Wed, 24 Sep 2025 17:42:11 +0100 Subject: [PATCH] Add `remove_unused_with` assist --- crates/ide/src/ide/assists/mod.rs | 2 + .../ide/src/ide/assists/remove_unused_with.rs | 79 +++++++++++++++++++ docs/code_actions.md | 12 +++ 3 files changed, 93 insertions(+) create mode 100644 crates/ide/src/ide/assists/remove_unused_with.rs diff --git a/crates/ide/src/ide/assists/mod.rs b/crates/ide/src/ide/assists/mod.rs index 79427ed6..b4a847dd 100644 --- a/crates/ide/src/ide/assists/mod.rs +++ b/crates/ide/src/ide/assists/mod.rs @@ -21,6 +21,7 @@ mod pack_bindings; mod remove_empty_inherit; mod remove_empty_let_in; mod remove_unused_binding; +mod remove_unused_with; mod rewrite_string; use crate::{DefDatabase, FileRange, TextEdit, WorkspaceEdit}; @@ -53,6 +54,7 @@ pub(crate) fn assists(db: &dyn DefDatabase, frange: FileRange) -> Vec { remove_empty_inherit::remove_empty_inherit, remove_empty_let_in::remove_empty_let_in, remove_unused_binding::remove_unused_binding, + remove_unused_with::remove_unused_with, rewrite_string::quote_attr, rewrite_string::rewrite_indented_to_string, rewrite_string::rewrite_string_to_indented, diff --git a/crates/ide/src/ide/assists/remove_unused_with.rs b/crates/ide/src/ide/assists/remove_unused_with.rs new file mode 100644 index 00000000..13eac8b5 --- /dev/null +++ b/crates/ide/src/ide/assists/remove_unused_with.rs @@ -0,0 +1,79 @@ +//! Remove an unused `with ...`. +//! +//! ```nix +//! foo = with pkgs; [ pkgs.bar ]; +//! ``` +//! => +//! ```nix +//! foo = [ pkgs.bar ]; +//! ``` +use super::{AssistKind, AssistsCtx}; +use crate::DiagnosticKind::UnusedWith; +use crate::TextEdit; +use syntax::ast; + +pub(super) fn remove_unused_with(ctx: &mut AssistsCtx<'_>) -> Option<()> { + let cursor_with = ctx.covering_node::()?; + let with_range = cursor_with.with_token()?.text_range(); + + let file = ctx.frange.file_id; + let check = ctx.db.liveness_check(file); + let diags = check.as_ref().to_diagnostics(ctx.db, file); + + let no_relevant_diags = diags + .filter(|d| d.kind == UnusedWith && d.range.intersect(with_range).is_some()) + .count() + == 0; + + if no_relevant_diags { + return None; + } + + let semicolon_token = cursor_with.semicolon_token()?; + let trivia_range = std::iter::successors(semicolon_token.next_token(), |tok| tok.next_token()) + .take_while(|tok| tok.kind().is_trivia()) + .last() + .unwrap_or(semicolon_token); + + ctx.add( + "remove_unused_with", + "Remove unused with", + AssistKind::QuickFix, + vec![TextEdit { + delete: with_range.cover(trivia_range.text_range()), + insert: Default::default(), + }], + ); + + Some(()) +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + + define_check_assist!(super::remove_unused_with); + + #[test] + fn in_use_with() { + check_no("a: $0with lib; bar"); + } + + #[test] + fn unused_with() { + // Simple + check("a: $0with 1;a", expect!["a: a"]); + + // With trivia + check("a: $0with /* trivia */ 1; a", expect!["a: a"]); + + // With array + check("a: $0with [ 1 ]; a", expect!["a: a"]); + + // 1st of multiple + check("a: $0with 1; with 2; a", expect!["a: with 2; a"]); + + // 2nd of multiple + check("a: with 1; $0with 2; a", expect!["a: with 1; a"]); + } +} diff --git a/docs/code_actions.md b/docs/code_actions.md index 1f77517d..d975709b 100644 --- a/docs/code_actions.md +++ b/docs/code_actions.md @@ -142,6 +142,18 @@ let in {} ``` +### `remove_unused_with` + +Remove an unused `with ...`. + +```nix +foo = with pkgs; [ pkgs.bar ]; +``` +=> +```nix +foo = [ pkgs.bar ]; +``` + ### `rewrite_string_to_indented` and `rewrite_indented_to_string` Rewrite between double quoted strings and indented strings