zeta2: Parse imports via Tree-sitter queries + improve zeta retrieval-stats (#39735)

Release Notes:

- N/A

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Agus <agus@zed.dev>
Co-authored-by: Oleksiy <oleksiy@zed.dev>
This commit is contained in:
Michael Sloan
2025-10-08 12:04:06 -06:00
committed by GitHub
co-authored by Max Agus Oleksiy
parent 5fd187769d
commit bcef3b5010
35 changed files with 3076 additions and 508 deletions
+133
View File
@@ -4,6 +4,7 @@ use itertools::Itertools;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::error::Error;
use std::fmt::{Display, Formatter};
use std::mem;
use std::path::StripPrefixError;
@@ -184,6 +185,31 @@ impl<T: AsRef<Path>> PathExt for T {
}
}
pub fn path_ends_with(base: &Path, suffix: &Path) -> bool {
strip_path_suffix(base, suffix).is_some()
}
pub fn strip_path_suffix<'a>(base: &'a Path, suffix: &Path) -> Option<&'a Path> {
if let Some(remainder) = base
.as_os_str()
.as_encoded_bytes()
.strip_suffix(suffix.as_os_str().as_encoded_bytes())
{
if remainder
.last()
.is_none_or(|last_byte| std::path::is_separator(*last_byte as char))
{
let os_str = unsafe {
OsStr::from_encoded_bytes_unchecked(
&remainder[0..remainder.len().saturating_sub(1)],
)
};
return Some(Path::new(os_str));
}
}
None
}
/// In memory, this is identical to `Path`. On non-Windows conversions to this type are no-ops. On
/// windows, these conversions sanitize UNC paths by removing the `\\\\?\\` prefix.
#[derive(Eq, PartialEq, Hash, Ord, PartialOrd)]
@@ -401,6 +427,82 @@ pub fn is_absolute(path_like: &str, path_style: PathStyle) -> bool {
.is_some_and(|path| path.starts_with('/') || path.starts_with('\\')))
}
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub struct NormalizeError;
impl Error for NormalizeError {}
impl std::fmt::Display for NormalizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("parent reference `..` points outside of base directory")
}
}
/// Copied from stdlib where it's unstable.
///
/// Normalize a path, including `..` without traversing the filesystem.
///
/// Returns an error if normalization would leave leading `..` components.
///
/// <div class="warning">
///
/// This function always resolves `..` to the "lexical" parent.
/// That is "a/b/../c" will always resolve to `a/c` which can change the meaning of the path.
/// In particular, `a/c` and `a/b/../c` are distinct on many systems because `b` may be a symbolic link, so its parent isn't `a`.
///
/// </div>
///
/// [`path::absolute`](absolute) is an alternative that preserves `..`.
/// Or [`Path::canonicalize`] can be used to resolve any `..` by querying the filesystem.
pub fn normalize_lexically(path: &Path) -> Result<PathBuf, NormalizeError> {
use std::path::Component;
let mut lexical = PathBuf::new();
let mut iter = path.components().peekable();
// Find the root, if any, and add it to the lexical path.
// Here we treat the Windows path "C:\" as a single "root" even though
// `components` splits it into two: (Prefix, RootDir).
let root = match iter.peek() {
Some(Component::ParentDir) => return Err(NormalizeError),
Some(p @ Component::RootDir) | Some(p @ Component::CurDir) => {
lexical.push(p);
iter.next();
lexical.as_os_str().len()
}
Some(Component::Prefix(prefix)) => {
lexical.push(prefix.as_os_str());
iter.next();
if let Some(p @ Component::RootDir) = iter.peek() {
lexical.push(p);
iter.next();
}
lexical.as_os_str().len()
}
None => return Ok(PathBuf::new()),
Some(Component::Normal(_)) => 0,
};
for component in iter {
match component {
Component::RootDir => unreachable!(),
Component::Prefix(_) => return Err(NormalizeError),
Component::CurDir => continue,
Component::ParentDir => {
// It's an error if ParentDir causes us to go above the "root".
if lexical.as_os_str().len() == root {
return Err(NormalizeError);
} else {
lexical.pop();
}
}
Component::Normal(path) => lexical.push(path),
}
}
Ok(lexical)
}
/// A delimiter to use in `path_query:row_number:column_number` strings parsing.
pub const FILE_ROW_COLUMN_DELIMITER: char = ':';
@@ -1798,4 +1900,35 @@ mod tests {
let path = Path::new("/a/b/c/long.app.tar.gz");
assert_eq!(path.multiple_extensions(), Some("app.tar.gz".to_string()));
}
#[test]
fn test_strip_path_suffix() {
let base = Path::new("/a/b/c/file_name");
let suffix = Path::new("file_name");
assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a/b/c")));
let base = Path::new("/a/b/c/file_name.tsx");
let suffix = Path::new("file_name.tsx");
assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a/b/c")));
let base = Path::new("/a/b/c/file_name.stories.tsx");
let suffix = Path::new("c/file_name.stories.tsx");
assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a/b")));
let base = Path::new("/a/b/c/long.app.tar.gz");
let suffix = Path::new("b/c/long.app.tar.gz");
assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("/a")));
let base = Path::new("/a/b/c/long.app.tar.gz");
let suffix = Path::new("/a/b/c/long.app.tar.gz");
assert_eq!(strip_path_suffix(base, suffix), Some(Path::new("")));
let base = Path::new("/a/b/c/long.app.tar.gz");
let suffix = Path::new("/a/b/c/no_match.app.tar.gz");
assert_eq!(strip_path_suffix(base, suffix), None);
let base = Path::new("/a/b/c/long.app.tar.gz");
let suffix = Path::new("app.tar.gz");
assert_eq!(strip_path_suffix(base, suffix), None);
}
}