mirror of
https://github.com/sharkdp/bat.git
synced 2026-10-26 20:16:33 +00:00
Refactor: string-or-struct matcher syntax, Case enum, remove case_sensitive_mappings table
Co-authored-by: keith-hall <11882719+keith-hall@users.noreply.github.com>
This commit is contained in:
1 parent
22c38b6fcb
commit
2a29802dd5
4 files changed
+76
-36
No files matched your search
+58
-25
@@ -47,16 +47,34 @@ impl ToTokens for MappingTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, DeserializeFromStr)]
|
/// Whether a glob pattern should be matched case-sensitively or case-insensitively.
|
||||||
|
///
|
||||||
|
/// Mirrors the runtime `Case` type in `src/syntax_mapping.rs`.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
enum Case {
|
||||||
|
Sensitive,
|
||||||
|
Insensitive,
|
||||||
|
}
|
||||||
|
impl ToTokens for Case {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let t = match self {
|
||||||
|
Self::Sensitive => quote! { Case::Sensitive },
|
||||||
|
Self::Insensitive => quote! { Case::Insensitive },
|
||||||
|
};
|
||||||
|
tokens.append_all(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
/// A single matcher.
|
/// A single matcher.
|
||||||
///
|
///
|
||||||
/// Codegen converts this into a `Lazy<Option<GlobMatcher>>`.
|
/// Codegen converts this into a `Lazy<Option<GlobMatcher>>`.
|
||||||
struct Matcher {
|
struct Matcher {
|
||||||
segments: Vec<MatcherSegment>,
|
segments: Vec<MatcherSegment>,
|
||||||
/// Whether the glob pattern should be matched case-insensitively.
|
/// Whether the glob pattern should be matched case-sensitively.
|
||||||
///
|
///
|
||||||
/// Defaults to `true` (case-insensitive) for backwards compatibility.
|
/// Defaults to `Case::Insensitive` for backwards compatibility.
|
||||||
case_insensitive: bool,
|
case: Case,
|
||||||
}
|
}
|
||||||
/// Parse a matcher.
|
/// Parse a matcher.
|
||||||
///
|
///
|
||||||
@@ -124,21 +142,53 @@ impl FromStr for Matcher {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
segments: non_empty_segments,
|
segments: non_empty_segments,
|
||||||
case_insensitive: true,
|
case: Case::Insensitive,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper type for deserializing a `Matcher` from either a plain string or a
|
||||||
|
/// `{ glob = "...", case_sensitive = true }` struct.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum RawMatcher {
|
||||||
|
Simple(String),
|
||||||
|
Full {
|
||||||
|
glob: String,
|
||||||
|
#[serde(default)]
|
||||||
|
case_sensitive: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for Matcher {
|
||||||
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let raw = RawMatcher::deserialize(deserializer)?;
|
||||||
|
match raw {
|
||||||
|
RawMatcher::Simple(s) => Matcher::from_str(&s).map_err(serde::de::Error::custom),
|
||||||
|
RawMatcher::Full { glob, case_sensitive } => {
|
||||||
|
let mut matcher =
|
||||||
|
Matcher::from_str(&glob).map_err(serde::de::Error::custom)?;
|
||||||
|
matcher.case = if case_sensitive {
|
||||||
|
Case::Sensitive
|
||||||
|
} else {
|
||||||
|
Case::Insensitive
|
||||||
|
};
|
||||||
|
Ok(matcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl ToTokens for Matcher {
|
impl ToTokens for Matcher {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
let case_insensitive = self.case_insensitive;
|
let case = &self.case;
|
||||||
let t = match self.segments.as_slice() {
|
let t = match self.segments.as_slice() {
|
||||||
[] => unreachable!("0-length matcher should never be created"),
|
[] => unreachable!("0-length matcher should never be created"),
|
||||||
[MatcherSegment::Text(text)] => {
|
[MatcherSegment::Text(text)] => {
|
||||||
quote! { Lazy::new(|| Some(build_matcher_fixed(#text, #case_insensitive))) }
|
quote! { Lazy::new(|| Some(build_matcher_fixed(#text, #case))) }
|
||||||
}
|
}
|
||||||
// parser logic ensures that this case can only happen when there are dynamic segments
|
// parser logic ensures that this case can only happen when there are dynamic segments
|
||||||
segs @ [_, ..] => {
|
segs @ [_, ..] => {
|
||||||
quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ], #case_insensitive)) }
|
quote! { Lazy::new(|| build_matcher_dynamic(&[ #(#segs),* ], #case)) }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
tokens.append_all(t);
|
tokens.append_all(t);
|
||||||
@@ -189,10 +239,6 @@ impl MatcherSegment {
|
|||||||
struct MappingDefModel {
|
struct MappingDefModel {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
mappings: IndexMap<MappingTarget, Vec<Matcher>>,
|
mappings: IndexMap<MappingTarget, Vec<Matcher>>,
|
||||||
/// Case-sensitive mappings. Unlike `mappings`, these glob patterns are
|
|
||||||
/// matched case-sensitively.
|
|
||||||
#[serde(default)]
|
|
||||||
case_sensitive_mappings: IndexMap<MappingTarget, Vec<Matcher>>,
|
|
||||||
}
|
}
|
||||||
impl MappingDefModel {
|
impl MappingDefModel {
|
||||||
fn into_mapping_list(self) -> MappingList {
|
fn into_mapping_list(self) -> MappingList {
|
||||||
@@ -205,19 +251,6 @@ impl MappingDefModel {
|
|||||||
.map(|matcher| (matcher, target.clone()))
|
.map(|matcher| (matcher, target.clone()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.chain(
|
|
||||||
self.case_sensitive_mappings
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|(target, matchers)| {
|
|
||||||
matchers
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut matcher| {
|
|
||||||
matcher.case_insensitive = false;
|
|
||||||
(matcher, target.clone())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.collect();
|
.collect();
|
||||||
MappingList(list)
|
MappingList(list)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-4
@@ -17,9 +17,16 @@ use ignored_suffixes::IgnoredSuffixes;
|
|||||||
mod builtin;
|
mod builtin;
|
||||||
pub mod ignored_suffixes;
|
pub mod ignored_suffixes;
|
||||||
|
|
||||||
fn make_glob_matcher(from: &str, case_insensitive: bool) -> Result<GlobMatcher> {
|
/// Whether a glob pattern should be matched case-sensitively or case-insensitively.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub(crate) enum Case {
|
||||||
|
Sensitive,
|
||||||
|
Insensitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_glob_matcher(from: &str, case: Case) -> Result<GlobMatcher> {
|
||||||
let matcher = GlobBuilder::new(from)
|
let matcher = GlobBuilder::new(from)
|
||||||
.case_insensitive(case_insensitive)
|
.case_insensitive(matches!(case, Case::Insensitive))
|
||||||
.literal_separator(true)
|
.literal_separator(true)
|
||||||
.build()?
|
.build()?
|
||||||
.compile_matcher();
|
.compile_matcher();
|
||||||
@@ -97,14 +104,14 @@ impl<'a> SyntaxMapping<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
|
pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
|
||||||
let matcher = make_glob_matcher(from, true)?;
|
let matcher = make_glob_matcher(from, Case::Insensitive)?;
|
||||||
self.custom_mappings.push((matcher, to));
|
self.custom_mappings.push((matcher, to));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [`Self::insert`], but the glob pattern is matched case-sensitively.
|
/// Like [`Self::insert`], but the glob pattern is matched case-sensitively.
|
||||||
pub fn insert_case_sensitive(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
|
pub fn insert_case_sensitive(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> {
|
||||||
let matcher = make_glob_matcher(from, false)?;
|
let matcher = make_glob_matcher(from, Case::Sensitive)?;
|
||||||
self.custom_mappings.push((matcher, to));
|
self.custom_mappings.push((matcher, to));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::env;
|
|||||||
use globset::GlobMatcher;
|
use globset::GlobMatcher;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::syntax_mapping::{make_glob_matcher, MappingTarget};
|
use crate::syntax_mapping::{make_glob_matcher, Case, MappingTarget};
|
||||||
|
|
||||||
// Static syntax mappings generated from /src/syntax_mapping/builtins/ by the
|
// Static syntax mappings generated from /src/syntax_mapping/builtins/ by the
|
||||||
// build script (/build/syntax_mapping.rs).
|
// build script (/build/syntax_mapping.rs).
|
||||||
@@ -53,8 +53,8 @@ include!(concat!(
|
|||||||
/// A failure to compile is a fatal error.
|
/// A failure to compile is a fatal error.
|
||||||
///
|
///
|
||||||
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
|
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
|
||||||
fn build_matcher_fixed(from: &str, case_insensitive: bool) -> GlobMatcher {
|
fn build_matcher_fixed(from: &str, case: Case) -> GlobMatcher {
|
||||||
make_glob_matcher(from, case_insensitive)
|
make_glob_matcher(from, case)
|
||||||
.expect("A builtin fixed glob matcher failed to compile")
|
.expect("A builtin fixed glob matcher failed to compile")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ fn build_matcher_fixed(from: &str, case_insensitive: bool) -> GlobMatcher {
|
|||||||
/// to compile.
|
/// to compile.
|
||||||
///
|
///
|
||||||
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
|
/// Used internally by `Lazy<Option<GlobMatcher>>`'s lazy evaluation closure.
|
||||||
fn build_matcher_dynamic(segs: &[MatcherSegment], case_insensitive: bool) -> Option<GlobMatcher> {
|
fn build_matcher_dynamic(segs: &[MatcherSegment], case: Case) -> Option<GlobMatcher> {
|
||||||
// join segments
|
// join segments
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
for seg in segs {
|
for seg in segs {
|
||||||
@@ -78,7 +78,7 @@ fn build_matcher_dynamic(segs: &[MatcherSegment], case_insensitive: bool) -> Opt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// compile glob matcher
|
// compile glob matcher
|
||||||
let matcher = make_glob_matcher(&buf, case_insensitive).ok()?;
|
let matcher = make_glob_matcher(&buf, case).ok()?;
|
||||||
Some(matcher)
|
Some(matcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
[case_sensitive_mappings]
|
[mappings]
|
||||||
"Python" = ["BUILD"]
|
"Python" = [{ glob = "BUILD", case_sensitive = true }]
|
||||||
Reference in new issue
Block a user