mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
replace the regex crate with the fancy-regex crate (#6227)
This commit is contained in:
parent
606547ecb4
commit
cdeb8de75d
20 changed files with 227 additions and 111 deletions
33
Cargo.lock
generated
33
Cargo.lock
generated
|
@ -299,6 +299,21 @@ dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-set"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||||
|
dependencies = [
|
||||||
|
"bit-vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit-vec"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
@ -1119,6 +1134,16 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fancy-regex"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0678ab2d46fa5195aaf59ad034c083d351377d4af57f3e073c074d0da3e3c766"
|
||||||
|
dependencies = [
|
||||||
|
"bit-set",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -2529,6 +2554,7 @@ version = "0.66.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm 0.24.0",
|
"crossterm 0.24.0",
|
||||||
|
"fancy-regex",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
"is_executable",
|
"is_executable",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -2544,7 +2570,6 @@ dependencies = [
|
||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"reedline",
|
"reedline",
|
||||||
"regex",
|
|
||||||
"rstest",
|
"rstest",
|
||||||
"strip-ansi-escapes",
|
"strip-ansi-escapes",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
@ -2583,6 +2608,7 @@ dependencies = [
|
||||||
"dtparse",
|
"dtparse",
|
||||||
"eml-parser",
|
"eml-parser",
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
|
"fancy-regex",
|
||||||
"filesize",
|
"filesize",
|
||||||
"filetime",
|
"filetime",
|
||||||
"fs_extra",
|
"fs_extra",
|
||||||
|
@ -2624,7 +2650,6 @@ dependencies = [
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rayon",
|
"rayon",
|
||||||
"reedline",
|
"reedline",
|
||||||
"regex",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"rstest",
|
"rstest",
|
||||||
|
@ -2678,11 +2703,11 @@ dependencies = [
|
||||||
name = "nu-json"
|
name = "nu-json"
|
||||||
version = "0.66.3"
|
version = "0.66.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"fancy-regex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"regex",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
@ -2739,13 +2764,13 @@ dependencies = [
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-humanize",
|
"chrono-humanize",
|
||||||
|
"fancy-regex",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"miette 5.1.1",
|
"miette 5.1.1",
|
||||||
"nu-json",
|
"nu-json",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"num-format",
|
"num-format",
|
||||||
"regex",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sys-locale",
|
"sys-locale",
|
||||||
|
|
|
@ -20,18 +20,18 @@ nu-utils = { path = "../nu-utils", version = "0.66.3" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.66.3" }
|
nu-color-config = { path = "../nu-color-config", version = "0.66.3" }
|
||||||
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
|
||||||
crossterm = "0.24.0"
|
|
||||||
miette = { version = "5.1.0", features = ["fancy"] }
|
|
||||||
thiserror = "1.0.31"
|
|
||||||
fuzzy-matcher = "0.3.7"
|
|
||||||
|
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
|
crossterm = "0.24.0"
|
||||||
|
fancy-regex = "0.10.0"
|
||||||
|
fuzzy-matcher = "0.3.7"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
regex = "1.5.4"
|
miette = { version = "5.1.0", features = ["fancy"] }
|
||||||
strip-ansi-escapes = "0.1.1"
|
strip-ansi-escapes = "0.1.1"
|
||||||
sysinfo = "0.24.1"
|
sysinfo = "0.24.1"
|
||||||
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = []
|
plugin = []
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::{
|
||||||
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
|
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
|
||||||
NuHighlighter, NuValidator, NushellPrompt,
|
NuHighlighter, NuValidator, NushellPrompt,
|
||||||
};
|
};
|
||||||
|
use fancy_regex::Regex;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
@ -18,7 +19,6 @@ use nu_protocol::{
|
||||||
Type, Value, VarId,
|
Type, Value, VarId,
|
||||||
};
|
};
|
||||||
use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi};
|
use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi};
|
||||||
use regex::Regex;
|
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
use std::{sync::atomic::Ordering, time::Instant};
|
use std::{sync::atomic::Ordering, time::Instant};
|
||||||
use strip_ansi_escapes::strip;
|
use strip_ansi_escapes::strip;
|
||||||
|
@ -494,7 +494,7 @@ fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
|
||||||
|
|
||||||
let banner = format!(
|
let banner = format!(
|
||||||
r#"{} __ ,
|
r#"{} __ ,
|
||||||
{} .--()°'.' {}Welcome to {}Nushell{},
|
{} .--()°'.' {}Welcome to {}Nushell{},
|
||||||
{}'|, . ,' {}based on the {}nu{} language,
|
{}'|, . ,' {}based on the {}nu{} language,
|
||||||
{} !_-(_\ {}where all data is structured!
|
{} !_-(_\ {}where all data is structured!
|
||||||
|
|
||||||
|
@ -909,7 +909,7 @@ lazy_static! {
|
||||||
fn looks_like_path(orig: &str) -> bool {
|
fn looks_like_path(orig: &str) -> bool {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
if DRIVE_PATH_REGEX.is_match(orig) {
|
if DRIVE_PATH_REGEX.is_match(orig).unwrap_or(false) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ digest = "0.10.0"
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.2.0"
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
encoding_rs = "0.8.30"
|
encoding_rs = "0.8.30"
|
||||||
|
fancy-regex = "0.10.0"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
filetime = "0.2.15"
|
filetime = "0.2.15"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
|
@ -64,7 +65,6 @@ powierza-coefficient = "1.0.1"
|
||||||
quick-xml = "0.23.0"
|
quick-xml = "0.23.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rayon = "1.5.1"
|
rayon = "1.5.1"
|
||||||
regex = "1.5.4"
|
|
||||||
reqwest = {version = "0.11", features = ["blocking", "json"] }
|
reqwest = {version = "0.11", features = ["blocking", "json"] }
|
||||||
roxmltree = "0.14.0"
|
roxmltree = "0.14.0"
|
||||||
rust-embed = "6.3.0"
|
rust-embed = "6.3.0"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use fancy_regex::Regex;
|
||||||
use nu_ansi_term::{
|
use nu_ansi_term::{
|
||||||
Color::{Default, Red, White},
|
Color::{Default, Red, White},
|
||||||
Style,
|
Style,
|
||||||
|
@ -11,7 +12,6 @@ use nu_protocol::{
|
||||||
ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Help;
|
pub struct Help;
|
||||||
|
|
||||||
|
@ -350,7 +350,7 @@ pub fn highlight_search_string(
|
||||||
string_style: &Style,
|
string_style: &Style,
|
||||||
) -> Result<String, ShellError> {
|
) -> Result<String, ShellError> {
|
||||||
let regex_string = format!("(?i){}", needle);
|
let regex_string = format!("(?i){}", needle);
|
||||||
let regex = match regex::Regex::new(®ex_string) {
|
let regex = match Regex::new(®ex_string) {
|
||||||
Ok(regex) => regex,
|
Ok(regex) => regex,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
|
@ -367,21 +367,34 @@ pub fn highlight_search_string(
|
||||||
let mut highlighted = String::new();
|
let mut highlighted = String::new();
|
||||||
|
|
||||||
for cap in regex.captures_iter(haystack) {
|
for cap in regex.captures_iter(haystack) {
|
||||||
let start = match cap.get(0) {
|
match cap {
|
||||||
Some(cap) => cap.start(),
|
Ok(capture) => {
|
||||||
None => 0,
|
let start = match capture.get(0) {
|
||||||
};
|
Some(acap) => acap.start(),
|
||||||
let end = match cap.get(0) {
|
None => 0,
|
||||||
Some(cap) => cap.end(),
|
};
|
||||||
None => 0,
|
let end = match capture.get(0) {
|
||||||
};
|
Some(acap) => acap.end(),
|
||||||
highlighted.push_str(
|
None => 0,
|
||||||
&string_style
|
};
|
||||||
.paint(&haystack[last_match_end..start])
|
highlighted.push_str(
|
||||||
.to_string(),
|
&string_style
|
||||||
);
|
.paint(&haystack[last_match_end..start])
|
||||||
highlighted.push_str(&style.paint(&haystack[start..end]).to_string());
|
.to_string(),
|
||||||
last_match_end = end;
|
);
|
||||||
|
highlighted.push_str(&style.paint(&haystack[start..end]).to_string());
|
||||||
|
last_match_end = end;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(ShellError::GenericError(
|
||||||
|
"Error with regular expression capture".into(),
|
||||||
|
e.to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlighted.push_str(&string_style.paint(&haystack[last_match_end..]).to_string());
|
highlighted.push_str(&string_style.paint(&haystack[last_match_end..]).to_string());
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use super::{operations::Axis, NuDataFrame};
|
use super::{operations::Axis, NuDataFrame};
|
||||||
|
|
||||||
use nu_protocol::{ast::Operator, span, ShellError, Span, Spanned, Value};
|
use nu_protocol::{ast::Operator, span, ShellError, Span, Spanned, Value};
|
||||||
use num::Zero;
|
use num::Zero;
|
||||||
use polars::prelude::{
|
use polars::prelude::{
|
||||||
|
@ -294,7 +293,7 @@ pub(super) fn compute_series_single_value(
|
||||||
compare_series_decimal(&lhs, *val, ChunkedArray::equal, lhs_span)
|
compare_series_decimal(&lhs, *val, ChunkedArray::equal, lhs_span)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => {
|
||||||
let equal_pattern = format!("^{}$", regex::escape(val));
|
let equal_pattern = format!("^{}$", fancy_regex::escape(val));
|
||||||
contains_series_pat(&lhs, &equal_pattern, lhs_span)
|
contains_series_pat(&lhs, &equal_pattern, lhs_span)
|
||||||
}
|
}
|
||||||
Value::Date { val, .. } => {
|
Value::Date { val, .. } => {
|
||||||
|
@ -406,7 +405,7 @@ pub(super) fn compute_series_single_value(
|
||||||
},
|
},
|
||||||
Operator::StartsWith => match &right {
|
Operator::StartsWith => match &right {
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => {
|
||||||
let starts_with_pattern = format!("^{}", regex::escape(val));
|
let starts_with_pattern = format!("^{}", fancy_regex::escape(val));
|
||||||
contains_series_pat(&lhs, &starts_with_pattern, lhs_span)
|
contains_series_pat(&lhs, &starts_with_pattern, lhs_span)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
|
@ -419,7 +418,7 @@ pub(super) fn compute_series_single_value(
|
||||||
},
|
},
|
||||||
Operator::EndsWith => match &right {
|
Operator::EndsWith => match &right {
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => {
|
||||||
let ends_with_pattern = format!("{}$", regex::escape(val));
|
let ends_with_pattern = format!("{}$", fancy_regex::escape(val));
|
||||||
contains_series_pat(&lhs, &ends_with_pattern, lhs_span)
|
contains_series_pat(&lhs, &ends_with_pattern, lhs_span)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::help::highlight_search_string;
|
use crate::help::highlight_search_string;
|
||||||
|
use fancy_regex::Regex;
|
||||||
use lscolors::Style as LsColors_Style;
|
use lscolors::Style as LsColors_Style;
|
||||||
use nu_ansi_term::{Color::Default, Style};
|
use nu_ansi_term::{Color::Default, Style};
|
||||||
use nu_color_config::get_color_config;
|
use nu_color_config::get_color_config;
|
||||||
|
@ -10,7 +11,6 @@ use nu_protocol::{
|
||||||
Signature, Span, SyntaxShape, Value,
|
Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::get_ls_colors;
|
use nu_utils::get_ls_colors;
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Find;
|
pub struct Find;
|
||||||
|
@ -197,18 +197,26 @@ fn find_with_regex(
|
||||||
|
|
||||||
input.filter(
|
input.filter(
|
||||||
move |value| match value {
|
move |value| match value {
|
||||||
Value::String { val, .. } => re.is_match(val.as_str()) != invert,
|
Value::String { val, .. } => re.is_match(val.as_str()).unwrap_or(false) != invert,
|
||||||
Value::Record { cols: _, vals, .. } => {
|
Value::Record { cols: _, vals, .. } => {
|
||||||
let matches: Vec<bool> = vals
|
let matches: Vec<bool> = vals
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| re.is_match(v.into_string(" ", &config).as_str()) != invert)
|
.map(|v| {
|
||||||
|
re.is_match(v.into_string(" ", &config).as_str())
|
||||||
|
.unwrap_or(false)
|
||||||
|
!= invert
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
matches.iter().any(|b| *b)
|
matches.iter().any(|b| *b)
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
let matches: Vec<bool> = vals
|
let matches: Vec<bool> = vals
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| re.is_match(v.into_string(" ", &config).as_str()) != invert)
|
.map(|v| {
|
||||||
|
re.is_match(v.into_string(" ", &config).as_str())
|
||||||
|
.unwrap_or(false)
|
||||||
|
!= invert
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
matches.iter().any(|b| *b)
|
matches.iter().any(|b| *b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::formats::to::delimited::merge_descriptors;
|
use crate::formats::to::delimited::merge_descriptors;
|
||||||
|
use fancy_regex::Regex;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
@ -6,7 +7,6 @@ use nu_protocol::{
|
||||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned,
|
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Spanned,
|
||||||
SyntaxShape, Value,
|
SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use fancy_regex::Regex;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
@ -5,7 +6,6 @@ use nu_protocol::{
|
||||||
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Parse;
|
pub struct Parse;
|
||||||
|
@ -55,6 +55,61 @@ impl Command for Parse {
|
||||||
example: "echo \"hi there\" | parse -r '(?P<foo>\\w+) (?P<bar>\\w+)'",
|
example: "echo \"hi there\" | parse -r '(?P<foo>\\w+) (?P<bar>\\w+)'",
|
||||||
result: Some(result),
|
result: Some(result),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Parse a string using fancy-regex named capture group pattern",
|
||||||
|
example: "echo \"foo bar.\" | parse -r '\\s*(?<name>\\w+)(?=\\.)'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::Record {
|
||||||
|
cols: vec!["name".to_string()],
|
||||||
|
vals: vec![Value::test_string("bar")],
|
||||||
|
span: Span::test_data()
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Parse a string using fancy-regex capture group pattern",
|
||||||
|
example: "echo \"foo! bar.\" | parse -r '(\\w+)(?=\\.)|(\\w+)(?=!)'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["Capture1".to_string(), "Capture2".to_string()],
|
||||||
|
vals: vec![Value::test_string(""), Value::test_string("foo")],
|
||||||
|
span: Span::test_data()
|
||||||
|
},
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["Capture1".to_string(), "Capture2".to_string()],
|
||||||
|
vals: vec![Value::test_string("bar"), Value::test_string("")],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Parse a string using fancy-regex look behind pattern",
|
||||||
|
example: "echo \" @another(foo bar) \" | parse -r '\\s*(?<=[() ])(@\\w+)(\\([^)]*\\))?\\s*'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::Record {
|
||||||
|
cols: vec!["Capture1".to_string(), "Capture2".to_string()],
|
||||||
|
vals: vec![Value::test_string("@another"), Value::test_string("(foo bar)")],
|
||||||
|
span: Span::test_data()
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Parse a string using fancy-regex look ahead atomic group pattern",
|
||||||
|
example: "echo \"abcd\" | parse -r '^a(bc(?=d)|b)cd$'",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::Record {
|
||||||
|
cols: vec!["Capture1".to_string()],
|
||||||
|
vals: vec![Value::test_string("b")],
|
||||||
|
span: Span::test_data()
|
||||||
|
}],
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +144,15 @@ fn operate(
|
||||||
build_regex(&pattern_item, pattern_span)?
|
build_regex(&pattern_item, pattern_span)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let regex_pattern =
|
let regex_pattern = Regex::new(&item_to_parse).map_err(|err| {
|
||||||
Regex::new(&item_to_parse).map_err(|e| parse_regex_error(e, pattern_span))?;
|
ShellError::GenericError(
|
||||||
|
"Error with regular expression".into(),
|
||||||
|
err.to_string(),
|
||||||
|
Some(pattern_span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let columns = column_names(®ex_pattern);
|
let columns = column_names(®ex_pattern);
|
||||||
let mut parsed: Vec<Value> = Vec::new();
|
let mut parsed: Vec<Value> = Vec::new();
|
||||||
|
@ -102,9 +164,21 @@ fn operate(
|
||||||
|
|
||||||
for c in results {
|
for c in results {
|
||||||
let mut cols = Vec::with_capacity(columns.len());
|
let mut cols = Vec::with_capacity(columns.len());
|
||||||
let mut vals = Vec::with_capacity(c.len());
|
let captures = match c {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(ShellError::GenericError(
|
||||||
|
"Error with regular expression captures".into(),
|
||||||
|
e.to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut vals = Vec::with_capacity(captures.len());
|
||||||
|
|
||||||
for (column_name, cap) in columns.iter().zip(c.iter().skip(1)) {
|
for (column_name, cap) in columns.iter().zip(captures.iter().skip(1)) {
|
||||||
let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string();
|
let cap_string = cap.map(|v| v.as_str()).unwrap_or("").to_string();
|
||||||
cols.push(column_name.clone());
|
cols.push(column_name.clone());
|
||||||
vals.push(Value::String {
|
vals.push(Value::String {
|
||||||
|
@ -156,7 +230,7 @@ fn build_regex(input: &str, span: Span) -> Result<String, ShellError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !before.is_empty() {
|
if !before.is_empty() {
|
||||||
output.push_str(®ex::escape(&before));
|
output.push_str(&fancy_regex::escape(&before));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for column as we're now at one
|
// Look for column as we're now at one
|
||||||
|
@ -202,35 +276,6 @@ fn column_names(regex: &Regex) -> Vec<String> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_regex_error(e: regex::Error, base_span: Span) -> ShellError {
|
|
||||||
match e {
|
|
||||||
regex::Error::Syntax(msg) => {
|
|
||||||
let mut lines = msg.lines();
|
|
||||||
|
|
||||||
let main_msg = lines
|
|
||||||
.next()
|
|
||||||
.map(|l| l.replace(':', ""))
|
|
||||||
.expect("invalid regex pattern");
|
|
||||||
|
|
||||||
let span = lines.nth(1).and_then(|l| l.find('^')).map(|space| {
|
|
||||||
let start = base_span.start + space - 3;
|
|
||||||
Span::new(start, start + 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
let msg = lines
|
|
||||||
.next()
|
|
||||||
.and_then(|l| l.split(':').nth(1))
|
|
||||||
.map(|s| format!("{}: {}", main_msg, s.trim()));
|
|
||||||
|
|
||||||
match (msg, span) {
|
|
||||||
(Some(msg), Some(span)) => ShellError::DelimiterError(msg, span),
|
|
||||||
_ => ShellError::DelimiterError("Invalid regex".to_owned(), base_span),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => ShellError::DelimiterError("Invalid regex".to_owned(), base_span),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use fancy_regex::Regex;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value};
|
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Span, Value};
|
||||||
|
@ -265,10 +266,13 @@ impl Count for Counter {
|
||||||
// use regex here because it can search for CRLF first and not duplicate the count
|
// use regex here because it can search for CRLF first and not duplicate the count
|
||||||
let line_ending_types = [CRLF, LF, CR, NEL, FF, LS, PS];
|
let line_ending_types = [CRLF, LF, CR, NEL, FF, LS, PS];
|
||||||
let pattern = &line_ending_types.join("|");
|
let pattern = &line_ending_types.join("|");
|
||||||
let newline_pattern = regex::Regex::new(pattern).expect("Unable to create regex");
|
let newline_pattern = Regex::new(pattern).expect("Unable to create regex");
|
||||||
let line_endings = newline_pattern
|
let line_endings = newline_pattern
|
||||||
.find_iter(s)
|
.find_iter(s)
|
||||||
.map(|f| f.as_str().to_string())
|
.map(|f| match f {
|
||||||
|
Ok(mat) => mat.as_str().to_string(),
|
||||||
|
Err(_) => "".to_string(),
|
||||||
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
let has_line_ending_suffix =
|
let has_line_ending_suffix =
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
use fancy_regex::{NoExpand, Regex};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use regex::{NoExpand, Regex};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
|
@ -133,6 +133,23 @@ impl Command for SubCommand {
|
||||||
span: Span::test_data(),
|
span: Span::test_data(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Find and replace with fancy-regex",
|
||||||
|
example: r#"'a sucessful b' | str replace '\b([sS])uc(?:cs|s?)e(ed(?:ed|ing|s?)|ss(?:es|ful(?:ly)?|i(?:ons?|ve(?:ly)?)|ors?)?)\b' '${1}ucce$2'"#,
|
||||||
|
result: Some(Value::String {
|
||||||
|
val: "a successful b".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Find and replace with fancy-regex",
|
||||||
|
example: r#"'GHIKK-9+*' | str replace '[*[:xdigit:]+]' 'z'"#,
|
||||||
|
result: Some(Value::String {
|
||||||
|
val: "GHIKK-z+*".to_string(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use fancy_regex::Regex;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, CellPath},
|
ast::{Call, CellPath},
|
||||||
|
@ -311,7 +312,7 @@ fn trim(s: &str, char_: Option<char>, closure_flags: &ClosureFlags) -> String {
|
||||||
// create a regex string that looks for 2 or more of each of these characters
|
// create a regex string that looks for 2 or more of each of these characters
|
||||||
let re_str = format!("{}{{2,}}", reg);
|
let re_str = format!("{}{{2,}}", reg);
|
||||||
// create the regex
|
// create the regex
|
||||||
let re = regex::Regex::new(&re_str).expect("Error creating regular expression");
|
let re = Regex::new(&re_str).expect("Error creating regular expression");
|
||||||
// replace all mutliple occurances with single occurences represented by r
|
// replace all mutliple occurances with single occurences represented by r
|
||||||
let new_str = re.replace_all(&return_string, r.to_string());
|
let new_str = re.replace_all(&return_string, r.to_string());
|
||||||
// update the return string so the next loop has the latest changes
|
// update the return string so the next loop has the latest changes
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
use fancy_regex::Regex;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use nu_engine::env_to_strings;
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
|
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
||||||
|
use nu_protocol::{Category, Example, ListStream, PipelineData, RawStream, Span, Spanned};
|
||||||
|
use nu_system::ForegroundProcess;
|
||||||
|
use pathdiff::diff_paths;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -5,18 +14,6 @@ use std::process::{Command as CommandSys, Stdio};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use nu_engine::env_to_strings;
|
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
|
||||||
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
|
||||||
use nu_protocol::{Category, Example, ListStream, PipelineData, RawStream, Span, Spanned};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
|
||||||
use nu_system::ForegroundProcess;
|
|
||||||
use pathdiff::diff_paths;
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
const OUTPUT_BUFFER_SIZE: usize = 1024;
|
const OUTPUT_BUFFER_SIZE: usize = 1024;
|
||||||
const OUTPUT_BUFFERS_IN_FLIGHT: usize = 3;
|
const OUTPUT_BUFFERS_IN_FLIGHT: usize = 3;
|
||||||
|
|
||||||
|
@ -505,7 +502,7 @@ impl ExternalCommand {
|
||||||
fn has_unsafe_shell_characters(arg: &str) -> bool {
|
fn has_unsafe_shell_characters(arg: &str) -> bool {
|
||||||
let re: Regex = Regex::new(r"[^\w@%+=:,./-]").expect("regex to be valid");
|
let re: Regex = Regex::new(r"[^\w@%+=:,./-]").expect("regex to be valid");
|
||||||
|
|
||||||
re.is_match(arg)
|
re.is_match(arg).unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shell_arg_escape(arg: &str) -> String {
|
fn shell_arg_escape(arg: &str) -> String {
|
||||||
|
|
|
@ -184,7 +184,9 @@ mod regex {
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(actual.err.contains("unclosed group"));
|
assert!(actual
|
||||||
|
.err
|
||||||
|
.contains("Opening parenthesis without closing parenthesis"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,11 @@ preserve_order = ["linked-hash-map", "linked-hash-map/serde_impl"]
|
||||||
default = ["preserve_order"]
|
default = ["preserve_order"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = "1.0"
|
fancy-regex = "0.10.0"
|
||||||
num-traits = "0.2.14"
|
|
||||||
regex = "^1.0"
|
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
linked-hash-map = { version="0.5", optional=true }
|
linked-hash-map = { version="0.5", optional=true }
|
||||||
|
num-traits = "0.2.14"
|
||||||
|
serde = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-path = { path="../nu-path", version = "0.66.3" }
|
nu-path = { path="../nu-path", version = "0.66.3" }
|
||||||
|
|
|
@ -12,7 +12,7 @@ use serde::ser;
|
||||||
|
|
||||||
//use super::util::ParseNumber;
|
//use super::util::ParseNumber;
|
||||||
|
|
||||||
use regex::Regex;
|
use fancy_regex::Regex;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// FIXME: re-enable tests
|
// FIXME: re-enable tests
|
||||||
/*
|
/*
|
||||||
use nu_json::Value;
|
use nu_json::Value;
|
||||||
use regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
|
@ -11,19 +11,20 @@ version = "0.66.3"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-utils = { path = "../nu-utils", version = "0.66.3" }
|
nu-utils = { path = "../nu-utils", version = "0.66.3" }
|
||||||
nu-path = { path = "../nu-path", version = "0.66.3" }
|
nu-path = { path = "../nu-path", version = "0.66.3" }
|
||||||
thiserror = "1.0.31"
|
|
||||||
miette = { version = "5.1.0", features = ["fancy"] }
|
|
||||||
serde = {version = "1.0.130", features = ["derive"]}
|
|
||||||
chrono = { version="0.4.19", features=["serde"] }
|
|
||||||
indexmap = { version="1.7", features=["serde-1"] }
|
|
||||||
chrono-humanize = "0.2.1"
|
|
||||||
byte-unit = "4.0.9"
|
|
||||||
serde_json = { version = "1.0", optional = true }
|
|
||||||
nu-json = { path = "../nu-json", version = "0.66.3" }
|
nu-json = { path = "../nu-json", version = "0.66.3" }
|
||||||
typetag = "0.1.8"
|
|
||||||
|
byte-unit = "4.0.9"
|
||||||
|
chrono = { version="0.4.19", features=["serde"] }
|
||||||
|
chrono-humanize = "0.2.1"
|
||||||
|
fancy-regex = "0.10.0"
|
||||||
|
indexmap = { version="1.7", features=["serde-1"] }
|
||||||
|
miette = { version = "5.1.0", features = ["fancy"] }
|
||||||
num-format = "0.4.0"
|
num-format = "0.4.0"
|
||||||
|
serde = {version = "1.0.130", features = ["derive"]}
|
||||||
|
serde_json = { version = "1.0", optional = true }
|
||||||
sys-locale = "0.2.0"
|
sys-locale = "0.2.0"
|
||||||
regex = "1.5.4"
|
thiserror = "1.0.31"
|
||||||
|
typetag = "0.1.8"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = ["serde_json"]
|
plugin = ["serde_json"]
|
||||||
|
|
|
@ -13,11 +13,11 @@ use byte_unit::ByteUnit;
|
||||||
use chrono::{DateTime, Duration, FixedOffset};
|
use chrono::{DateTime, Duration, FixedOffset};
|
||||||
use chrono_humanize::HumanTime;
|
use chrono_humanize::HumanTime;
|
||||||
pub use custom_value::CustomValue;
|
pub use custom_value::CustomValue;
|
||||||
|
use fancy_regex::Regex;
|
||||||
pub use from_value::FromValue;
|
pub use from_value::FromValue;
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
use num_format::{Locale, ToFormattedString};
|
use num_format::{Locale, ToFormattedString};
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
use regex::Regex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
@ -2178,7 +2178,11 @@ impl Value {
|
||||||
.map_err(|e| ShellError::UnsupportedInput(format!("{e}"), *rhs_span))?;
|
.map_err(|e| ShellError::UnsupportedInput(format!("{e}"), *rhs_span))?;
|
||||||
let is_match = regex.is_match(lhs);
|
let is_match = regex.is_match(lhs);
|
||||||
Ok(Value::Bool {
|
Ok(Value::Bool {
|
||||||
val: if invert { !is_match } else { is_match },
|
val: if invert {
|
||||||
|
!is_match.unwrap_or(false)
|
||||||
|
} else {
|
||||||
|
is_match.unwrap_or(true)
|
||||||
|
},
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,12 +63,12 @@ fn where_not_works() -> TestResult {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_regex_fails() -> TestResult {
|
fn invalid_regex_fails() -> TestResult {
|
||||||
fail_test(r#"'foo' =~ '['"#, "regex parse error")
|
fail_test(r#"'foo' =~ '['"#, "Invalid character class")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_not_regex_fails() -> TestResult {
|
fn invalid_not_regex_fails() -> TestResult {
|
||||||
fail_test(r#"'foo' !~ '['"#, "regex parse error")
|
fail_test(r#"'foo' !~ '['"#, "Invalid character class")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue