Expand Nushell's help system (#7611)

This commit is contained in:
Jakub Žádník
2022-12-30 17:44:37 +02:00
committed by GitHub
parent f3d2be7a56
commit 8bfcea8054
23 changed files with 1509 additions and 446 deletions

View File

@ -25,7 +25,7 @@ use crate::{
parse_internal_call, parse_multispan_value, parse_signature, parse_string, parse_value,
parse_var_with_opt_type, trim_quotes, ParsedInternalCall,
},
unescape_unquote_string, ParseError,
unescape_unquote_string, ParseError, Token, TokenContents,
};
pub fn parse_def_predecl(
@ -229,57 +229,6 @@ pub fn parse_for(
)
}
fn build_usage(working_set: &StateWorkingSet, spans: &[Span]) -> String {
let mut usage = String::new();
let mut num_spaces = 0;
let mut first = true;
// Use the comments to build the usage
for comment_part in spans {
let contents = working_set.get_span_contents(*comment_part);
let comment_line = if first {
// Count the number of spaces still at the front, skipping the '#'
let mut pos = 1;
while pos < contents.len() {
if let Some(b' ') = contents.get(pos) {
// continue
} else {
break;
}
pos += 1;
}
num_spaces = pos;
first = false;
String::from_utf8_lossy(&contents[pos..]).to_string()
} else {
let mut pos = 1;
while pos < contents.len() && pos < num_spaces {
if let Some(b' ') = contents.get(pos) {
// continue
} else {
break;
}
pos += 1;
}
String::from_utf8_lossy(&contents[pos..]).to_string()
};
if !usage.is_empty() {
usage.push('\n');
}
usage.push_str(&comment_line);
}
usage
}
pub fn parse_def(
working_set: &mut StateWorkingSet,
lite_command: &LiteCommand,
@ -287,7 +236,7 @@ pub fn parse_def(
) -> (Pipeline, Option<ParseError>) {
let spans = &lite_command.parts[..];
let usage = build_usage(working_set, &lite_command.comments);
let (usage, extra_usage) = working_set.build_usage(&lite_command.comments);
// Checking that the function is used with the correct name
// Maybe this is not necessary but it is a sanity check
@ -397,6 +346,7 @@ pub fn parse_def(
signature.name = name.clone();
*signature = signature.add_help();
signature.usage = usage;
signature.extra_usage = extra_usage;
*declaration = signature.clone().into_block_command(block_id);
@ -444,7 +394,7 @@ pub fn parse_extern(
let spans = &lite_command.parts;
let mut error = None;
let usage = build_usage(working_set, &lite_command.comments);
let (usage, extra_usage) = working_set.build_usage(&lite_command.comments);
// Checking that the function is used with the correct name
// Maybe this is not necessary but it is a sanity check
@ -515,11 +465,12 @@ pub fn parse_extern(
signature.name = name.clone();
signature.usage = usage.clone();
signature.extra_usage = extra_usage.clone();
signature.allows_unknown_args = true;
let decl = KnownExternal {
name: name.to_string(),
usage,
usage: [usage, extra_usage].join("\n"),
signature,
};
@ -559,9 +510,11 @@ pub fn parse_extern(
pub fn parse_alias(
working_set: &mut StateWorkingSet,
spans: &[Span],
lite_command: &LiteCommand,
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
let spans = &lite_command.parts;
// if the call is "alias", turn it into "print $nu.scope.aliases"
if spans.len() == 1 {
let head = Expression {
@ -672,7 +625,7 @@ pub fn parse_alias(
);
}
working_set.add_alias(alias_name, replacement);
working_set.add_alias(alias_name, replacement, lite_command.comments.clone());
}
let err = if spans.len() < 4 {
@ -785,7 +738,7 @@ pub fn parse_export_in_block(
}
match full_name.as_slice() {
b"export alias" => parse_alias(working_set, &lite_command.parts, expand_aliases_denylist),
b"export alias" => parse_alias(working_set, lite_command, expand_aliases_denylist),
b"export def" | b"export def-env" => {
parse_def(working_set, lite_command, expand_aliases_denylist)
}
@ -1075,7 +1028,7 @@ pub fn parse_export_in_module(
parts: spans[1..].to_vec(),
};
let (pipeline, err) =
parse_alias(working_set, &lite_command.parts, expand_aliases_denylist);
parse_alias(working_set, &lite_command, expand_aliases_denylist);
error = error.or(err);
let export_alias_decl_id =
@ -1328,11 +1281,41 @@ pub fn parse_export_env(
(pipeline, Some(block_id), None)
}
fn collect_first_comments(tokens: &[Token]) -> Vec<Span> {
let mut comments = vec![];
let mut tokens_iter = tokens.iter().peekable();
while let Some(token) = tokens_iter.next() {
match token.contents {
TokenContents::Comment => {
comments.push(token.span);
}
TokenContents::Eol => {
if let Some(Token {
contents: TokenContents::Eol,
..
}) = tokens_iter.peek()
{
if !comments.is_empty() {
break;
}
}
}
_ => {
comments.clear();
break;
}
}
}
comments
}
pub fn parse_module_block(
working_set: &mut StateWorkingSet,
span: Span,
expand_aliases_denylist: &[usize],
) -> (Block, Module, Option<ParseError>) {
) -> (Block, Module, Vec<Span>, Option<ParseError>) {
let mut error = None;
working_set.enter_scope();
@ -1342,6 +1325,8 @@ pub fn parse_module_block(
let (output, err) = lex(source, span.start, &[], &[], false);
error = error.or(err);
let module_comments = collect_first_comments(&output);
let (output, err) = lite_parse(&output);
error = error.or(err);
@ -1378,11 +1363,8 @@ pub fn parse_module_block(
(pipeline, err)
}
b"alias" => {
let (pipeline, err) = parse_alias(
working_set,
&command.parts,
expand_aliases_denylist,
);
let (pipeline, err) =
parse_alias(working_set, command, expand_aliases_denylist);
(pipeline, err)
}
@ -1452,17 +1434,20 @@ pub fn parse_module_block(
working_set.exit_scope();
(block, module, error)
(block, module, module_comments, error)
}
pub fn parse_module(
working_set: &mut StateWorkingSet,
spans: &[Span],
lite_command: &LiteCommand,
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
// TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are
// visible and usable in this module's scope). We want to disable that for files.
let spans = &lite_command.parts;
let mut module_comments = lite_command.comments.clone();
let mut error = None;
let bytes = working_set.get_span_contents(spans[0]);
@ -1496,12 +1481,14 @@ pub fn parse_module(
let block_span = Span::new(start, end);
let (block, module, err) =
let (block, module, inner_comments, err) =
parse_module_block(working_set, block_span, expand_aliases_denylist);
error = error.or(err);
let block_id = working_set.add_block(block);
let _ = working_set.add_module(&module_name, module);
module_comments.extend(inner_comments);
let _ = working_set.add_module(&module_name, module, module_comments);
let block_expr = Expression {
expr: Expr::Block(block_id),
@ -1734,7 +1721,7 @@ pub fn parse_use(
working_set.parsed_module_files.push(module_path);
// Parse the module
let (block, module, err) = parse_module_block(
let (block, module, module_comments, err) = parse_module_block(
working_set,
Span::new(span_start, span_end),
expand_aliases_denylist,
@ -1748,7 +1735,8 @@ pub fn parse_use(
working_set.currently_parsed_cwd = prev_currently_parsed_cwd;
let _ = working_set.add_block(block);
let module_id = working_set.add_module(&module_name, module.clone());
let module_id =
working_set.add_module(&module_name, module.clone(), module_comments);
(
ImportPattern {
@ -2320,7 +2308,7 @@ pub fn parse_overlay_new(
custom_completion: None,
}]);
let module_id = working_set.add_module(&overlay_name, Module::new());
let module_id = working_set.add_module(&overlay_name, Module::new(), vec![]);
working_set.add_overlay(
overlay_name.as_bytes().to_vec(),
@ -2557,7 +2545,7 @@ pub fn parse_overlay_use(
working_set.currently_parsed_cwd.clone()
};
let (block, module, err) = parse_module_block(
let (block, module, module_comments, err) = parse_module_block(
working_set,
Span::new(span_start, span_end),
expand_aliases_denylist,
@ -2568,7 +2556,8 @@ pub fn parse_overlay_use(
working_set.currently_parsed_cwd = prev_currently_parsed_cwd;
let _ = working_set.add_block(block);
let module_id = working_set.add_module(&overlay_name, module.clone());
let module_id =
working_set.add_module(&overlay_name, module.clone(), module_comments);
(
new_name.map(|spanned| spanned.item).unwrap_or(overlay_name),

View File

@ -5248,8 +5248,8 @@ pub fn parse_builtin_commands(
let (expr, err) = parse_for(working_set, &lite_command.parts, expand_aliases_denylist);
(Pipeline::from_vec(vec![expr]), err)
}
b"alias" => parse_alias(working_set, &lite_command.parts, expand_aliases_denylist),
b"module" => parse_module(working_set, &lite_command.parts, expand_aliases_denylist),
b"alias" => parse_alias(working_set, lite_command, expand_aliases_denylist),
b"module" => parse_module(working_set, lite_command, expand_aliases_denylist),
b"use" => {
let (pipeline, _, err) =
parse_use(working_set, &lite_command.parts, expand_aliases_denylist);