Break up interdependencies of command crates (#9429)

# Description
Make sure that our different crates that contain commands can be
compiled in parallel.
This can under certain circumstances accelerate the compilation with
sufficient multithreading available.

## Details
- Move `help` commands from `nu-cmd-lang` back to `nu-command`
- This also makes sense as the commands are implemented in an
ANSI-terminal specific way
- Make `nu-cmd-lang` only a dev dependency for `nu-command`
- Change context creation helpers for `nu-cmd-extra` and
`nu-cmd-dataframe` to have a consistent api used in
`src/main.rs`:`get_engine_state()`
- `nu-command` now indepedent from `nu-cmd-extra` and `nu-cmd-dataframe`
that are now dependencies of `nu` directly. (change to internal
features)
- Fix tests that previously used `nu-command::create_default_context()`
with replacement functions

## From scratch compilation times:

just debug (dev) build and default features
```
cargo clean --profile dev && cargo build --timings
```

### before

![grafik](https://github.com/nushell/nushell/assets/15833959/e49f1f42-2e53-4a6c-bc23-625b686af1bc)

### after

![grafik](https://github.com/nushell/nushell/assets/15833959/8dec4723-e625-4a86-b91e-e6e808f64726)

# User-Facing Changes
None direct, only change to compilation on multithreaded jobs expected.

# Tests + Formatting
Tests that previously chose to use `nu-command` for their scope will
still use `nu-cmd-lang` + `nu-command` (command list in the granularity
at the time)
This commit is contained in:
Stefan Holderbach
2023-06-14 23:12:55 +02:00
committed by GitHub
parent b14bdd865f
commit 46eebc644c
26 changed files with 132 additions and 85 deletions

View File

@ -0,0 +1,159 @@
use crate::help::highlight_search_in_table;
use nu_color_config::StyleComputer;
use nu_engine::{get_full_help, scope::ScopeData, CallExt};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
};
#[derive(Clone)]
pub struct HelpExterns;
impl Command for HelpExterns {
fn name(&self) -> &str {
"help externs"
}
fn usage(&self) -> &str {
"Show help on nushell externs."
}
fn signature(&self) -> Signature {
Signature::build("help externs")
.category(Category::Core)
.rest(
"rest",
SyntaxShape::String,
"the name of extern to get help on",
)
.named(
"find",
SyntaxShape::String,
"string to find in extern names and usage",
Some('f'),
)
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
.allow_variants_without_examples(true)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "show all externs",
example: "help externs",
result: None,
},
Example {
description: "show help for single extern",
example: "help externs smth",
result: None,
},
Example {
description: "search for string in extern names and usages",
example: "help externs --find smth",
result: None,
},
]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
help_externs(engine_state, stack, call)
}
}
pub fn help_externs(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
// 🚩The following two-lines are copied from filters/find.rs:
let style_computer = StyleComputer::from_config(engine_state, stack);
// Currently, search results all use the same style.
// Also note that this sample string is passed into user-written code (the closure that may or may not be
// defined for "string").
let string_style = style_computer.compute("string", &Value::string("search result", head));
let highlight_style =
style_computer.compute("search_result", &Value::string("search result", head));
if let Some(f) = find {
let all_cmds_vec = build_help_externs(engine_state, stack, head);
let found_cmds_vec = highlight_search_in_table(
all_cmds_vec,
&f.item,
&["name", "usage"],
&string_style,
&highlight_style,
)?;
return Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()));
}
if rest.is_empty() {
let found_cmds_vec = build_help_externs(engine_state, stack, head);
Ok(found_cmds_vec
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
} else {
let mut name = String::new();
for r in &rest {
if !name.is_empty() {
name.push(' ');
}
name.push_str(&r.item);
}
let output = engine_state
.get_signatures_with_examples(false)
.iter()
.filter(|(signature, _, _, _, _)| signature.name == name)
.map(|(signature, examples, _, _, is_parser_keyword)| {
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
})
.collect::<Vec<String>>();
if !output.is_empty() {
Ok(Value::String {
val: output.join("======================\n\n"),
span: call.head,
}
.into_pipeline_data())
} else {
Err(ShellError::CommandNotFound(span(&[
rest[0].span,
rest[rest.len() - 1].span,
])))
}
}
}
fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
let mut scope = ScopeData::new(engine_state, stack);
scope.populate_all();
scope.collect_externs(span)
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::HelpExterns;
use crate::test_examples;
test_examples(HelpExterns {})
}
}