Skip to content

fix(lsp): filter organizeImports from fixAll code action#9487

Merged
ematipico merged 3 commits intobiomejs:mainfrom
mvanhorn:osc/9477-fix-organizeimports-fixall
Mar 18, 2026
Merged

fix(lsp): filter organizeImports from fixAll code action#9487
ematipico merged 3 commits intobiomejs:mainfrom
mvanhorn:osc/9477-fix-organizeimports-fixall

Conversation

@mvanhorn
Copy link
Contributor

Summary

When source.fixAll.biome runs (e.g. on save), it includes organize imports actions even when source.organizeImports.biome is set to "explicit" or "never" in editor settings. This PR filters out the organize imports rule from the fix-all pass when organize imports was not explicitly requested.

The fix adds an include_organize_imports parameter to the fix_all() function. When source.organizeImports.biome is not in the editor's requested code actions, the organize imports rule is added to the skip list in FixFileParams, preventing it from running as part of fix-all.

Fixes #9477

Test Plan

  • cargo check -p biome_lsp passes
  • All 43 existing biome_lsp tests pass (cargo test -p biome_lsp)

AI Assistance

This PR was written with AI assistance (Claude Code).

When `source.fixAll.biome` is triggered but `source.organizeImports.biome`
is not explicitly requested, the fix-all action now skips the organize
imports rule. This prevents import sorting on save when the user has set
`source.organizeImports.biome` to "explicit" or "never" in their editor
settings.

Fixes biomejs#9477

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Mar 15, 2026

🦋 Changeset detected

Latest commit: 48e5175

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the A-LSP Area: language server protocol label Mar 15, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 15, 2026

Walkthrough

Adds an ORGANIZE_IMPORTS_CATEGORY constant and detects when source.organizeImports is explicitly requested via params.context.only, tracking it with has_organize_imports. Propagates that flag into the fix‑all flow by extending fix_all with an include_organize_imports: bool parameter. When false, the fix‑all pipeline skips the organizeImports rule by adding it to the analyzer skip list so imports are not reordered unless explicitly requested. Updates call sites, tests, and a release note; documentation comment for fix_all updated to reflect conditional exclusion.

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main change: filtering organiseImports from fixAll code actions to respect editor settings.
Description check ✅ Passed The description clearly explains the problem, the solution, and references the fixed issue #9477 with test verification.
Linked Issues check ✅ Passed The PR directly addresses issue #9477 by preventing organiseImports from running during fix-all unless explicitly requested, matching the issue's objective.
Out of Scope Changes check ✅ Passed All changes focus on the fix-all organiseImports filtering: handler logic, test coverage, and changelog; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.

OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_lsp/src/handlers/analysis.rs (1)

33-34: Derive the skip selector from ORGANIZE_IMPORTS_CATEGORY.

Right now the kind lives as ORGANIZE_IMPORTS_CATEGORY, but the skip path re-spells it as "source" / "organizeImports". If those drift, the bug saunters back in. Reusing RuleSelector::from_lsp_filter(...) keeps both paths in lock-step.

♻️ Small tidy-up
     let mut skip = vec![];
     if !include_organize_imports {
-        skip.push(AnalyzerSelector::Rule(RuleSelector::Rule(
-            "source",
-            "organizeImports",
-        )));
+        if let Some(selector) =
+            RuleSelector::from_lsp_filter(ORGANIZE_IMPORTS_CATEGORY.to_str().as_ref())
+        {
+            skip.push(AnalyzerSelector::Rule(selector));
+        }
     }

Also applies to: 302-316, 379-385

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_lsp/src/handlers/analysis.rs` around lines 33 - 34, Replace the
hard-coded skip selector strings ("source"/"organizeImports") by deriving the
selector from the existing ORGANIZE_IMPORTS_CATEGORY constant using
RuleSelector::from_lsp_filter(...); locate the places that construct the skip
path (the code that currently spells out "source" / "organizeImports", including
the occurrences around the earlier constant and the other two spots noted) and
call RuleSelector::from_lsp_filter(ORGANIZE_IMPORTS_CATEGORY.clone().into() / or
pass ORGANIZE_IMPORTS_CATEGORY as required by the API) to produce the selector,
then use that RuleSelector instead of the literal strings so both paths stay in
sync with ORGANIZE_IMPORTS_CATEGORY.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_lsp/src/handlers/analysis.rs`:
- Around line 33-34: Replace the hard-coded skip selector strings
("source"/"organizeImports") by deriving the selector from the existing
ORGANIZE_IMPORTS_CATEGORY constant using RuleSelector::from_lsp_filter(...);
locate the places that construct the skip path (the code that currently spells
out "source" / "organizeImports", including the occurrences around the earlier
constant and the other two spots noted) and call
RuleSelector::from_lsp_filter(ORGANIZE_IMPORTS_CATEGORY.clone().into() / or pass
ORGANIZE_IMPORTS_CATEGORY as required by the API) to produce the selector, then
use that RuleSelector instead of the literal strings so both paths stay in sync
with ORGANIZE_IMPORTS_CATEGORY.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: baaff266-ffc8-4760-bda9-1f3c6d22d8ab

