use crate::{file_resolution_check, scoop_version_check};
pub fn init_script() -> &'static str {
concat!(
r#"# scoop shell integration for PowerShell
# Add to your $PROFILE: Invoke-Expression (& scoop init powershell)
# Get the scoop binary path (avoids conflict with wrapper function)
$script:ScoopBin = (Get-Command scoop -CommandType Application -ErrorAction SilentlyContinue).Source
if (-not $script:ScoopBin) {
Write-Warning "scoop binary not found in PATH"
return
}
# Wrapper function for scoop
function scoop {
param([Parameter(ValueFromRemainingArguments=$true)]$Arguments)
$command = if ($Arguments.Count -gt 0) { $Arguments[0] } else { '' }
switch ($command) {
'use' {
& $script:ScoopBin @Arguments
if ($LASTEXITCODE -eq 0) {
$name = $Arguments | Where-Object { $_ -notmatch '^-' } | Select-Object -Skip 1 -First 1
if ($name) {
Invoke-Expression (& $script:ScoopBin activate $name)
}
}
}
{ $_ -in 'activate', 'deactivate', 'shell' } {
if ($Arguments -match '(-h|--help|-V|--version)') {
& $script:ScoopBin @Arguments
} else {
Invoke-Expression (& $script:ScoopBin @Arguments)
}
}
default {
& $script:ScoopBin @Arguments
}
}
}
# Auto-activate hook
function _scoop_hook {
"#,
scoop_version_check!(powershell),
file_resolution_check!(powershell),
r#"
}
# Override prompt to call hook
if (-not $env:SCOOP_NO_AUTO) {
$global:_scoop_original_prompt = $function:prompt
function global:prompt {
_scoop_hook
& $global:_scoop_original_prompt
}
}
# Run hook on startup
_scoop_hook
# Tab completion
Register-ArgumentCompleter -Native -CommandName scoop -ScriptBlock {
param($wordToComplete, $commandAst, $cursorPosition)
$commands = @('list', 'create', 'use', 'remove', 'info', 'install', 'uninstall',
'doctor', 'init', 'completions', 'activate', 'deactivate', 'shell',
'migrate', 'lang')
$tokens = $commandAst.ToString() -split '\s+'
$cmd = if ($tokens.Count -gt 1) { $tokens[1] } else { '' }
# First argument: complete subcommands
if ($tokens.Count -le 2 -and $wordToComplete -notmatch '^-') {
$commands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
return
}
# Environment name completion for specific commands
if ($cmd -in 'use', 'remove', 'info', 'activate', 'shell') {
$envs = & $script:ScoopBin list --bare 2>$null
if ($envs) {
$envs | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
return
}
# Python version completion
if ($cmd -in 'install', 'uninstall', 'create') {
$versions = & $script:ScoopBin list --pythons --bare 2>$null | Sort-Object -Unique
if ($versions) {
$versions | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
}
return
}
# Shell completion for init/completions
if ($cmd -in 'init', 'completions') {
@('bash', 'zsh', 'fish', 'powershell') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
return
}
# Language completion for lang
if ($cmd -eq 'lang') {
@('en', 'ko', 'ja', 'pt-BR') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
return
}
# Migrate subcommand completion
if ($cmd -eq 'migrate') {
@('list', 'all') | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
}
return
}
}
"#
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_init_script_has_valid_powershell_syntax() {
let script = init_script();
let check_command = format!("$null = [scriptblock]::Create(@'\n{}\n'@)", script);
let output = std::process::Command::new("pwsh")
.arg("-NoProfile")
.arg("-Command")
.arg(&check_command)
.output();
match output {
Ok(result) => {
assert!(
result.status.success(),
"PowerShell script has syntax errors:\n{}",
String::from_utf8_lossy(&result.stderr)
);
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
eprintln!("Skipping PowerShell syntax test: pwsh not found");
}
Err(e) => panic!("Failed to run pwsh: {}", e),
}
}
#[test]
fn test_init_script_not_empty() {
let script = init_script();
assert!(!script.is_empty(), "Script should not be empty");
}
#[test]
fn test_init_script_defines_wrapper_function() {
let script = init_script();
assert!(
script.contains("function scoop"),
"Script missing wrapper function"
);
}
#[test]
fn test_init_script_defines_hook_function() {
let script = init_script();
assert!(
script.contains("function _scoop_hook"),
"Script missing auto-activate hook"
);
}
#[test]
fn test_init_script_registers_prompt_hook() {
let script = init_script();
assert!(
script.contains("function global:prompt"),
"Script must override prompt for auto-activation"
);
}
#[test]
fn test_init_script_registers_completion() {
let script = init_script();
assert!(
script.contains("Register-ArgumentCompleter"),
"Script must register PowerShell completion"
);
}
#[test]
fn test_init_script_checks_scoop_no_auto() {
let script = init_script();
assert!(
script.contains("SCOOP_NO_AUTO"),
"Script must check SCOOP_NO_AUTO environment variable"
);
}
#[test]
fn test_init_script_uses_invoke_expression() {
let script = init_script();
assert!(
script.contains("Invoke-Expression"),
"Script must use Invoke-Expression for eval"
);
}
#[test]
fn test_init_script_has_dynamic_completions() {
let script = init_script();
assert!(
script.contains("list --bare"),
"Script must provide dynamic env name completions"
);
assert!(
script.contains("list --pythons --bare"),
"Script must provide dynamic Python version completions"
);
}
#[test]
fn test_init_script_stores_binary_path() {
let script = init_script();
assert!(
script.contains("$script:ScoopBin"),
"Script must store binary path to avoid recursion"
);
assert!(
script.contains("Get-Command scoop -CommandType Application"),
"Script must use Get-Command to find binary"
);
}
#[test]
fn test_init_script_handles_use_command() {
let script = init_script();
assert!(
script.contains("'use'"),
"Script must handle 'use' command specially"
);
}
}