diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-06-13 15:48:53 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-13 15:48:53 -0400 |
commit | 015ea60d25a3c773fc9d9bf17fc904de236814f9 (patch) | |
tree | 7ad367b85a48fc5c0980bbfd91e98139d9f9da19 /cli/util/glob.rs | |
parent | 92e7287f4a744cad1fbe46ba1ce84c2b479ce6ac (diff) |
fix(lsp): don't pre-load documents matched in the config file's "exclude" (#19431)
This prevents documents specified in a deno.json's "exclude" from being
pre-loaded by the lsp.
For example, someone may have something like:
```jsonc
// deno.json
{
"exclude": [
"dist" // build directory
]
}
```
Diffstat (limited to 'cli/util/glob.rs')
-rw-r--r-- | cli/util/glob.rs | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/cli/util/glob.rs b/cli/util/glob.rs new file mode 100644 index 000000000..55c9a516e --- /dev/null +++ b/cli/util/glob.rs @@ -0,0 +1,108 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use std::path::Path; +use std::path::PathBuf; + +use deno_core::anyhow::Context; +use deno_core::error::AnyError; + +pub fn expand_globs(paths: Vec<PathBuf>) -> Result<Vec<PathBuf>, AnyError> { + let mut new_paths = vec![]; + for path in paths { + let path_str = path.to_string_lossy(); + if is_glob_pattern(&path_str) { + let globbed_paths = glob(&path_str)?; + + for globbed_path_result in globbed_paths { + new_paths.push(globbed_path_result?); + } + } else { + new_paths.push(path); + } + } + + Ok(new_paths) +} + +pub fn glob(pattern: &str) -> Result<glob::Paths, AnyError> { + glob::glob_with(&escape_brackets(pattern), match_options()) + .with_context(|| format!("Failed to expand glob: \"{}\"", pattern)) +} + +pub struct GlobPattern(glob::Pattern); + +impl GlobPattern { + pub fn new_if_pattern(pattern: &str) -> Result<Option<Self>, AnyError> { + if !is_glob_pattern(pattern) { + return Ok(None); + } + Self::new(pattern).map(Some) + } + + pub fn new(pattern: &str) -> Result<Self, AnyError> { + let pattern = glob::Pattern::new(pattern) + .with_context(|| format!("Failed to expand glob: \"{}\"", pattern))?; + Ok(Self(pattern)) + } + + pub fn matches_path(&self, path: &Path) -> bool { + self.0.matches_path(path) + } +} + +pub struct GlobSet(Vec<GlobPattern>); + +impl GlobSet { + pub fn new(matchers: Vec<GlobPattern>) -> Self { + Self(matchers) + } + + pub fn matches_path(&self, path: &Path) -> bool { + for pattern in &self.0 { + if pattern.matches_path(path) { + return true; + } + } + false + } +} + +pub fn is_glob_pattern(path: &str) -> bool { + path.chars().any(|c| matches!(c, '*' | '?')) +} + +fn escape_brackets(pattern: &str) -> String { + // Escape brackets - we currently don't support them, because with introduction + // of glob expansion paths like "pages/[id].ts" would suddenly start giving + // wrong results. We might want to revisit that in the future. + pattern.replace('[', "[[]").replace(']', "[]]") +} + +fn match_options() -> glob::MatchOptions { + // Matches what `deno_task_shell` does + glob::MatchOptions { + // false because it should work the same way on case insensitive file systems + case_sensitive: false, + // true because it copies what sh does + require_literal_separator: true, + // true because it copies with sh does—these files are considered "hidden" + require_literal_leading_dot: true, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + pub fn glob_set_matches_path() { + let glob_set = GlobSet::new(vec![ + GlobPattern::new("foo/bar").unwrap(), + GlobPattern::new("foo/baz").unwrap(), + ]); + + assert!(glob_set.matches_path(Path::new("foo/bar"))); + assert!(glob_set.matches_path(Path::new("foo/baz"))); + assert!(!glob_set.matches_path(Path::new("foo/qux"))); + } +} |