Skip to content

Commit

Permalink
Reorder simple command words in display
Browse files Browse the repository at this point in the history
  • Loading branch information
magicant committed May 6, 2024
1 parent 47cb94a commit 50c2c38
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 2 deletions.
3 changes: 3 additions & 0 deletions yash-syntax/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
is not followed by any name characters. Previously, it returned
`Err(SyntaxError::UnclosedParam{…})` if `{` was followed by a non-name
character other than `}`.
- `impl std::fmt::Display for SimpleCommand` now prints the command words
after the redirections if there are no assignments and the first word looks
like a keyword.

### Fixed

Expand Down
35 changes: 33 additions & 2 deletions yash-syntax/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
//! here-document contents omitted. To pretty-format an AST in multiple lines
//! with here-document contents included, you can use ... TODO TBD.

use crate::parser::lex::Keyword;
use crate::parser::lex::Operator;
use crate::parser::lex::TryFromOperatorError;
use crate::source::Location;
Expand Down Expand Up @@ -964,15 +965,35 @@ impl SimpleCommand {
pub fn is_one_word(&self) -> bool {
self.assigns.is_empty() && self.words.len() == 1 && self.redirs.is_empty()
}

/// Tests whether the first word of the simple command is a keyword.
#[must_use]
fn first_word_is_keyword(&self) -> bool {
let Some(word) = self.words.first() else {
return false;
};
let Some(literal) = word.to_string_if_literal() else {
return false;
};
literal.parse::<Keyword>().is_ok()
}
}

impl fmt::Display for SimpleCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let i1 = self.assigns.iter().map(|x| x as &dyn fmt::Display);
let i2 = self.words.iter().map(|x| x as &dyn fmt::Display);
let i3 = self.redirs.iter().map(|x| x as &dyn fmt::Display);
write!(f, "{}", i1.chain(i2).chain(i3).format(" "))
// TODO Avoid printing a keyword as the first word

if !self.assigns.is_empty() || !self.first_word_is_keyword() {
write!(f, "{}", i1.chain(i2).chain(i3).format(" "))
} else {
// If the simple command starts with an assignment or redirection,
// the first word may be a keyword which is treated as a plain word.
// In this case, we need to avoid the word being interpreted as a
// keyword by printing the assignment or redirection first.
write!(f, "{}", i3.chain(i2).format(" "))
}
}
}

Expand Down Expand Up @@ -1925,6 +1946,16 @@ mod tests {
assert_eq!(command.to_string(), "foo=bar <<END 1<<-here");
}

#[test]
fn simple_command_display_with_keyword() {
let command = SimpleCommand {
assigns: vec![],
words: vec!["if".parse().unwrap()],
redirs: vec!["<foo".parse().unwrap()].into(),
};
assert_eq!(command.to_string(), "<foo if");
}

#[test]
fn elif_then_display() {
let condition: List = "c 1& c 2".parse().unwrap();
Expand Down

0 comments on commit 50c2c38

Please sign in to comment.