vscode-ruff icon indicating copy to clipboard operation
vscode-ruff copied to clipboard

Nested workspace omits sub configs, probably because of extend-exclude

Open Jangberry-pro opened this issue 9 months ago • 4 comments

Hey,

I'm working on a complex project, with submodules, with different settings and the meta project excludes the sub ones with extend-exclude. We use it so that CI only care about its repository but while developing we do use the whole collection of repo, even when developing in submodules.

As excluding sub project is an intended behavior, I opened sub projects as new folder in the workspace, awaiting it to force include the sub directories, but it doesn't work as I intended.

The closest issue I found is #414, except this time it is for a whole child folder opened in a workspace.

I've reproduced a minimal exemple to clarify:

ex/
├── .ruff.toml
├── sub
│   ├── .ruff.toml
│   ├── test.py
│   └── foo
│       └── test.py
└── test.py

Where the test.py files are filled with

print("That's a pretty long long long long long long long long long long long long long long long long long long long long long long line")

-> cat ex/.ruff.toml

target-version = "py312"
line-length = 120

extend-exclude = [
    "sub",
]

-> cat ex/sub/.ruff.toml

target-version = "py312"
line-length = 120

extend-exclude = [
    "foo",
]

What I get:

  • formatting temp/sub/foo/test.py does nothing as expected
  • formatting temp/test.py break the long line as expected
  • formatting temp/sub/test.py does nothing instead of breaking the line

In the extension output I get (I changed my home to <$HOME> for privacy):

DEBUG main ruff_server::session::index::ruff_settings: Indexing settings for workspace: <$HOME>/ex
2025-05-14 10:05:51.825783382 DEBUG ThreadId(04) ruff_server::session::index::ruff_settings: Loaded settings from: `<$HOME>/ex/.ruff.toml` for `<$HOME>/ex`
2025-05-14 10:05:51.825915250 DEBUG ThreadId(04) ruff_server::session::index::ruff_settings: Ignored path via `extend-exclude`: <$HOME>/ex/sub
2025-05-14 10:05:51.826834994  INFO main ruff_server::session::index: Registering workspace: <$HOME>/ex
2025-05-14 10:05:51.826868981 DEBUG main ruff_server::session::index::ruff_settings: Indexing settings for workspace: <$HOME>/ex/sub
2025-05-14 10:05:51.827300462 DEBUG main ruff_server::session::index::ruff_settings: Loaded settings from: `<$HOME>/ex/.ruff.toml`
2025-05-14 10:05:51.827922641 DEBUG ThreadId(16) ruff_server::session::index::ruff_settings: Ignored path via `extend-exclude`: <$HOME>/ex/sub
2025-05-14 10:05:51.830188073  INFO main ruff_server::session::index: Registering workspace: <$HOME>/ex/sub
2025-05-14 10:05:51.836248830  INFO ruff:main ruff_server::server: Configuration file watcher successfully registered

It seems to me a bug occurs right after the Indexing settings for workspace: <$HOME>/ex/sub, I feel like the intended behavior would be loading ex/sub/.ruff.toml.

Also feel free to edit the title I struggle to find a representative one

Jangberry-pro avatar May 14 '25 08:05 Jangberry-pro

Thanks for the detailed write up. I think this is working as intended for performance reasons.

