Workspace discovery (#14308)

This commit is contained in:
Micha Reiser 2024-11-15 19:20:15 +01:00 committed by GitHub
parent 2b58705cc1
commit 81e5830585
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 1972 additions and 181 deletions

View file

@ -1,12 +1,8 @@
// TODO support untitled files for the LSP use case. Wrap a `str` and `String`
// The main question is how `as_std_path` would work for untitled files, that can only exist in the LSP case
// but there's no compile time guarantee that a [`OsSystem`] never gets an untitled file path.
use camino::{Utf8Path, Utf8PathBuf};
use std::borrow::Borrow;
use std::fmt::Formatter;
use std::ops::Deref;
use std::path::{Path, StripPrefixError};
use std::path::{Path, PathBuf, StripPrefixError};
/// A slice of a path on [`System`](super::System) (akin to [`str`]).
///
@ -23,6 +19,32 @@ impl SystemPath {
unsafe { &*(path as *const Utf8Path as *const SystemPath) }
}
/// Takes any path, and when possible, converts Windows UNC paths to regular paths.
/// If the path can't be converted, it's returned unmodified.
///
/// On non-Windows this is no-op.
///
/// `\\?\C:\Windows` will be converted to `C:\Windows`,
/// but `\\?\C:\COM` will be left as-is (due to a reserved filename).
///
/// Use this to pass arbitrary paths to programs that may not be UNC-aware.
///
/// It's generally safe to pass UNC paths to legacy programs, because
/// these paths contain a reserved prefix, so will gracefully fail
/// if used with legacy APIs that don't support UNC.
///
/// This function does not perform any I/O.
///
/// Currently paths with unpaired surrogates aren't converted even if they
/// could be, due to limitations of Rust's `OsStr` API.
///
/// To check if a path remained as UNC, use `path.as_os_str().as_encoded_bytes().starts_with(b"\\\\")`.
#[inline]
pub fn simplified(&self) -> &SystemPath {
// SAFETY: simplified only trims the path, that means the returned path must be a valid UTF-8 path.
SystemPath::from_std_path(dunce::simplified(self.as_std_path())).unwrap()
}
/// Extracts the file extension, if possible.
///
/// The extension is:
@ -123,6 +145,39 @@ impl SystemPath {
self.0.parent().map(SystemPath::new)
}
/// Produces an iterator over `SystemPath` and its ancestors.
///
/// The iterator will yield the `SystemPath` that is returned if the [`parent`] method is used zero
/// or more times. That means, the iterator will yield `&self`, `&self.parent().unwrap()`,
/// `&self.parent().unwrap().parent().unwrap()` and so on. If the [`parent`] method returns
/// [`None`], the iterator will do likewise. The iterator will always yield at least one value,
/// namely `&self`.
///
/// # Examples
///
/// ```
/// use ruff_db::system::SystemPath;
///
/// let mut ancestors = SystemPath::new("/foo/bar").ancestors();
/// assert_eq!(ancestors.next(), Some(SystemPath::new("/foo/bar")));
/// assert_eq!(ancestors.next(), Some(SystemPath::new("/foo")));
/// assert_eq!(ancestors.next(), Some(SystemPath::new("/")));
/// assert_eq!(ancestors.next(), None);
///
/// let mut ancestors = SystemPath::new("../foo/bar").ancestors();
/// assert_eq!(ancestors.next(), Some(SystemPath::new("../foo/bar")));
/// assert_eq!(ancestors.next(), Some(SystemPath::new("../foo")));
/// assert_eq!(ancestors.next(), Some(SystemPath::new("..")));
/// assert_eq!(ancestors.next(), Some(SystemPath::new("")));
/// assert_eq!(ancestors.next(), None);
/// ```
///
/// [`parent`]: SystemPath::parent
#[inline]
pub fn ancestors(&self) -> impl Iterator<Item = &SystemPath> {
self.0.ancestors().map(SystemPath::new)
}
/// Produces an iterator over the [`camino::Utf8Component`]s of the path.
///
/// When parsing the path, there is a small amount of normalization:
@ -473,6 +528,10 @@ impl SystemPathBuf {
self.0
}
pub fn into_std_path_buf(self) -> PathBuf {
self.0.into_std_path_buf()
}
#[inline]
pub fn as_path(&self) -> &SystemPath {
SystemPath::new(&self.0)