📥 Commits

Reviewing files that changed from the base of the PR and between fe9ff6b and 233d1ab.

📒 Files selected for processing (1)
  • crates/biome_lsp/src/handlers/analysis.rs

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add a test or update the existing ones?

"@biomejs/biome": patch
---

Fixed [#9477](https://github.com/biomejs/biome/issues/9477): `source.fixAll.biome` no longer sorts imports when `source.organizeImports.biome` is set to "explicit" or "never" in editor settings. The organize imports action is now excluded from the fix-all pass unless explicitly requested.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Fixed [#9477](https://github.com/biomejs/biome/issues/9477): `source.fixAll.biome` no longer sorts imports when `source.organizeImports.biome` is set to "explicit" or "never" in editor settings. The organize imports action is now excluded from the fix-all pass unless explicitly requested.
Fixed [#9477](https://github.com/biomejs/biome/issues/9477): `source.fixAll.biome` no longer sorts imports when `source.organizeImports.biome` is disabled in editor settings. The organize imports action is now excluded from the fix-all pass unless explicitly requested.

Different editor settings have different values. e.g. zed uses false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in 48e5175.

Add test that confirms source.fixAll.biome does not reorder imports
when source.organizeImports is not in the code action filter. Also
update changeset wording per reviewer feedback.
@mvanhorn
Copy link
Contributor Author

Added test in 48e5175: fix_all_does_not_sort_imports_unless_requested opens a document with unsorted imports and a lint error, requests source.fixAll without source.organizeImports, and verifies the response fixes the lint error but preserves import order.

Also updated the changeset wording per your inline suggestion.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_lsp/src/server.tests.rs (1)

2641-2649: Avoid coupling this check to edits[0] being a full-document rewrite.

Line 2646 assumes the first edit contains the whole file text. If fix-all later emits targeted edits, this test can fail even when behaviour is correct.

💡 Suggested tightening
     let edits = changes
         .get(&uri!("document.js"))
         .context("expected edits for document.js")?;
-    let new_text = &edits[0].new_text;
-    assert!(
-        new_text.starts_with("import { b }"),
-        "imports should NOT be reordered when organizeImports is not requested, got: {new_text}"
-    );
+    assert!(!edits.is_empty(), "expected at least one fix-all edit");
+
+    let full_document_edit = edits.iter().find(|edit| {
+        edit.range.start == Position::new(0, 0) && edit.range.end.line >= 2
+    });
+    let new_text = full_document_edit
+        .map(|edit| edit.new_text.as_str())
+        .context("expected a full-document edit for stable content assertions")?;
+
+    assert!(
+        new_text.starts_with("import { b }"),
+        "imports should NOT be reordered when organizeImports is not requested, got: {new_text}"
+    );
+    assert!(
+        new_text.contains("=== 0"),
+        "fix-all should still apply the lint fix, got: {new_text}"
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_lsp/src/server.tests.rs` around lines 2641 - 2649, The test
currently assumes edits[0] is a full-document rewrite; instead scan all edits
for one that represents the full-document replacement or contains the expected
import prefix. Replace the direct index access (edits[0].new_text) with a check
like edits.iter().any(|te| te.new_text.starts_with("import { b }")) so the
assertion succeeds if any emitted edit is the full-document rewrite (or contains
the expected import) and doesn't fail when targeted edits are produced;
reference the existing variables action.edit, changes, edits and
uri!("document.js") to locate the edit vector to change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_lsp/src/server.tests.rs`:
- Around line 2641-2649: The test currently assumes edits[0] is a full-document
rewrite; instead scan all edits for one that represents the full-document
replacement or contains the expected import prefix. Replace the direct index
access (edits[0].new_text) with a check like edits.iter().any(|te|
te.new_text.starts_with("import { b }")) so the assertion succeeds if any
emitted edit is the full-document rewrite (or contains the expected import) and
doesn't fail when targeted edits are produced; reference the existing variables
action.edit, changes, edits and uri!("document.js") to locate the edit vector to
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cef711c1-5596-4edc-aff2-b3a2b8cac90e

📥 Commits

Reviewing files that changed from the base of the PR and between 37e56cb and 48e5175.

📒 Files selected for processing (2)
  • .changeset/quiet-foxes-camp.md
  • crates/biome_lsp/src/server.tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/quiet-foxes-camp.md

@ematipico ematipico merged commit 331dc0d into biomejs:main Mar 18, 2026
13 checks passed
@github-actions github-actions bot mentioned this pull request Mar 18, 2026
@mvanhorn
Copy link
Contributor Author

Thanks for the merge! Glad this cleans up the fixAll behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-LSP Area: language server protocol

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 source.organizeImports.biome: "explicit" still sorts imports on save

2 participants