Ruff tries to skip over directories that are excluded. For example, ruff skips over .venv and it won't show any lint warnings even if there's a ruff.toml in one of the packages that you installed (there shouldn't be any but there could).

What you're asking for would require ruff to always walk the entire directory tree because, maybe, there's some .ruff.toml somewhere. This would be very expensive because folders like .venv tend to contain many files.

Ruff's behavior here is consistent with git's where you can't unignore files in a previously ignored directory because git doesn't traverse the directory (for performance reasons).

You could make use of VS code's multi folder feature and adding individual folders (e.g. opening sub explicitly)

MichaReiser avatar May 18 '25 16:05 MichaReiser

Hum there might be a misunderstanding, I get the performance reasons and tried to force include the folder :

As excluding sub project is an intended behavior, I opened sub projects as new folder in the workspace, awaiting it to force include the sub directories, but it doesn't work as I intended.

Except if what you call multi folder feature is different of what I call Workspace ? I must admit that I don't use this kind of feature really often...

But the sequence

2025-05-14 10:05:51.826868981 DEBUG main ruff_server::session::index::ruff_settings: Indexing settings for workspace: <$HOME>/ex/sub
2025-05-14 10:05:51.827300462 DEBUG main ruff_server::session::index::ruff_settings: Loaded settings from: `<$HOME>/ex/.ruff.toml`

really make me feel something went wrong.

Jangberry-pro avatar May 19 '25 08:05 Jangberry-pro

Well well well... The issue actually probably is directly in ruff. Applying the following dirty patch I got it to work:

diff --git a/crates/ruff_server/src/session/index/ruff_settings.rs b/crates/ruff_server/src/session/index/ruff_settings.rs
index 3156d6e0f..484909330 100644
--- a/crates/ruff_server/src/session/index/ruff_settings.rs
+++ b/crates/ruff_server/src/session/index/ruff_settings.rs
@@ -177,14 +177,8 @@ impl RuffSettingsIndex {
         let mut respect_gitignore = None;
         let mut index = BTreeMap::default();
 
-        // If this is *not* the default workspace, then we should skip the workspace root itself
-        // because it will be resolved when walking the workspace directory tree. This is done by
-        // the `WalkBuilder` below.
-        let should_skip_workspace = usize::from(!is_default_workspace);
-
-        // Add any settings from above the workspace root, skipping the workspace root itself if
-        // this is *not* the default workspace.
-        for directory in root.ancestors().skip(should_skip_workspace) {
+        // Add any settings from above the workspace root.
+        for directory in root.ancestors() {
             match settings_toml(directory) {
                 Ok(Some(pyproject)) => {
                     match ruff_workspace::resolver::resolve_root_settings(
@@ -308,6 +302,10 @@ impl RuffSettingsIndex {
                         );
                         return WalkState::Skip;
                     }
+                    if directory == root {
+                        // Skip root as already included
+                        return WalkState::Continue;
+                    }
                 }
 
                 match settings_toml(&directory) {

It looks like the parent's yaml takes precedence over root folder's yaml because scanned first... the should_skip_workspace seem to actually kills this feature.

Here are the logs with my freshly built custom server:

2025-05-20 23:07:30.849417257 DEBUG Negotiated position encoding: UTF16
2025-05-20 23:07:30.849641705 DEBUG Indexing settings for workspace: <$HOME>/ex
2025-05-20 23:07:30.857199904 DEBUG Loaded settings from: `<$HOME>/ex/.ruff.toml`
2025-05-20 23:07:30.865953994 DEBUG Ignored path via `extend-exclude`: <$HOME>/ex/sub
2025-05-20 23:07:30.876157818  INFO Registering workspace: <$HOME>/ex
2025-05-20 23:07:30.876319587 DEBUG Indexing settings for workspace: <$HOME>/ex/sub
2025-05-20 23:07:30.880635114 DEBUG Loaded settings from: `<$HOME>/ex/sub/.ruff.toml`
2025-05-20 23:07:30.888796223 DEBUG Ignored path via `extend-exclude`: <$HOME>/ex/sub/foo
2025-05-20 23:07:30.892008207  INFO Registering workspace: <$HOME>/ex/sub
2025-05-20 23:07:30.894010274 DEBUG Included path via `include`: <$HOME>/ex/test.py
2025-05-20 23:07:30.894039900 DEBUG Included path via `include`: <$HOME>/ex/sub/test.py
2025-05-20 23:07:30.895065355  INFO Configuration file watcher successfully registered
2025-05-20 23:09:35.696582001  WARN Received notification $/setTrace which does not have a handler.
2025-05-20 23:16:33.251551310 DEBUG Included path via `include`: <$HOME>/ex/test.py

Should I close this issue and open an issue or a PR there ?

Jangberry avatar May 20 '25 21:05 Jangberry

I'm facing the same issue and maybe can elaborate more on it. If I simply open the subproject folder sub in VS code, I wouldn't expect settings from any parent folders (outside the current workspace) to take place, as I'm purely developing the sub project. What happens now with the native server, is that it still reads the settings from the parent directory (which is outside the current workspace), exludes the sub folder, and thus it never sees the correct config sub/.ruff.toml. Everything works as expected on command line. Also, everything works as expected in VS code if I set "ruff.nativeServer": false in VS Code settings.

magister-teemu avatar Sep 29 '25 10:09 magister-teemu