mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-07 01:51:14 -03:00
Allow if/then/fi, while/do/done and for/do/done
This commit is contained in:
1
share/completions/do.fish
Normal file
1
share/completions/do.fish
Normal file
@@ -0,0 +1 @@
|
|||||||
|
complete -c do -xa '(__fish_complete_subcommand)'
|
||||||
1
share/completions/then.fish
Normal file
1
share/completions/then.fish
Normal file
@@ -0,0 +1 @@
|
|||||||
|
complete -c then -xa '(__fish_complete_subcommand)'
|
||||||
99
src/ast.rs
99
src/ast.rs
@@ -79,8 +79,10 @@ fn visit_decorated_statement_decorator(
|
|||||||
_node: &mut Option<DecoratedStatementDecorator>,
|
_node: &mut Option<DecoratedStatementDecorator>,
|
||||||
);
|
);
|
||||||
fn visit_job_conjunction_decorator(&mut self, _node: &mut Option<JobConjunctionDecorator>);
|
fn visit_job_conjunction_decorator(&mut self, _node: &mut Option<JobConjunctionDecorator>);
|
||||||
|
fn visit_do(&mut self, _node: &mut Option<KeywordDo>);
|
||||||
fn visit_else_clause(&mut self, _node: &mut Option<ElseClause>);
|
fn visit_else_clause(&mut self, _node: &mut Option<ElseClause>);
|
||||||
fn visit_semi_nl(&mut self, _node: &mut Option<SemiNl>);
|
fn visit_semi_nl(&mut self, _node: &mut Option<SemiNl>);
|
||||||
|
fn visit_then(&mut self, _node: &mut Option<KeywordThen>);
|
||||||
fn visit_time(&mut self, _node: &mut Option<KeywordTime>);
|
fn visit_time(&mut self, _node: &mut Option<KeywordTime>);
|
||||||
fn visit_token_background(&mut self, _node: &mut Option<TokenBackground>);
|
fn visit_token_background(&mut self, _node: &mut Option<TokenBackground>);
|
||||||
}
|
}
|
||||||
@@ -977,6 +979,12 @@ macro_rules! visit_optional_field_mut {
|
|||||||
(KeywordTime, $field:expr, $visitor:ident) => {
|
(KeywordTime, $field:expr, $visitor:ident) => {
|
||||||
$visitor.visit_time(&mut $field);
|
$visitor.visit_time(&mut $field);
|
||||||
};
|
};
|
||||||
|
(KeywordDo, $field:expr, $visitor:ident) => {
|
||||||
|
$visitor.visit_do(&mut $field);
|
||||||
|
};
|
||||||
|
(KeywordThen, $field:expr, $visitor:ident) => {
|
||||||
|
$visitor.visit_then(&mut $field);
|
||||||
|
};
|
||||||
(TokenBackground, $field:expr, $visitor:ident) => {
|
(TokenBackground, $field:expr, $visitor:ident) => {
|
||||||
$visitor.visit_token_background(&mut $field);
|
$visitor.visit_token_background(&mut $field);
|
||||||
};
|
};
|
||||||
@@ -1315,7 +1323,11 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
|| (token.typ == ParseTokenType::string
|
|| (token.typ == ParseTokenType::string
|
||||||
&& !matches!(
|
&& !matches!(
|
||||||
token.keyword,
|
token.keyword,
|
||||||
ParseKeyword::kw_case | ParseKeyword::kw_end | ParseKeyword::kw_else
|
ParseKeyword::kw_case
|
||||||
|
| ParseKeyword::kw_done
|
||||||
|
| ParseKeyword::kw_else
|
||||||
|
| ParseKeyword::kw_end
|
||||||
|
| ParseKeyword::kw_fi
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1333,6 +1345,8 @@ pub struct ForHeader {
|
|||||||
pub args: ArgumentList,
|
pub args: ArgumentList,
|
||||||
/// newline or semicolon
|
/// newline or semicolon
|
||||||
pub semi_nl: SemiNl,
|
pub semi_nl: SemiNl,
|
||||||
|
// Optional "do" for compatibility with POSIX.
|
||||||
|
pub kw_do: Option<KeywordDo>,
|
||||||
}
|
}
|
||||||
implement_node!(ForHeader, branch, for_header);
|
implement_node!(ForHeader, branch, for_header);
|
||||||
implement_acceptor_for_branch!(
|
implement_acceptor_for_branch!(
|
||||||
@@ -1342,6 +1356,7 @@ pub struct ForHeader {
|
|||||||
(kw_in: (KeywordIn)),
|
(kw_in: (KeywordIn)),
|
||||||
(args: (ArgumentList)),
|
(args: (ArgumentList)),
|
||||||
(semi_nl: (SemiNl)),
|
(semi_nl: (SemiNl)),
|
||||||
|
(kw_do: (Option<KeywordDo>)),
|
||||||
);
|
);
|
||||||
impl ConcreteNode for ForHeader {
|
impl ConcreteNode for ForHeader {
|
||||||
fn as_for_header(&self) -> Option<&ForHeader> {
|
fn as_for_header(&self) -> Option<&ForHeader> {
|
||||||
@@ -1361,6 +1376,8 @@ pub struct WhileHeader {
|
|||||||
pub kw_while: KeywordWhile,
|
pub kw_while: KeywordWhile,
|
||||||
pub condition: JobConjunction,
|
pub condition: JobConjunction,
|
||||||
pub andor_tail: AndorJobList,
|
pub andor_tail: AndorJobList,
|
||||||
|
// Optional "do" for compatibility with POSIX.
|
||||||
|
pub kw_do: Option<KeywordDo>,
|
||||||
}
|
}
|
||||||
implement_node!(WhileHeader, branch, while_header);
|
implement_node!(WhileHeader, branch, while_header);
|
||||||
implement_acceptor_for_branch!(
|
implement_acceptor_for_branch!(
|
||||||
@@ -1368,6 +1385,7 @@ pub struct WhileHeader {
|
|||||||
(kw_while: (KeywordWhile)),
|
(kw_while: (KeywordWhile)),
|
||||||
(condition: (JobConjunction)),
|
(condition: (JobConjunction)),
|
||||||
(andor_tail: (AndorJobList)),
|
(andor_tail: (AndorJobList)),
|
||||||
|
(kw_do: (Option<KeywordDo>)),
|
||||||
);
|
);
|
||||||
impl ConcreteNode for WhileHeader {
|
impl ConcreteNode for WhileHeader {
|
||||||
fn as_while_header(&self) -> Option<&WhileHeader> {
|
fn as_while_header(&self) -> Option<&WhileHeader> {
|
||||||
@@ -1441,7 +1459,7 @@ pub struct BlockStatement {
|
|||||||
/// List of jobs in this block.
|
/// List of jobs in this block.
|
||||||
pub jobs: JobList,
|
pub jobs: JobList,
|
||||||
/// The 'end' node.
|
/// The 'end' node.
|
||||||
pub end: KeywordEnd,
|
pub end: KeywordEndOrDone,
|
||||||
/// Arguments and redirections associated with the block.
|
/// Arguments and redirections associated with the block.
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
@@ -1450,7 +1468,7 @@ pub struct BlockStatement {
|
|||||||
BlockStatement,
|
BlockStatement,
|
||||||
(header: (variant<BlockStatementHeaderVariant>)),
|
(header: (variant<BlockStatementHeaderVariant>)),
|
||||||
(jobs: (JobList)),
|
(jobs: (JobList)),
|
||||||
(end: (KeywordEnd)),
|
(end: (KeywordEndOrDone)),
|
||||||
(args_or_redirs: (ArgumentOrRedirectionList)),
|
(args_or_redirs: (ArgumentOrRedirectionList)),
|
||||||
);
|
);
|
||||||
impl ConcreteNode for BlockStatement {
|
impl ConcreteNode for BlockStatement {
|
||||||
@@ -1504,6 +1522,10 @@ pub struct IfClause {
|
|||||||
pub condition: JobConjunction,
|
pub condition: JobConjunction,
|
||||||
/// 'and/or' tail.
|
/// 'and/or' tail.
|
||||||
pub andor_tail: AndorJobList,
|
pub andor_tail: AndorJobList,
|
||||||
|
// Semi
|
||||||
|
pub semi_nl: SemiNl,
|
||||||
|
// Optional "then" for compatibility with POSIX.
|
||||||
|
pub kw_then: Option<KeywordThen>,
|
||||||
/// The body to execute if the condition is true.
|
/// The body to execute if the condition is true.
|
||||||
pub body: JobList,
|
pub body: JobList,
|
||||||
}
|
}
|
||||||
@@ -1513,6 +1535,7 @@ pub struct IfClause {
|
|||||||
(kw_if: (KeywordIf)),
|
(kw_if: (KeywordIf)),
|
||||||
(condition: (JobConjunction)),
|
(condition: (JobConjunction)),
|
||||||
(andor_tail: (AndorJobList)),
|
(andor_tail: (AndorJobList)),
|
||||||
|
(kw_then: (Option<KeywordThen>)),
|
||||||
(body: (JobList)),
|
(body: (JobList)),
|
||||||
);
|
);
|
||||||
impl ConcreteNode for IfClause {
|
impl ConcreteNode for IfClause {
|
||||||
@@ -1610,7 +1633,7 @@ pub struct IfStatement {
|
|||||||
/// else part
|
/// else part
|
||||||
pub else_clause: Option<ElseClause>,
|
pub else_clause: Option<ElseClause>,
|
||||||
/// literal end
|
/// literal end
|
||||||
pub end: KeywordEnd,
|
pub end: KeywordEndOrFi,
|
||||||
/// block args / redirs
|
/// block args / redirs
|
||||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||||
}
|
}
|
||||||
@@ -1620,7 +1643,7 @@ pub struct IfStatement {
|
|||||||
(if_clause: (IfClause)),
|
(if_clause: (IfClause)),
|
||||||
(elseif_clauses: (ElseifClauseList)),
|
(elseif_clauses: (ElseifClauseList)),
|
||||||
(else_clause: (Option<ElseClause>)),
|
(else_clause: (Option<ElseClause>)),
|
||||||
(end: (KeywordEnd)),
|
(end: (KeywordEndOrFi)),
|
||||||
(args_or_redirs: (ArgumentOrRedirectionList)),
|
(args_or_redirs: (ArgumentOrRedirectionList)),
|
||||||
);
|
);
|
||||||
impl ConcreteNode for IfStatement {
|
impl ConcreteNode for IfStatement {
|
||||||
@@ -2073,14 +2096,18 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
define_keyword_node!(JobConjunctionDecorator, kw_and, kw_or);
|
define_keyword_node!(JobConjunctionDecorator, kw_and, kw_or);
|
||||||
define_keyword_node!(KeywordBegin, kw_begin);
|
define_keyword_node!(KeywordBegin, kw_begin);
|
||||||
define_keyword_node!(KeywordCase, kw_case);
|
define_keyword_node!(KeywordCase, kw_case);
|
||||||
|
define_keyword_node!(KeywordDo, kw_do);
|
||||||
define_keyword_node!(KeywordElse, kw_else);
|
define_keyword_node!(KeywordElse, kw_else);
|
||||||
define_keyword_node!(KeywordEnd, kw_end);
|
define_keyword_node!(KeywordEnd, kw_end);
|
||||||
|
define_keyword_node!(KeywordEndOrDone, kw_end, kw_done);
|
||||||
|
define_keyword_node!(KeywordEndOrFi, kw_end, kw_fi);
|
||||||
define_keyword_node!(KeywordFor, kw_for);
|
define_keyword_node!(KeywordFor, kw_for);
|
||||||
define_keyword_node!(KeywordFunction, kw_function);
|
define_keyword_node!(KeywordFunction, kw_function);
|
||||||
define_keyword_node!(KeywordIf, kw_if);
|
define_keyword_node!(KeywordIf, kw_if);
|
||||||
define_keyword_node!(KeywordIn, kw_in);
|
define_keyword_node!(KeywordIn, kw_in);
|
||||||
define_keyword_node!(KeywordNot, kw_not, kw_exclam);
|
define_keyword_node!(KeywordNot, kw_not, kw_exclam);
|
||||||
define_keyword_node!(KeywordSwitch, kw_switch);
|
define_keyword_node!(KeywordSwitch, kw_switch);
|
||||||
|
define_keyword_node!(KeywordThen, kw_then);
|
||||||
define_keyword_node!(KeywordTime, kw_time);
|
define_keyword_node!(KeywordTime, kw_time);
|
||||||
define_keyword_node!(KeywordWhile, kw_while);
|
define_keyword_node!(KeywordWhile, kw_while);
|
||||||
|
|
||||||
@@ -2125,6 +2152,26 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CheckParse for KeywordDo {
|
||||||
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
|
let keyword = pop.peek_token(0).keyword;
|
||||||
|
if !matches!(keyword, ParseKeyword::kw_do) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
!pop.peek_token(1).is_dash_prefix_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CheckParse for KeywordThen {
|
||||||
|
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||||
|
let keyword = pop.peek_token(0).keyword;
|
||||||
|
if !matches!(keyword, ParseKeyword::kw_then) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
!pop.peek_token(1).is_dash_prefix_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DecoratedStatement {
|
impl DecoratedStatement {
|
||||||
/// Return the decoration for this statement.
|
/// Return the decoration for this statement.
|
||||||
pub fn decoration(&self) -> StatementDecoration {
|
pub fn decoration(&self) -> StatementDecoration {
|
||||||
@@ -3161,7 +3208,10 @@ fn did_visit_fields_of<'a>(&'a mut self, node: &'a dyn NodeMut, flow: VisitResul
|
|||||||
if next_token.typ == ParseTokenType::string
|
if next_token.typ == ParseTokenType::string
|
||||||
&& matches!(
|
&& matches!(
|
||||||
next_token.keyword,
|
next_token.keyword,
|
||||||
ParseKeyword::kw_case | ParseKeyword::kw_else | ParseKeyword::kw_end
|
ParseKeyword::kw_case
|
||||||
|
| ParseKeyword::kw_else
|
||||||
|
| ParseKeyword::kw_end
|
||||||
|
| ParseKeyword::kw_fi
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.consume_excess_token_generating_error();
|
self.consume_excess_token_generating_error();
|
||||||
@@ -3245,6 +3295,12 @@ fn visit_semi_nl(&mut self, node: &mut Option<SemiNl>) {
|
|||||||
fn visit_time(&mut self, node: &mut Option<KeywordTime>) {
|
fn visit_time(&mut self, node: &mut Option<KeywordTime>) {
|
||||||
*node = self.try_parse::<KeywordTime>().map(|b| *b);
|
*node = self.try_parse::<KeywordTime>().map(|b| *b);
|
||||||
}
|
}
|
||||||
|
fn visit_do(&mut self, node: &mut Option<KeywordDo>) {
|
||||||
|
*node = self.try_parse::<KeywordDo>().map(|b| *b);
|
||||||
|
}
|
||||||
|
fn visit_then(&mut self, node: &mut Option<KeywordThen>) {
|
||||||
|
*node = self.try_parse::<KeywordThen>().map(|b| *b);
|
||||||
|
}
|
||||||
fn visit_token_background(&mut self, node: &mut Option<TokenBackground>) {
|
fn visit_token_background(&mut self, node: &mut Option<TokenBackground>) {
|
||||||
*node = self.try_parse::<TokenBackground>().map(|b| *b);
|
*node = self.try_parse::<TokenBackground>().map(|b| *b);
|
||||||
}
|
}
|
||||||
@@ -3546,6 +3602,14 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
"'case' builtin not inside of switch block"
|
"'case' builtin not inside of switch block"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
ParseKeyword::kw_done => {
|
||||||
|
parse_error!(
|
||||||
|
self,
|
||||||
|
tok,
|
||||||
|
ParseErrorCode::unbalancing_case,
|
||||||
|
"'done' outside of a block"
|
||||||
|
);
|
||||||
|
}
|
||||||
ParseKeyword::kw_end => {
|
ParseKeyword::kw_end => {
|
||||||
parse_error!(
|
parse_error!(
|
||||||
self,
|
self,
|
||||||
@@ -3562,6 +3626,14 @@ fn consume_excess_token_generating_error(&mut self) {
|
|||||||
"'else' builtin not inside of if block"
|
"'else' builtin not inside of if block"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
ParseKeyword::kw_fi => {
|
||||||
|
parse_error!(
|
||||||
|
self,
|
||||||
|
tok,
|
||||||
|
ParseErrorCode::unbalancing_end,
|
||||||
|
"'fi' outside of a block"
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
internal_error!(
|
internal_error!(
|
||||||
self,
|
self,
|
||||||
@@ -3819,9 +3891,11 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> StatementVariant {
|
|||||||
// looks like an option (starts with a dash), then parse it as a decorated statement.
|
// looks like an option (starts with a dash), then parse it as a decorated statement.
|
||||||
let help_only_kws = [
|
let help_only_kws = [
|
||||||
ParseKeyword::kw_begin,
|
ParseKeyword::kw_begin,
|
||||||
|
ParseKeyword::kw_do,
|
||||||
ParseKeyword::kw_function,
|
ParseKeyword::kw_function,
|
||||||
ParseKeyword::kw_if,
|
ParseKeyword::kw_if,
|
||||||
ParseKeyword::kw_switch,
|
ParseKeyword::kw_switch,
|
||||||
|
ParseKeyword::kw_then,
|
||||||
ParseKeyword::kw_while,
|
ParseKeyword::kw_while,
|
||||||
];
|
];
|
||||||
if if help_only_kws.contains(&self.peek_token(0).keyword) {
|
if if help_only_kws.contains(&self.peek_token(0).keyword) {
|
||||||
@@ -3834,8 +3908,13 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> StatementVariant {
|
|||||||
|
|
||||||
// Likewise if the next token doesn't look like an argument at all. This corresponds to
|
// Likewise if the next token doesn't look like an argument at all. This corresponds to
|
||||||
// e.g. a "naked if".
|
// e.g. a "naked if".
|
||||||
let naked_invocation_invokes_help = ![ParseKeyword::kw_begin, ParseKeyword::kw_end]
|
let naked_invocation_invokes_help = ![
|
||||||
.contains(&self.peek_token(0).keyword);
|
ParseKeyword::kw_begin,
|
||||||
|
ParseKeyword::kw_done,
|
||||||
|
ParseKeyword::kw_end,
|
||||||
|
ParseKeyword::kw_fi,
|
||||||
|
]
|
||||||
|
.contains(&self.peek_token(0).keyword);
|
||||||
if naked_invocation_invokes_help
|
if naked_invocation_invokes_help
|
||||||
&& [ParseTokenType::end, ParseTokenType::terminate]
|
&& [ParseTokenType::end, ParseTokenType::terminate]
|
||||||
.contains(&self.peek_token(1).typ)
|
.contains(&self.peek_token(1).typ)
|
||||||
@@ -3864,7 +3943,7 @@ fn new_decorated_statement(slf: &mut Populator<'_>) -> StatementVariant {
|
|||||||
let embedded = self.allocate_visit::<SwitchStatement>();
|
let embedded = self.allocate_visit::<SwitchStatement>();
|
||||||
StatementVariant::SwitchStatement(embedded)
|
StatementVariant::SwitchStatement(embedded)
|
||||||
}
|
}
|
||||||
ParseKeyword::kw_end => {
|
ParseKeyword::kw_done | ParseKeyword::kw_end | ParseKeyword::kw_fi => {
|
||||||
// 'end' is forbidden as a command.
|
// 'end' is forbidden as a command.
|
||||||
// For example, `if end` or `while end` will produce this error.
|
// For example, `if end` or `while end` will produce this error.
|
||||||
// We still have to descend into the decorated statement because
|
// We still have to descend into the decorated statement because
|
||||||
@@ -4036,7 +4115,7 @@ fn visit_keyword(&mut self, keyword: &mut dyn Keyword) -> VisitResult {
|
|||||||
|
|
||||||
// Special error reporting for keyword_t<kw_end>.
|
// Special error reporting for keyword_t<kw_end>.
|
||||||
let allowed_keywords = keyword.allowed_keywords();
|
let allowed_keywords = keyword.allowed_keywords();
|
||||||
if keyword.allowed_keywords() == [ParseKeyword::kw_end] {
|
if allowed_keywords.contains(&ParseKeyword::kw_end) {
|
||||||
return VisitResult::Break(MissingEndError {
|
return VisitResult::Break(MissingEndError {
|
||||||
allowed_keywords,
|
allowed_keywords,
|
||||||
token: *self.peek_token(0),
|
token: *self.peek_token(0),
|
||||||
|
|||||||
@@ -209,6 +209,14 @@ struct BuiltinData {
|
|||||||
name: L!("disown"),
|
name: L!("disown"),
|
||||||
func: disown::disown,
|
func: disown::disown,
|
||||||
},
|
},
|
||||||
|
BuiltinData {
|
||||||
|
name: L!("do"),
|
||||||
|
func: builtin_generic,
|
||||||
|
},
|
||||||
|
BuiltinData {
|
||||||
|
name: L!("done"),
|
||||||
|
func: builtin_generic,
|
||||||
|
},
|
||||||
BuiltinData {
|
BuiltinData {
|
||||||
name: L!("echo"),
|
name: L!("echo"),
|
||||||
func: echo::echo,
|
func: echo::echo,
|
||||||
@@ -245,6 +253,10 @@ struct BuiltinData {
|
|||||||
name: L!("fg"),
|
name: L!("fg"),
|
||||||
func: fg::fg,
|
func: fg::fg,
|
||||||
},
|
},
|
||||||
|
BuiltinData {
|
||||||
|
name: L!("fi"),
|
||||||
|
func: builtin_generic,
|
||||||
|
},
|
||||||
BuiltinData {
|
BuiltinData {
|
||||||
name: L!("fish_indent"),
|
name: L!("fish_indent"),
|
||||||
func: fish_indent::fish_indent,
|
func: fish_indent::fish_indent,
|
||||||
@@ -345,6 +357,10 @@ struct BuiltinData {
|
|||||||
name: L!("test"),
|
name: L!("test"),
|
||||||
func: test::test,
|
func: test::test,
|
||||||
},
|
},
|
||||||
|
BuiltinData {
|
||||||
|
name: L!("then"),
|
||||||
|
func: builtin_generic,
|
||||||
|
},
|
||||||
BuiltinData {
|
BuiltinData {
|
||||||
name: L!("time"),
|
name: L!("time"),
|
||||||
func: builtin_generic,
|
func: builtin_generic,
|
||||||
@@ -504,6 +520,8 @@ pub fn builtin_get_desc(name: &wstr) -> Option<&'static wstr> {
|
|||||||
_ if name == "continue" => wgettext!("Skip over remaining innermost loop"),
|
_ if name == "continue" => wgettext!("Skip over remaining innermost loop"),
|
||||||
_ if name == "count" => wgettext!("Count the number of arguments"),
|
_ if name == "count" => wgettext!("Count the number of arguments"),
|
||||||
_ if name == "disown" => wgettext!("Remove job from job list"),
|
_ if name == "disown" => wgettext!("Remove job from job list"),
|
||||||
|
_ if name == "do" => wgettext!("Optional after a loop header"),
|
||||||
|
_ if name == "done" => wgettext!("Alternative to 'end', to end a block of commands"),
|
||||||
_ if name == "echo" => wgettext!("Print arguments"),
|
_ if name == "echo" => wgettext!("Print arguments"),
|
||||||
_ if name == "else" => wgettext!("Evaluate block if condition is false"),
|
_ if name == "else" => wgettext!("Evaluate block if condition is false"),
|
||||||
_ if name == "emit" => wgettext!("Emit an event"),
|
_ if name == "emit" => wgettext!("Emit an event"),
|
||||||
@@ -513,6 +531,7 @@ pub fn builtin_get_desc(name: &wstr) -> Option<&'static wstr> {
|
|||||||
_ if name == "exit" => wgettext!("Exit the shell"),
|
_ if name == "exit" => wgettext!("Exit the shell"),
|
||||||
_ if name == "false" => wgettext!("Return an unsuccessful result"),
|
_ if name == "false" => wgettext!("Return an unsuccessful result"),
|
||||||
_ if name == "fg" => wgettext!("Send job to foreground"),
|
_ if name == "fg" => wgettext!("Send job to foreground"),
|
||||||
|
_ if name == "fi" => wgettext!("Alternative to 'end', to end a block of commands"),
|
||||||
_ if name == "fish_key_reader" => wgettext!("explore what characters keyboard keys send"),
|
_ if name == "fish_key_reader" => wgettext!("explore what characters keyboard keys send"),
|
||||||
_ if name == "for" => wgettext!("Perform a set of commands multiple times"),
|
_ if name == "for" => wgettext!("Perform a set of commands multiple times"),
|
||||||
_ if name == "function" => wgettext!("Define a new function"),
|
_ if name == "function" => wgettext!("Define a new function"),
|
||||||
@@ -531,6 +550,7 @@ pub fn builtin_get_desc(name: &wstr) -> Option<&'static wstr> {
|
|||||||
_ if name == "realpath" => wgettext!("Show absolute path sans symlinks"),
|
_ if name == "realpath" => wgettext!("Show absolute path sans symlinks"),
|
||||||
_ if name == "return" => wgettext!("Stop the currently evaluated function"),
|
_ if name == "return" => wgettext!("Stop the currently evaluated function"),
|
||||||
_ if name == "set" => wgettext!("Handle environment variables"),
|
_ if name == "set" => wgettext!("Handle environment variables"),
|
||||||
|
_ if name == "then" => wgettext!("Optional after the condition of an if-statement"),
|
||||||
_ if name == "set_color" => wgettext!("Set the terminal color"),
|
_ if name == "set_color" => wgettext!("Set the terminal color"),
|
||||||
_ if name == "source" => wgettext!("Evaluate contents of file"),
|
_ if name == "source" => wgettext!("Evaluate contents of file"),
|
||||||
_ if name == "status" => wgettext!("Return status information about fish"),
|
_ if name == "status" => wgettext!("Return status information about fish"),
|
||||||
|
|||||||
@@ -846,14 +846,18 @@ fn visit_keyword(&mut self, node: &dyn Keyword) {
|
|||||||
| ParseKeyword::kw_builtin
|
| ParseKeyword::kw_builtin
|
||||||
| ParseKeyword::kw_case
|
| ParseKeyword::kw_case
|
||||||
| ParseKeyword::kw_command
|
| ParseKeyword::kw_command
|
||||||
|
| ParseKeyword::kw_do
|
||||||
|
| ParseKeyword::kw_done
|
||||||
| ParseKeyword::kw_else
|
| ParseKeyword::kw_else
|
||||||
| ParseKeyword::kw_end
|
| ParseKeyword::kw_end
|
||||||
| ParseKeyword::kw_exec
|
| ParseKeyword::kw_exec
|
||||||
|
| ParseKeyword::kw_fi
|
||||||
| ParseKeyword::kw_for
|
| ParseKeyword::kw_for
|
||||||
| ParseKeyword::kw_function
|
| ParseKeyword::kw_function
|
||||||
| ParseKeyword::kw_if
|
| ParseKeyword::kw_if
|
||||||
| ParseKeyword::kw_in
|
| ParseKeyword::kw_in
|
||||||
| ParseKeyword::kw_switch
|
| ParseKeyword::kw_switch
|
||||||
|
| ParseKeyword::kw_then
|
||||||
| ParseKeyword::kw_while => role = HighlightRole::keyword,
|
| ParseKeyword::kw_while => role = HighlightRole::keyword,
|
||||||
ParseKeyword::kw_and
|
ParseKeyword::kw_and
|
||||||
| ParseKeyword::kw_or
|
| ParseKeyword::kw_or
|
||||||
|
|||||||
@@ -92,10 +92,13 @@ pub enum ParseKeyword {
|
|||||||
kw_builtin,
|
kw_builtin,
|
||||||
kw_case,
|
kw_case,
|
||||||
kw_command,
|
kw_command,
|
||||||
|
kw_do,
|
||||||
|
kw_done,
|
||||||
kw_else,
|
kw_else,
|
||||||
kw_end,
|
kw_end,
|
||||||
kw_exclam,
|
kw_exclam,
|
||||||
kw_exec,
|
kw_exec,
|
||||||
|
kw_fi,
|
||||||
kw_for,
|
kw_for,
|
||||||
kw_function,
|
kw_function,
|
||||||
kw_if,
|
kw_if,
|
||||||
@@ -103,6 +106,7 @@ pub enum ParseKeyword {
|
|||||||
kw_not,
|
kw_not,
|
||||||
kw_or,
|
kw_or,
|
||||||
kw_switch,
|
kw_switch,
|
||||||
|
kw_then,
|
||||||
kw_time,
|
kw_time,
|
||||||
kw_while,
|
kw_while,
|
||||||
}
|
}
|
||||||
@@ -237,10 +241,13 @@ pub fn to_wstr(self) -> &'static wstr {
|
|||||||
ParseKeyword::kw_builtin => L!("builtin"),
|
ParseKeyword::kw_builtin => L!("builtin"),
|
||||||
ParseKeyword::kw_case => L!("case"),
|
ParseKeyword::kw_case => L!("case"),
|
||||||
ParseKeyword::kw_command => L!("command"),
|
ParseKeyword::kw_command => L!("command"),
|
||||||
|
ParseKeyword::kw_do => L!("do"),
|
||||||
|
ParseKeyword::kw_done => L!("done"),
|
||||||
ParseKeyword::kw_else => L!("else"),
|
ParseKeyword::kw_else => L!("else"),
|
||||||
ParseKeyword::kw_end => L!("end"),
|
ParseKeyword::kw_end => L!("end"),
|
||||||
ParseKeyword::kw_exclam => L!("!"),
|
ParseKeyword::kw_exclam => L!("!"),
|
||||||
ParseKeyword::kw_exec => L!("exec"),
|
ParseKeyword::kw_exec => L!("exec"),
|
||||||
|
ParseKeyword::kw_fi => L!("fi"),
|
||||||
ParseKeyword::kw_for => L!("for"),
|
ParseKeyword::kw_for => L!("for"),
|
||||||
ParseKeyword::kw_function => L!("function"),
|
ParseKeyword::kw_function => L!("function"),
|
||||||
ParseKeyword::kw_if => L!("if"),
|
ParseKeyword::kw_if => L!("if"),
|
||||||
@@ -248,6 +255,7 @@ pub fn to_wstr(self) -> &'static wstr {
|
|||||||
ParseKeyword::kw_not => L!("not"),
|
ParseKeyword::kw_not => L!("not"),
|
||||||
ParseKeyword::kw_or => L!("or"),
|
ParseKeyword::kw_or => L!("or"),
|
||||||
ParseKeyword::kw_switch => L!("switch"),
|
ParseKeyword::kw_switch => L!("switch"),
|
||||||
|
ParseKeyword::kw_then => L!("then"),
|
||||||
ParseKeyword::kw_time => L!("time"),
|
ParseKeyword::kw_time => L!("time"),
|
||||||
ParseKeyword::kw_while => L!("while"),
|
ParseKeyword::kw_while => L!("while"),
|
||||||
_ => L!("unknown_keyword"),
|
_ => L!("unknown_keyword"),
|
||||||
@@ -270,10 +278,13 @@ fn from(s: &wstr) -> Self {
|
|||||||
_ if s == "builtin" => ParseKeyword::kw_builtin,
|
_ if s == "builtin" => ParseKeyword::kw_builtin,
|
||||||
_ if s == "case" => ParseKeyword::kw_case,
|
_ if s == "case" => ParseKeyword::kw_case,
|
||||||
_ if s == "command" => ParseKeyword::kw_command,
|
_ if s == "command" => ParseKeyword::kw_command,
|
||||||
|
_ if s == "do" => ParseKeyword::kw_do,
|
||||||
|
_ if s == "done" => ParseKeyword::kw_done,
|
||||||
_ if s == "else" => ParseKeyword::kw_else,
|
_ if s == "else" => ParseKeyword::kw_else,
|
||||||
_ if s == "end" => ParseKeyword::kw_end,
|
_ if s == "end" => ParseKeyword::kw_end,
|
||||||
_ if s == "exec" => ParseKeyword::kw_exec,
|
_ if s == "exec" => ParseKeyword::kw_exec,
|
||||||
_ if s == "for" => ParseKeyword::kw_for,
|
_ if s == "for" => ParseKeyword::kw_for,
|
||||||
|
_ if s == "fi" => ParseKeyword::kw_fi,
|
||||||
_ if s == "function" => ParseKeyword::kw_function,
|
_ if s == "function" => ParseKeyword::kw_function,
|
||||||
_ if s == "if" => ParseKeyword::kw_if,
|
_ if s == "if" => ParseKeyword::kw_if,
|
||||||
_ if s == "in" => ParseKeyword::kw_in,
|
_ if s == "in" => ParseKeyword::kw_in,
|
||||||
@@ -281,6 +292,7 @@ fn from(s: &wstr) -> Self {
|
|||||||
_ if s == "or" => ParseKeyword::kw_or,
|
_ if s == "or" => ParseKeyword::kw_or,
|
||||||
_ if s == "switch" => ParseKeyword::kw_switch,
|
_ if s == "switch" => ParseKeyword::kw_switch,
|
||||||
_ if s == "time" => ParseKeyword::kw_time,
|
_ if s == "time" => ParseKeyword::kw_time,
|
||||||
|
_ if s == "then" => ParseKeyword::kw_then,
|
||||||
_ if s == "while" => ParseKeyword::kw_while,
|
_ if s == "while" => ParseKeyword::kw_while,
|
||||||
_ => ParseKeyword::none,
|
_ => ParseKeyword::none,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
use crate::wcstringutil::count_newlines;
|
use crate::wcstringutil::count_newlines;
|
||||||
|
|
||||||
/// A struct representing the token type that we use internally.
|
/// A struct representing the token type that we use internally.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ParseToken {
|
pub struct ParseToken {
|
||||||
/// The type of the token as represented by the parser
|
/// The type of the token as represented by the parser
|
||||||
pub typ: ParseTokenType,
|
pub typ: ParseTokenType,
|
||||||
|
|||||||
@@ -50,10 +50,13 @@ macro_rules! reserved_words {
|
|||||||
("case"),
|
("case"),
|
||||||
("command", [subcommand]),
|
("command", [subcommand]),
|
||||||
("continue"),
|
("continue"),
|
||||||
|
("do", [subcommand]),
|
||||||
|
("done"),
|
||||||
("else", [subcommand]),
|
("else", [subcommand]),
|
||||||
("end"),
|
("end"),
|
||||||
("eval"),
|
("eval"),
|
||||||
("exec", [subcommand]),
|
("exec", [subcommand]),
|
||||||
|
("fi"),
|
||||||
("for"),
|
("for"),
|
||||||
("function"),
|
("function"),
|
||||||
("if", [subcommand]),
|
("if", [subcommand]),
|
||||||
@@ -65,6 +68,7 @@ macro_rules! reserved_words {
|
|||||||
("status"),
|
("status"),
|
||||||
("string"),
|
("string"),
|
||||||
("switch"),
|
("switch"),
|
||||||
|
("then"),
|
||||||
("test"),
|
("test"),
|
||||||
("time", [subcommand]),
|
("time", [subcommand]),
|
||||||
("while", [subcommand]),
|
("while", [subcommand]),
|
||||||
|
|||||||
@@ -614,3 +614,12 @@ echo 'echo "foo" "bar"' > $tmpdir/indent_test.fish
|
|||||||
$fish_indent --write $tmpdir/indent_test.fish
|
$fish_indent --write $tmpdir/indent_test.fish
|
||||||
cat $tmpdir/indent_test.fish
|
cat $tmpdir/indent_test.fish
|
||||||
# CHECK: echo foo bar
|
# CHECK: echo foo bar
|
||||||
|
|
||||||
|
# TODO: We should not force the line break.
|
||||||
|
echo 'if true; then
|
||||||
|
echo body
|
||||||
|
fi' | $fish_indent
|
||||||
|
# CHECK: if true
|
||||||
|
# CHECK: then
|
||||||
|
# CHECK: {{ }}echo body
|
||||||
|
# CHECK: fi
|
||||||
|
|||||||
Reference in New Issue
Block a user