Detail Bug Report
https://app.detail.dev/org_4c5b920c-9e04-4530-a4d2-953927859978/bugs/bug_709a16bd-e80f-4e1b-a22e-d824a08546d3
Summary
- Context: The
get_drives() function in crates/typos-lsp/src/windows.rs returns Windows drive letters that are used to create router routes for matching file URIs to their corresponding workspace configurations.
- Bug: The function returns uppercase drive letters (A, B, C, etc.), but LSP clients may send file URIs with lowercase drive letters (a, b, c, etc.), and the matchit router performs case-sensitive matching.
- Actual vs. expected: When a client sends a URI with a lowercase drive letter (e.g.,
file:///c:/Users/file.txt), it fails to match routes created with uppercase letters (e.g., /C%3A/{*p}), causing the LSP server to fall back to the default policy instead of using the workspace-specific configuration.
- Impact: Files opened on Windows with clients that use lowercase drive letters (such as VS Code) will not use workspace-specific typos configuration files (e.g.,
_typos.toml), instead falling back to default settings, which may miss custom dictionaries, ignore patterns, or other project-specific settings.
Code with bug
#[cfg(windows)]
pub fn get_drives() -> Vec<String> {
let mut drives = Vec::new();
let mut bitmask = unsafe { GetLogicalDrives() };
let mut letter = b'A'; // <-- BUG 🔴 Always starts with uppercase 'A'
while bitmask > 0 {
if bitmask & 1 != 0 {
drives.push((letter as char).into()); // <-- BUG 🔴 Pushes uppercase letters
}
bitmask >>= 1;
letter += 1;
}
drives
}
In crates/typos-lsp/src/state.rs:
#[cfg(windows)]
for drive in crate::windows::get_drives() {
let route = format!("/{}%3A/{{*p}}", &drive); // <-- Creates routes like /C%3A/{*p}
self.router.insert_instance(
&route,
&PathBuf::from(format!("{}:\\", &drive)),
self.config.as_deref(),
)?;
}
Example
- Route creation:
get_drives() returns ["C", "D"], creating routes /C%3A/{*p} and /D%3A/{*p}.
- Client sends URI:
file:///c:/Users/project/file.txt (lowercase c).
- URI is sanitized to
/c%3A/Users/project/file.txt.
- Case-sensitive router fails to match
/c%3A/... against /C%3A/{*p}.
- Server falls back to default policy instead of workspace configuration.
Expected: The file should use the workspace configuration for the C: drive.
Recommended fix
Register both uppercase and lowercase drive routes so matching is case-agnostic:
#[cfg(windows)]
pub fn get_drives() -> Vec<String> {
let mut drives = Vec::new();
let mut bitmask = unsafe { GetLogicalDrives() };
let mut letter = b'A';
while bitmask > 0 {
if bitmask & 1 != 0 {
drives.push((letter as char).to_string()); // uppercase
drives.push((letter as char).to_lowercase().to_string()); // <-- FIX 🟢 Add lowercase
}
bitmask >>= 1;
letter += 1;
}
drives
}
Detail Bug Report
https://app.detail.dev/org_4c5b920c-9e04-4530-a4d2-953927859978/bugs/bug_709a16bd-e80f-4e1b-a22e-d824a08546d3
Summary
get_drives()function incrates/typos-lsp/src/windows.rsreturns Windows drive letters that are used to create router routes for matching file URIs to their corresponding workspace configurations.file:///c:/Users/file.txt), it fails to match routes created with uppercase letters (e.g.,/C%3A/{*p}), causing the LSP server to fall back to the default policy instead of using the workspace-specific configuration._typos.toml), instead falling back to default settings, which may miss custom dictionaries, ignore patterns, or other project-specific settings.Code with bug
In
crates/typos-lsp/src/state.rs:Example
get_drives()returns["C", "D"], creating routes/C%3A/{*p}and/D%3A/{*p}.file:///c:/Users/project/file.txt(lowercasec)./c%3A/Users/project/file.txt./c%3A/...against/C%3A/{*p}.Expected: The file should use the workspace configuration for the
C:drive.Recommended fix
Register both uppercase and lowercase drive routes so matching is case-agnostic: