aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--crates/shirabe-php-shim/src/preg.rs46
-rw-r--r--crates/shirabe-symfony-console/src/completion/completion_input.rs12
-rw-r--r--crates/shirabe-symfony-console/src/formatter/output_formatter.rs17
3 files changed, 41 insertions, 34 deletions
diff --git a/crates/shirabe-php-shim/src/preg.rs b/crates/shirabe-php-shim/src/preg.rs
index 0aa2c43e..b16ee81d 100644
--- a/crates/shirabe-php-shim/src/preg.rs
+++ b/crates/shirabe-php-shim/src/preg.rs
@@ -167,19 +167,15 @@ pub fn preg_match2(
}
// PREG_PATTERN_ORDER: the outer vec is indexed by capture group, the inner by
-// match occurrence. Non-participating groups are reported as "".
-pub fn preg_match_all(pattern: impl PregPattern, subject: &str) -> Vec<Vec<String>> {
+// match occurrence. A non-participating group is reported as None.
+pub fn preg_match_all(pattern: impl PregPattern, subject: &str) -> Vec<Vec<Option<String>>> {
let __resolved = pattern.resolve();
let (re, _anchored) = __resolved.parts();
let group_count = re.captures_len();
- let mut groups: Vec<Vec<String>> = vec![Vec::new(); group_count];
+ let mut groups: Vec<Vec<Option<String>>> = vec![Vec::new(); group_count];
for caps in re.captures_iter(subject) {
for (g, group) in groups.iter_mut().enumerate() {
- group.push(
- caps.get(g)
- .map(|m| m.as_str().to_string())
- .unwrap_or_default(),
- );
+ group.push(caps.get(g).map(|m| m.as_str().to_string()));
}
}
groups
@@ -218,17 +214,22 @@ pub fn preg_match_all2(
}
// PREG_SET_ORDER: the outer vec is indexed by match occurrence, the inner by
-// capture group (a classic `$matches` row).
+// capture group (a `$matches` row). A non-participating group is reported as
+// None.
pub fn preg_match_all_set_order(
pattern: impl PregPattern,
subject: &str,
- matches: &mut Vec<Vec<String>>,
+ matches: &mut Vec<Vec<Option<String>>>,
) -> usize {
let __resolved = pattern.resolve();
let (re, _anchored) = __resolved.parts();
- let mut rows: Vec<Vec<String>> = Vec::new();
+ let mut rows: Vec<Vec<Option<String>>> = Vec::new();
for caps in re.captures_iter(subject) {
- rows.push(php_match_row(&caps));
+ rows.push(
+ (0..caps.len())
+ .map(|g| caps.get(g).map(|m| m.as_str().to_string()))
+ .collect(),
+ );
}
let count = rows.len();
*matches = rows;
@@ -299,8 +300,9 @@ fn preg_split_impl(pattern: impl PregPattern, subject: &str, delim_capture: bool
let m = caps.get(0).unwrap();
result.push(subject[last..m.start()].to_string());
if delim_capture {
- // Mirror preg_match: trailing unmatched groups are dropped, interior
- // unmatched groups are emitted as "".
+ // `preg_split` accepts no PREG_UNMATCHED_AS_NULL, so the split list
+ // holds strings only: trailing unmatched groups are dropped,
+ // interior ones are emitted as "".
if let Some(last_g) = (1..caps.len()).rev().find(|&g| caps.get(g).is_some()) {
for g in 1..=last_g {
result.push(caps.get(g).map(|x| x.as_str()).unwrap_or("").to_string());
@@ -641,22 +643,6 @@ fn php_replacement_group(bytes: &[u8]) -> (usize, usize) {
(group, consumed)
}
-// Classic preg_match `$matches` row: index 0 is the full match, trailing
-// unmatched groups are truncated and interior unmatched groups become "".
-fn php_match_row(caps: &regex::Captures) -> Vec<String> {
- let last = (0..caps.len())
- .rev()
- .find(|&g| caps.get(g).is_some())
- .unwrap_or(0);
- (0..=last)
- .map(|g| {
- caps.get(g)
- .map(|m| m.as_str().to_string())
- .unwrap_or_default()
- })
- .collect()
-}
-
// Builds a single match's `$matches` map with both named and numbered keys
// (the named key precedes its number). Every group is present; a
// non-participating one is None.
diff --git a/crates/shirabe-symfony-console/src/completion/completion_input.rs b/crates/shirabe-symfony-console/src/completion/completion_input.rs
index 2f146a39..aafa7c8a 100644
--- a/crates/shirabe-symfony-console/src/completion/completion_input.rs
+++ b/crates/shirabe-symfony-console/src/completion/completion_input.rs
@@ -34,7 +34,17 @@ impl CompletionInput {
input_str,
);
- Self::from_tokens(tokens[0].clone(), current_index)
+ Self::from_tokens(
+ tokens[0]
+ .iter()
+ .map(|token| {
+ token
+ .clone()
+ .expect("group 0 participates whenever the pattern matches")
+ })
+ .collect(),
+ current_index,
+ )
}
/// Create an input based on an COMP_WORDS token list.
diff --git a/crates/shirabe-symfony-console/src/formatter/output_formatter.rs b/crates/shirabe-symfony-console/src/formatter/output_formatter.rs
index 311d50fd..c7c06cf5 100644
--- a/crates/shirabe-symfony-console/src/formatter/output_formatter.rs
+++ b/crates/shirabe-symfony-console/src/formatter/output_formatter.rs
@@ -109,7 +109,7 @@ impl OutputFormatter {
return Ok(Some(style.borrow().clone_box()));
}
- let mut matches: Vec<Vec<String>> = vec![];
+ let mut matches: Vec<Vec<Option<String>>> = vec![];
if preg_match_all_set_order(php_regex!("/([^=]+)=([^;]+)(;|$)/"), string, &mut matches) == 0
{
return Ok(None);
@@ -117,7 +117,14 @@ impl OutputFormatter {
let mut style = OutputFormatterStyle::new(None, None, vec![]);
for r#match in &matches {
- let mut r#match: Vec<String> = r#match.clone();
+ let mut r#match: Vec<String> = r#match
+ .iter()
+ .map(|group| {
+ group
+ .clone()
+ .expect("every group participates whenever the pattern matches")
+ })
+ .collect();
shirabe_php_shim::array_shift(&mut r#match);
r#match[0] = shirabe_php_shim::strtolower(&r#match[0]);
@@ -135,7 +142,11 @@ impl OutputFormatter {
);
let options = shirabe_php_shim::array_shift(&mut options).unwrap_or_default();
for option in &options {
- style.set_option(option);
+ style.set_option(
+ option
+ .as_deref()
+ .expect("group 0 participates whenever the pattern matches"),
+ );
}
} else {
return Ok(None);