Skip to content

Commit

Permalink
chore: drop slotmap and simplify locking (#4696)
Browse files Browse the repository at this point in the history
  • Loading branch information
arendjr authored Dec 5, 2024
1 parent f5ba162 commit 3914be8
Show file tree
Hide file tree
Showing 12 changed files with 430 additions and 569 deletions.
11 changes: 0 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ serde = { version = "1.0.215", features = ["derive"] }
serde_ini = "0.2.0"
serde_json = "1.0.133"
similar = "2.6.0"
slotmap = "1.0.7"
smallvec = { version = "1.13.2", features = ["union", "const_new", "serde"] }
syn = "1.0.109"
termcolor = "1.4.1"
Expand Down
1 change: 0 additions & 1 deletion crates/biome_service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ rustc-hash = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["raw_value"] }
slotmap = { workspace = true, features = ["serde"] }
smallvec = { workspace = true, features = ["serde"] }
tracing = { workspace = true, features = ["attributes", "log"] }

Expand Down
4 changes: 4 additions & 0 deletions crates/biome_service/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ impl WorkspaceError {
Self::NotFound(NotFound)
}

pub fn no_project() -> Self {
Self::NoProject(NoProject)
}

pub fn file_ignored(path: String) -> Self {
Self::FileIgnored(FileIgnored { path })
}
Expand Down
6 changes: 3 additions & 3 deletions crates/biome_service/src/file_handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ impl biome_console::fmt::Display for DocumentFileSource {
pub struct FixAllParams<'a> {
pub(crate) parse: AnyParse,
pub(crate) fix_file_mode: FixFileMode,
pub(crate) workspace: WorkspaceSettingsHandle<'a>,
pub(crate) workspace: WorkspaceSettingsHandle,
/// Whether it should format the code action
pub(crate) should_format: bool,
pub(crate) biome_path: &'a BiomePath,
Expand Down Expand Up @@ -447,7 +447,7 @@ pub struct DebugCapabilities {
#[derive(Debug)]
pub(crate) struct LintParams<'a> {
pub(crate) parse: AnyParse,
pub(crate) workspace: &'a WorkspaceSettingsHandle<'a>,
pub(crate) workspace: &'a WorkspaceSettingsHandle,
pub(crate) language: DocumentFileSource,
pub(crate) max_diagnostics: u32,
pub(crate) path: &'a BiomePath,
Expand All @@ -468,7 +468,7 @@ pub(crate) struct LintResults {
pub(crate) struct CodeActionsParams<'a> {
pub(crate) parse: AnyParse,
pub(crate) range: Option<TextRange>,
pub(crate) workspace: &'a WorkspaceSettingsHandle<'a>,
pub(crate) workspace: &'a WorkspaceSettingsHandle,
pub(crate) path: &'a BiomePath,
pub(crate) manifest: Option<PackageJson>,
pub(crate) language: DocumentFileSource,
Expand Down
8 changes: 7 additions & 1 deletion crates/biome_service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub mod dome;
#[cfg(feature = "schema")]
pub mod workspace_types;

use std::ops::Deref;
use std::{fs, ops::Deref, path::Path};

use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -69,3 +69,9 @@ impl<'app> Deref for WorkspaceRef<'app> {
}
}
}

/// Returns `true` if `path` is a directory or
/// if it is a symlink that resolves to a directory.
fn is_dir(path: &Path) -> bool {
path.is_dir() || (path.is_symlink() && fs::read_link(path).is_ok_and(|path| path.is_dir()))
}
122 changes: 86 additions & 36 deletions crates/biome_service/src/matcher/mod.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,101 @@
pub mod pattern;

use biome_configuration::BiomeDiagnostic;
use biome_console::markup;
use biome_diagnostics::Diagnostic;
use papaya::HashMap;
pub use pattern::{MatchOptions, Pattern, PatternError};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::RwLock;
use rustc_hash::FxBuildHasher;
use std::{
path::{Path, PathBuf},
sync::Arc,
};

use crate::WorkspaceError;

/// A data structure to use when there's need to match a string or a path a against
/// a unix shell style patterns
#[derive(Debug, Default)]
pub struct Matcher {
#[derive(Clone, Debug, Default)]
pub struct Matcher(Arc<Inner>);

impl Matcher {
pub fn empty() -> Self {
Self::default()
}

/// Creates a [Matcher] from a set of globs.
///
/// ## Errors
///
/// It can raise an error if the patterns aren't valid
pub fn from_globs(
working_directory: Option<PathBuf>,
globs: Option<&[Box<str>]>,
) -> Result<Matcher, WorkspaceError> {
let mut matcher = Inner::default();
if let Some(working_directory) = working_directory {
matcher.set_root(working_directory)
}
if let Some(string_set) = globs {
for pattern in string_set {
matcher.add_pattern(pattern).map_err(|err| {
BiomeDiagnostic::new_invalid_ignore_pattern(
pattern.to_string(),
err.msg.to_string(),
)
})?;
}
}
Ok(Self(Arc::new(matcher)))
}

pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// Matches the given string against the stored patterns.
///
/// Returns [true] if there's at least one match.
pub fn matches(&self, source: &str) -> bool {
self.0.matches(source)
}

/// Matches the given path against the stored patterns.
///
/// Returns [true] if there's at least one match.
pub fn matches_path(&self, source: &Path) -> bool {
self.0.matches_path(source)
}
}

#[derive(Clone, Debug, Default)]
struct Inner {
root: Option<PathBuf>,
patterns: Vec<Pattern>,
/// Check [glob website](https://docs.rs/glob/latest/glob/struct.MatchOptions.html) for [MatchOptions]
options: MatchOptions,
/// Whether the string was already checked
already_checked: RwLock<HashMap<String, bool>>,
/// Cached results for matches.
already_checked: HashMap<String, bool, FxBuildHasher>,
}

impl Matcher {
impl Inner {
/// Creates a new Matcher with given options.
///
/// Check [glob website](https://docs.rs/glob/latest/glob/struct.MatchOptions.html) for [MatchOptions]
pub fn new(options: MatchOptions) -> Self {
#[cfg(test)]
fn new(options: MatchOptions) -> Self {
Self {
root: None,
patterns: Vec::new(),
options,
already_checked: RwLock::new(HashMap::default()),
already_checked: HashMap::default(),
}
}

pub fn empty() -> Self {
Self {
root: None,
patterns: Vec::new(),
options: MatchOptions::default(),
already_checked: RwLock::new(HashMap::default()),
}
}

pub fn set_root(&mut self, root: PathBuf) {
fn set_root(&mut self, root: PathBuf) {
self.root = Some(root);
}

/// It adds a unix shell style pattern
pub fn add_pattern(&mut self, pattern: &str) -> Result<(), PatternError> {
fn add_pattern(&mut self, pattern: &str) -> Result<(), PatternError> {
let pattern = Pattern::new(pattern)?;
self.patterns.push(pattern);
Ok(())
Expand All @@ -54,33 +104,33 @@ impl Matcher {
/// It matches the given string against the stored patterns.
///
/// It returns [true] if there's at least a match
pub fn matches(&self, source: &str) -> bool {
let mut already_ignored = self.already_checked.write().unwrap();
if let Some(matches) = already_ignored.get(source) {
fn matches(&self, source: &str) -> bool {
let already_checked = self.already_checked.pin();
if let Some(matches) = already_checked.get(source) {
return *matches;
}
for pattern in &self.patterns {
if pattern.matches_with(source, self.options) || source.contains(pattern.as_str()) {
already_ignored.insert(source.to_string(), true);
already_checked.insert(source.to_string(), true);
return true;
}
}
already_ignored.insert(source.to_string(), false);
already_checked.insert(source.to_string(), false);
false
}

pub fn is_empty(&self) -> bool {
fn is_empty(&self) -> bool {
self.patterns.is_empty()
}

/// It matches the given path against the stored patterns
///
/// It returns [true] if there's at least one match
pub fn matches_path(&self, source: &Path) -> bool {
fn matches_path(&self, source: &Path) -> bool {
if self.is_empty() {
return false;
}
let mut already_checked = self.already_checked.write().unwrap();
let already_checked = self.already_checked.pin();
let source_as_string = source.to_str();
if let Some(source_as_string) = source_as_string {
if let Some(matches) = already_checked.get(source_as_string) {
Expand Down Expand Up @@ -136,14 +186,14 @@ impl Diagnostic for PatternError {
#[cfg(test)]
mod test {
use crate::matcher::pattern::MatchOptions;
use crate::matcher::Matcher;
use crate::matcher::Inner;
use std::env;

#[test]
fn matches() {
let current = env::current_dir().unwrap();
let dir = format!("{}/**/*.rs", current.display());
let mut ignore = Matcher::new(MatchOptions::default());
let mut ignore = Inner::new(MatchOptions::default());
ignore.add_pattern(&dir).unwrap();
let path = env::current_dir().unwrap().join("src/workspace.rs");
let result = ignore.matches(path.to_str().unwrap());
Expand All @@ -155,7 +205,7 @@ mod test {
fn matches_path() {
let current = env::current_dir().unwrap();
let dir = format!("{}/**/*.rs", current.display());
let mut ignore = Matcher::new(MatchOptions::default());
let mut ignore = Inner::new(MatchOptions::default());
ignore.add_pattern(&dir).unwrap();
let path = env::current_dir().unwrap().join("src/workspace.rs");
let result = ignore.matches_path(path.as_path());
Expand All @@ -167,7 +217,7 @@ mod test {
fn matches_path_for_single_file_or_directory_name() {
let dir = "inv";
let valid_test_dir = "valid/";
let mut ignore = Matcher::new(MatchOptions::default());
let mut ignore = Inner::new(MatchOptions::default());
ignore.add_pattern(dir).unwrap();
ignore.add_pattern(valid_test_dir).unwrap();

Expand All @@ -185,7 +235,7 @@ mod test {
#[test]
fn matches_single_path() {
let dir = "workspace.rs";
let mut ignore = Matcher::new(MatchOptions {
let mut ignore = Inner::new(MatchOptions {
require_literal_separator: true,
case_sensitive: true,
require_literal_leading_dot: true,
Expand Down
Loading

0 comments on commit 3914be8

Please sign in to comment.