mirror of
https://github.com/nushell/nushell
synced 2025-01-28 04:45:18 +00:00
Add all flag to nu-check command (#5911)
* Add all flag * Make all and moduel flags as mutually exclusive * Fix new test * format code... * tweak words * another tweak Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
parent
5d00ecef56
commit
37949e70e0
2 changed files with 300 additions and 19 deletions
|
@ -19,6 +19,7 @@ impl Command for NuCheck {
|
||||||
.optional("path", SyntaxShape::Filepath, "File path to parse")
|
.optional("path", SyntaxShape::Filepath, "File path to parse")
|
||||||
.switch("as-module", "Parse content as module", Some('m'))
|
.switch("as-module", "Parse content as module", Some('m'))
|
||||||
.switch("debug", "Show error messages", Some('d'))
|
.switch("debug", "Show error messages", Some('d'))
|
||||||
|
.switch("all", "Parse content as script first, returns result if success, otherwise, try with module", Some('a'))
|
||||||
.category(Category::Strings)
|
.category(Category::Strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,29 +41,42 @@ impl Command for NuCheck {
|
||||||
let path: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
let path: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
||||||
let is_module = call.has_flag("as-module");
|
let is_module = call.has_flag("as-module");
|
||||||
let is_debug = call.has_flag("debug");
|
let is_debug = call.has_flag("debug");
|
||||||
|
let is_all = call.has_flag("all");
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let mut contents = vec![];
|
let mut contents = vec![];
|
||||||
|
|
||||||
// DO NOT ever try to merge the working_set in this command
|
// DO NOT ever try to merge the working_set in this command
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
|
if is_all && is_module {
|
||||||
|
return Err(ShellError::GenericError(
|
||||||
|
"Detected command flags conflict".to_string(),
|
||||||
|
"You cannot have both `--all` and `--as-module` on the same command line, please refer to `nu-check --help` for more details".to_string(),
|
||||||
|
Some(call.head),
|
||||||
|
None, vec![]));
|
||||||
|
}
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::Value(Value::String { val, span }, ..) => {
|
PipelineData::Value(Value::String { val, span }, ..) => {
|
||||||
let contents = Vec::from(val);
|
let contents = Vec::from(val);
|
||||||
if is_module {
|
if is_all {
|
||||||
parse_module(&mut working_set, None, contents, is_debug, span)
|
heuristic_parse(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
|
} else if is_module {
|
||||||
|
parse_module(&mut working_set, None, &contents, is_debug, span)
|
||||||
} else {
|
} else {
|
||||||
parse_script(&mut working_set, None, contents, is_debug, span)
|
parse_script(&mut working_set, None, &contents, is_debug, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PipelineData::ListStream(stream, ..) => {
|
PipelineData::ListStream(stream, ..) => {
|
||||||
let list_stream = stream.into_string("\n", config);
|
let list_stream = stream.into_string("\n", config);
|
||||||
let contents = Vec::from(list_stream);
|
let contents = Vec::from(list_stream);
|
||||||
|
|
||||||
if is_module {
|
if is_all {
|
||||||
parse_module(&mut working_set, None, contents, is_debug, call.head)
|
heuristic_parse(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
|
} else if is_module {
|
||||||
|
parse_module(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
} else {
|
} else {
|
||||||
parse_script(&mut working_set, None, contents, is_debug, call.head)
|
parse_script(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
|
@ -77,10 +91,12 @@ impl Command for NuCheck {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_module {
|
if is_all {
|
||||||
parse_module(&mut working_set, None, contents, is_debug, call.head)
|
heuristic_parse(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
|
} else if is_module {
|
||||||
|
parse_module(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
} else {
|
} else {
|
||||||
parse_script(&mut working_set, None, contents, is_debug, call.head)
|
parse_script(&mut working_set, None, &contents, is_debug, call.head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -101,7 +117,9 @@ impl Command for NuCheck {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_module {
|
if is_all {
|
||||||
|
heuristic_parse_file(path, &mut working_set, call, is_debug)
|
||||||
|
} else if is_module {
|
||||||
parse_file_module(path, &mut working_set, call, is_debug)
|
parse_file_module(path, &mut working_set, call, is_debug)
|
||||||
} else {
|
} else {
|
||||||
parse_file_script(path, &mut working_set, call, is_debug)
|
parse_file_script(path, &mut working_set, call, is_debug)
|
||||||
|
@ -151,6 +169,16 @@ impl Command for NuCheck {
|
||||||
example: "echo $'two(char nl)lines' | nu-check ",
|
example: "echo $'two(char nl)lines' | nu-check ",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Heuristically parse which begins with script first, if it sees a failure, try module afterwards",
|
||||||
|
example: "nu-check -a script.nu",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Heuristically parse by showing error message",
|
||||||
|
example: "open foo.nu | lines | nu-check -ad",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,18 +223,97 @@ fn find_path(
|
||||||
Ok(path)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn heuristic_parse(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
filename: Option<&str>,
|
||||||
|
contents: &[u8],
|
||||||
|
is_debug: bool,
|
||||||
|
span: Span,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
match parse_script(working_set, filename, contents, is_debug, span) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => {
|
||||||
|
match parse_module(
|
||||||
|
working_set,
|
||||||
|
filename.map(|f| f.to_string()),
|
||||||
|
contents,
|
||||||
|
is_debug,
|
||||||
|
span,
|
||||||
|
) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => {
|
||||||
|
if is_debug {
|
||||||
|
Err(ShellError::GenericError(
|
||||||
|
"Failed to parse content,tried both script and module".to_string(),
|
||||||
|
"syntax error".to_string(),
|
||||||
|
Some(span),
|
||||||
|
Some("Run `nu-check --help` for more details".to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(PipelineData::Value(Value::boolean(false, span), None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn heuristic_parse_file(
|
||||||
|
path: String,
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
is_debug: bool,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head);
|
||||||
|
if err.is_none() {
|
||||||
|
if let Ok(contents) = std::fs::read(&path) {
|
||||||
|
match parse_script(
|
||||||
|
working_set,
|
||||||
|
Some(filename.as_str()),
|
||||||
|
&contents,
|
||||||
|
is_debug,
|
||||||
|
call.head,
|
||||||
|
) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => {
|
||||||
|
match parse_module(working_set, Some(filename), &contents, is_debug, call.head)
|
||||||
|
{
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => {
|
||||||
|
if is_debug {
|
||||||
|
Err(ShellError::GenericError(
|
||||||
|
"Failed to parse content,tried both script and module"
|
||||||
|
.to_string(),
|
||||||
|
"syntax error".to_string(),
|
||||||
|
Some(call.head),
|
||||||
|
Some("Run `nu-check --help` for more details".to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(PipelineData::Value(Value::boolean(false, call.head), None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ShellError::IOError("Can not read input".to_string()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ShellError::NotFound(call.head))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_module(
|
fn parse_module(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
filename: Option<String>,
|
filename: Option<String>,
|
||||||
contents: Vec<u8>,
|
contents: &[u8],
|
||||||
is_debug: bool,
|
is_debug: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let start = working_set.next_span_start();
|
let start = working_set.next_span_start();
|
||||||
working_set.add_file(
|
working_set.add_file(filename.unwrap_or_else(|| "empty".to_string()), contents);
|
||||||
filename.unwrap_or_else(|| "empty".to_string()),
|
|
||||||
contents.as_ref(),
|
|
||||||
);
|
|
||||||
let end = working_set.next_span_start();
|
let end = working_set.next_span_start();
|
||||||
|
|
||||||
let new_span = Span::new(start, end);
|
let new_span = Span::new(start, end);
|
||||||
|
@ -236,11 +343,11 @@ fn parse_module(
|
||||||
fn parse_script(
|
fn parse_script(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
filename: Option<&str>,
|
filename: Option<&str>,
|
||||||
contents: Vec<u8>,
|
contents: &[u8],
|
||||||
is_debug: bool,
|
is_debug: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let (_, err) = parse(working_set, filename, &contents, false, &[]);
|
let (_, err) = parse(working_set, filename, contents, false, &[]);
|
||||||
if err.is_some() {
|
if err.is_some() {
|
||||||
let msg = format!(r#"Found : {}"#, err.expect("Unable to parse content"));
|
let msg = format!(r#"Found : {}"#, err.expect("Unable to parse content"));
|
||||||
if is_debug {
|
if is_debug {
|
||||||
|
@ -271,7 +378,7 @@ fn parse_file_script(
|
||||||
parse_script(
|
parse_script(
|
||||||
working_set,
|
working_set,
|
||||||
Some(filename.as_str()),
|
Some(filename.as_str()),
|
||||||
contents,
|
&contents,
|
||||||
is_debug,
|
is_debug,
|
||||||
call.head,
|
call.head,
|
||||||
)
|
)
|
||||||
|
@ -292,7 +399,7 @@ fn parse_file_module(
|
||||||
let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head);
|
let (filename, err) = unescape_unquote_string(path.as_bytes(), call.head);
|
||||||
if err.is_none() {
|
if err.is_none() {
|
||||||
if let Ok(contents) = std::fs::read(path) {
|
if let Ok(contents) = std::fs::read(path) {
|
||||||
parse_module(working_set, Some(filename), contents, is_debug, call.head)
|
parse_module(working_set, Some(filename), &contents, is_debug, call.head)
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::IOError("Can not read path".to_string()))
|
Err(ShellError::IOError("Can not read path".to_string()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -550,3 +550,177 @@ fn parse_module_success_with_complex_external_stream() {
|
||||||
assert!(actual.err.is_empty());
|
assert!(actual.err.is_empty());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_with_flag_all_success_for_complex_external_stream() {
|
||||||
|
Playground::setup("nu_check_test_20", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"grep.nu",
|
||||||
|
r#"
|
||||||
|
#grep for nu
|
||||||
|
def grep-nu [
|
||||||
|
search #search term
|
||||||
|
entrada? #file or pipe
|
||||||
|
#
|
||||||
|
#Examples
|
||||||
|
#grep-nu search file.txt
|
||||||
|
#ls **/* | some_filter | grep-nu search
|
||||||
|
#open file.txt | grep-nu search
|
||||||
|
] {
|
||||||
|
if ($entrada | empty?) {
|
||||||
|
if ($in | column? name) {
|
||||||
|
grep -ihHn $search ($in | get name)
|
||||||
|
} else {
|
||||||
|
($in | into string) | grep -ihHn $search
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
grep -ihHn $search $entrada
|
||||||
|
}
|
||||||
|
| lines
|
||||||
|
| parse "{file}:{line}:{match}"
|
||||||
|
| str trim
|
||||||
|
| update match {|f|
|
||||||
|
$f.match
|
||||||
|
| nu-highlight
|
||||||
|
}
|
||||||
|
| rename "source file" "line number"
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
open grep.nu | nu-check -ad
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(actual.err.is_empty());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_with_flag_all_failure_for_complex_external_stream() {
|
||||||
|
Playground::setup("nu_check_test_21", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"grep.nu",
|
||||||
|
r#"
|
||||||
|
#grep for nu
|
||||||
|
def grep-nu
|
||||||
|
search #search term
|
||||||
|
entrada? #file or pipe
|
||||||
|
#
|
||||||
|
#Examples
|
||||||
|
#grep-nu search file.txt
|
||||||
|
#ls **/* | some_filter | grep-nu search
|
||||||
|
#open file.txt | grep-nu search
|
||||||
|
] {
|
||||||
|
if ($entrada | empty?) {
|
||||||
|
if ($in | column? name) {
|
||||||
|
grep -ihHn $search ($in | get name)
|
||||||
|
} else {
|
||||||
|
($in | into string) | grep -ihHn $search
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
grep -ihHn $search $entrada
|
||||||
|
}
|
||||||
|
| lines
|
||||||
|
| parse "{file}:{line}:{match}"
|
||||||
|
| str trim
|
||||||
|
| update match {|f|
|
||||||
|
$f.match
|
||||||
|
| nu-highlight
|
||||||
|
}
|
||||||
|
| rename "source file" "line number"
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
open grep.nu | nu-check -ad
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("syntax error"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_with_flag_all_failure_for_complex_list_stream() {
|
||||||
|
Playground::setup("nu_check_test_22", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"grep.nu",
|
||||||
|
r#"
|
||||||
|
#grep for nu
|
||||||
|
def grep-nu
|
||||||
|
search #search term
|
||||||
|
entrada? #file or pipe
|
||||||
|
#
|
||||||
|
#Examples
|
||||||
|
#grep-nu search file.txt
|
||||||
|
#ls **/* | some_filter | grep-nu search
|
||||||
|
#open file.txt | grep-nu search
|
||||||
|
] {
|
||||||
|
if ($entrada | empty?) {
|
||||||
|
if ($in | column? name) {
|
||||||
|
grep -ihHn $search ($in | get name)
|
||||||
|
} else {
|
||||||
|
($in | into string) | grep -ihHn $search
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
grep -ihHn $search $entrada
|
||||||
|
}
|
||||||
|
| lines
|
||||||
|
| parse "{file}:{line}:{match}"
|
||||||
|
| str trim
|
||||||
|
| update match {|f|
|
||||||
|
$f.match
|
||||||
|
| nu-highlight
|
||||||
|
}
|
||||||
|
| rename "source file" "line number"
|
||||||
|
}
|
||||||
|
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
open grep.nu | lines | nu-check -ad
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("syntax error"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_failure_due_conflicted_flags() {
|
||||||
|
Playground::setup("nu_check_test_23", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"script.nu",
|
||||||
|
r#"
|
||||||
|
greet "world"
|
||||||
|
|
||||||
|
def greet [name] {
|
||||||
|
echo "hello" $name
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
nu-check -a --as-module script.nu
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(actual.err.contains(
|
||||||
|
"You could not have both `--all` and `--as-module` at the same command line"
|
||||||
|
));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue