Add windows-shell setting (#1198)

This commit is contained in:
Casey Rodarmor 2022-05-31 13:01:59 -07:00 committed by GitHub
parent ccc54fb8d9
commit c24a194602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 128 additions and 57 deletions

View File

@ -671,9 +671,22 @@ foo:
`just` passes the command to be executed as an argument. Many shells will need an additional flag, often `-c`, to make them evaluate the first argument.
##### Windows Shell
`just` uses `sh` on Windows by default. To use a different shell on Windows, use `windows-shell`:
```make
set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"]
hello:
Write-Host "Hello, world!"
```
##### Windows PowerShell
`just` uses `sh` on Windows by default. To use PowerShell instead, set `windows-powershell` to true.
*`set windows-powershell` uses the legacy `powershell.exe` binary, and is no longer recommended. See the `windows-shell` setting above for a more flexible way to control which shell is used on Windows.*
`just` uses `sh` on Windows by default. To use `powershell.exe` instead, set `windows-powershell` to true.
```make
set windows-powershell := true

View File

@ -57,12 +57,14 @@ impl<'src> Analyzer<'src> {
settings.positional_arguments = positional_arguments;
}
Setting::Shell(shell) => {
assert!(settings.shell.is_none());
settings.shell = Some(shell);
}
Setting::WindowsPowerShell(windows_powershell) => {
settings.windows_powershell = windows_powershell;
}
Setting::WindowsShell(windows_shell) => {
settings.windows_shell = Some(windows_shell);
}
}
}

View File

@ -38,7 +38,7 @@ pub(crate) use ::{
};
// modules
pub(crate) use crate::{completions, config, config_error, keyed, setting};
pub(crate) use crate::{completions, config, config_error, keyed};
// functions
pub(crate) use crate::{load_dotenv::load_dotenv, output::output, unindent::unindent};
@ -64,11 +64,11 @@ pub(crate) use crate::{
parser::Parser, platform::Platform, position::Position, positional::Positional, recipe::Recipe,
recipe_context::RecipeContext, recipe_resolver::RecipeResolver, scope::Scope, search::Search,
search_config::SearchConfig, search_error::SearchError, set::Set, setting::Setting,
settings::Settings, shebang::Shebang, show_whitespace::ShowWhitespace, string_kind::StringKind,
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables,
verbosity::Verbosity, warning::Warning,
settings::Settings, shebang::Shebang, shell::Shell, show_whitespace::ShowWhitespace,
string_kind::StringKind, string_literal::StringLiteral, subcommand::Subcommand,
suggestion::Suggestion, table::Table, thunk::Thunk, token::Token, token_kind::TokenKind,
unresolved_dependency::UnresolvedDependency, unresolved_recipe::UnresolvedRecipe,
use_color::UseColor, variables::Variables, verbosity::Verbosity, warning::Warning,
};
// type aliases

View File

@ -15,6 +15,7 @@ pub(crate) enum Keyword {
Shell,
True,
WindowsPowershell,
WindowsShell,
}
impl Keyword {

View File

@ -106,6 +106,7 @@ mod set;
mod setting;
mod settings;
mod shebang;
mod shell;
mod show_whitespace;
mod string_kind;
mod string_literal;

View File

@ -215,20 +215,19 @@ impl<'src> Node<'src> for Fragment<'src> {
impl<'src> Node<'src> for Set<'src> {
fn tree(&self) -> Tree<'src> {
use Setting::*;
let mut set = Tree::atom(Keyword::Set.lexeme());
set.push_mut(self.name.lexeme().replace('-', "_"));
match &self.value {
AllowDuplicateRecipes(value)
| DotenvLoad(value)
| Export(value)
| PositionalArguments(value)
| WindowsPowerShell(value) => {
Setting::AllowDuplicateRecipes(value)
| Setting::DotenvLoad(value)
| Setting::Export(value)
| Setting::PositionalArguments(value)
| Setting::WindowsPowerShell(value) => {
set.push_mut(value.to_string());
}
Shell(setting::Shell { command, arguments }) => {
Setting::Shell(Shell { command, arguments })
| Setting::WindowsShell(Shell { command, arguments }) => {
set.push_mut(Tree::string(&command.cooked));
for argument in arguments {
set.push_mut(Tree::string(&argument.cooked));

View File

@ -764,26 +764,13 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
self.expect(ColonEquals)?;
if name.lexeme() == Keyword::Shell.lexeme() {
self.expect(BracketL)?;
let command = self.parse_string_literal()?;
let mut arguments = Vec::new();
if self.accepted(Comma)? {
while !self.next_is(BracketR) {
arguments.push(self.parse_string_literal()?);
if !self.accepted(Comma)? {
break;
}
}
}
self.expect(BracketR)?;
Ok(Set {
value: Setting::Shell(setting::Shell { arguments, command }),
value: Setting::Shell(self.parse_shell()?),
name,
})
} else if name.lexeme() == Keyword::WindowsShell.lexeme() {
Ok(Set {
value: Setting::WindowsShell(self.parse_shell()?),
name,
})
} else {
@ -792,6 +779,29 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
}))
}
}
/// Parse a shell setting value
fn parse_shell(&mut self) -> CompileResult<'src, Shell<'src>> {
self.expect(BracketL)?;
let command = self.parse_string_literal()?;
let mut arguments = Vec::new();
if self.accepted(Comma)? {
while !self.next_is(BracketR) {
arguments.push(self.parse_string_literal()?);
if !self.accepted(Comma)? {
break;
}
}
}
self.expect(BracketR)?;
Ok(Shell { arguments, command })
}
}
#[cfg(test)]

View File

@ -8,12 +8,7 @@ pub(crate) enum Setting<'src> {
PositionalArguments(bool),
Shell(Shell<'src>),
WindowsPowerShell(bool),
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub(crate) struct Shell<'src> {
pub(crate) arguments: Vec<StringLiteral<'src>>,
pub(crate) command: StringLiteral<'src>,
WindowsShell(Shell<'src>),
}
impl<'src> Display for Setting<'src> {
@ -24,19 +19,7 @@ impl<'src> Display for Setting<'src> {
| Setting::Export(value)
| Setting::PositionalArguments(value)
| Setting::WindowsPowerShell(value) => write!(f, "{}", value),
Setting::Shell(shell) => write!(f, "{}", shell),
Setting::Shell(shell) | Setting::WindowsShell(shell) => write!(f, "{}", shell),
}
}
}
impl<'src> Display for Shell<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "[{}", self.command)?;
for argument in &self.arguments {
write!(f, ", {}", argument)?;
}
write!(f, "]")
}
}

View File

@ -11,8 +11,9 @@ pub(crate) struct Settings<'src> {
pub(crate) dotenv_load: Option<bool>,
pub(crate) export: bool,
pub(crate) positional_arguments: bool,
pub(crate) shell: Option<setting::Shell<'src>>,
pub(crate) shell: Option<Shell<'src>>,
pub(crate) windows_powershell: bool,
pub(crate) windows_shell: Option<Shell<'src>>,
}
impl<'src> Settings<'src> {
@ -24,6 +25,7 @@ impl<'src> Settings<'src> {
positional_arguments: false,
shell: None,
windows_powershell: false,
windows_shell: None,
}
}
@ -42,6 +44,8 @@ impl<'src> Settings<'src> {
shell.command.cooked.as_ref()
} else if let Some(shell) = &config.shell {
shell
} else if let (true, Some(shell)) = (cfg!(windows), &self.windows_shell) {
shell.command.cooked.as_ref()
} else if cfg!(windows) && self.windows_powershell {
WINDOWS_POWERSHELL_SHELL
} else {
@ -60,6 +64,12 @@ impl<'src> Settings<'src> {
.collect()
} else if let Some(shell_args) = &config.shell_args {
shell_args.iter().map(String::as_ref).collect()
} else if let (true, Some(shell)) = (cfg!(windows), &self.windows_shell) {
shell
.arguments
.iter()
.map(|argument| argument.cooked.as_ref())
.collect()
} else if cfg!(windows) && self.windows_powershell {
WINDOWS_POWERSHELL_ARGS.to_vec()
} else {
@ -70,8 +80,6 @@ impl<'src> Settings<'src> {
#[cfg(test)]
mod tests {
use crate::setting::Shell;
use super::*;
#[test]

19
src/shell.rs Normal file
View File

@ -0,0 +1,19 @@
use crate::common::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub(crate) struct Shell<'src> {
pub(crate) arguments: Vec<StringLiteral<'src>>,
pub(crate) command: StringLiteral<'src>,
}
impl<'src> Display for Shell<'src> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "[{}", self.command)?;
for argument in &self.arguments {
write!(f, ", {}", argument)?;
}
write!(f, "]")
}
}

View File

@ -45,6 +45,7 @@ fn alias() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -73,6 +74,7 @@ fn assignment() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -114,6 +116,7 @@ fn body() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -165,6 +168,7 @@ fn dependencies() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -253,6 +257,7 @@ fn dependency_argument() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -305,6 +310,7 @@ fn duplicate_recipes() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -339,6 +345,7 @@ fn doc_comment() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -361,6 +368,7 @@ fn empty_justfile() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -492,6 +500,7 @@ fn parameters() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -562,6 +571,7 @@ fn priors() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -596,6 +606,7 @@ fn private() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -630,6 +641,7 @@ fn quiet() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -685,6 +697,7 @@ fn settings() {
"command": "a",
},
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -722,6 +735,7 @@ fn shebang() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),
@ -756,6 +770,7 @@ fn simple() {
"positional_arguments": false,
"shell": null,
"windows_powershell": false,
"windows_shell": null,
},
"warnings": [],
}),

View File

@ -45,4 +45,6 @@ mod tempdir;
mod undefined_variables;
#[cfg(target_family = "windows")]
mod windows_powershell;
#[cfg(target_family = "windows")]
mod windows_shell;
mod working_directory;

18
tests/windows_shell.rs Normal file
View File

@ -0,0 +1,18 @@
use crate::common::*;
#[test]
fn windows_shell_setting() {
Test::new()
.justfile(
r#"
set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"]
foo:
Write-Output bar
"#,
)
.shell(false)
.stdout("bar\r\n")
.stderr("Write-Output bar\n")
.run();
}