Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ echo 'Invoke-Expression "$(vfox activate pwsh)"' >> $PROFILE
# 3. copy internal/shell/clink_vfox.lua to script path

# For Nushell:
vfox activate nushell $nu.default-config-dir | save --append $nu.config-path
vfox activate nu $nu.default-config-dir | save --append $nu.config-path
```

> Remember to restart your shell to apply the changes.
Expand Down
2 changes: 1 addition & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ if (-not (Test-Path -Path $PROFILE)) { New-Item -Type File -Path $PROFILE -Force
# 3. 复制 internal/shell/clink_vfox.lua 到脚本路径

# 对于 Nushell:
vfox activate nushell $nu.default-config-dir | save --append $nu.config-path
vfox activate nu $nu.default-config-dir | save --append $nu.config-path
```

> 请记住重启你的 Shell 以应用更改。
Expand Down
49 changes: 49 additions & 0 deletions cmd/commands/activate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,52 @@ func TestRenderActivateScriptPassesNushellConfigPath(t *testing.T) {
t.Fatalf("generated vfox.nu does not reference self path:\n%s", string(content))
}
}

func TestRenderActivateScriptPassesNuConfigPath(t *testing.T) {
configDir := t.TempDir()

got, err := renderActivateScript("nu", "/usr/bin/vfox", []string{configDir}, env.Vars{}, false)
if err != nil {
t.Fatalf("renderActivateScript() failed: %v", err)
}

if !strings.Contains(got, `source ($nu.default-config-dir | path join "vfox.nu")`) {
t.Fatalf("renderActivateScript() returned unexpected Nushell source script:\n%s", got)
}

vfoxNu := filepath.Join(configDir, "vfox.nu")
content, err := os.ReadFile(vfoxNu)
if err != nil {
t.Fatalf("expected generated %s: %v", vfoxNu, err)
}
if !strings.Contains(string(content), "^'/usr/bin/vfox' activate nushell $nu.default-config-dir") {
t.Fatalf("generated vfox.nu does not reference self path:\n%s", string(content))
}
}

func TestRenderActivateScriptNushellDirectOutput(t *testing.T) {
for _, shellName := range []string{"nu", "nushell"} {
t.Run(shellName, func(t *testing.T) {
got, err := renderActivateScript(shellName, "/usr/bin/vfox", nil, env.Vars{}, false)
if err != nil {
t.Fatalf("renderActivateScript(%q) failed: %v", shellName, err)
}

if strings.Contains(got, "source ($nu.default-config-dir") {
t.Fatalf("direct activation script should not contain source line:\n%s", got)
}
if !strings.Contains(got, "export-env") {
t.Fatalf("direct activation script missing export-env:\n%s", got)
}
if !strings.Contains(got, "def --env updateVfoxEnvironment") {
t.Fatalf("direct activation script missing updateVfoxEnvironment:\n%s", got)
}
if !strings.Contains(got, "/usr/bin/vfox") {
t.Fatalf("direct activation script missing self path:\n%s", got)
}
if !strings.Contains(got, "$env.__VFOX_SHELL = 'nushell'") {
t.Fatalf("direct activation script missing __VFOX_SHELL:\n%s", got)
}
})
}
}
25 changes: 24 additions & 1 deletion docs/guides/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,33 @@ Type `Y` and press Enter to confirm.
</TabItem>
<TabItem label="Nushell">

> Requires Nushell 0.106.0+.

Add the hook to your Nushell configuration:

```shell
vfox activate nushell $nu.default-config-dir | save --append $nu.config-path
vfox activate nu $nu.default-config-dir | save --append $nu.config-path
```

Alternatively, you can directly generate and append the activation script without specifying a config directory:

```shell
vfox activate nu | save --append $nu.config-path
```

Or save the activation script to Nushell's vendor autoload directory:

```shell
mkdir ($nu.data-dir | path join "vendor/autoload")
vfox activate nu | save -f ($nu.data-dir | path join "vendor/autoload/vfox.nu")
```

::: tip 💡 Tip
The direct (`vfox activate nu | save --append ...`) and vendor autoload setups save a static snapshot of the activation script. Unlike the config-directory form, they do not automatically update when vfox is upgraded. When upgrading vfox:
- For vendor autoload configurations, re-running the command safely overwrites the file in place (`save -f`).
- For direct `$nu.config-path` configurations, remove the previous vfox hook block from `$nu.config-path` before re-appending to avoid duplicate hooks.
:::

</TabItem>
</Tabs>

Expand Down
12 changes: 11 additions & 1 deletion docs/guides/uninstallation.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,17 @@ After saving the file, reload your PowerShell profile:

::: details Nushell

Open your Nushell config file (location shown by `$nu.config-path`) and remove the vfox activation line that was appended during installation.
Depending on the configuration method used:

- **Config directory setup**: Open your Nushell configuration file (shown by `$nu.config-path`) and remove the line sourcing `vfox.nu`. Then delete the generated `vfox.nu` file:
```shell
rm ($nu.default-config-dir | path join "vfox.nu")
```
- **Direct configuration**: Open your Nushell configuration file (shown by `$nu.config-path`) and remove the appended `vfox` activation script block.
- **Vendor autoload**: Delete the activation script from the vendor autoload directory:
```shell
rm ($nu.data-dir | path join "vendor/autoload/vfox.nu")
```

:::

Expand Down
25 changes: 24 additions & 1 deletion docs/zh-hans/guides/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,33 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
</TabItem>
<TabItem label="Nushell">

> 需要 Nushell 0.106.0+。

将 Hook 添加到 Nushell 配置文件:

```shell
vfox activate nu $nu.default-config-dir | save --append $nu.config-path
```

或者直接生成激活脚本并追加到配置中(无需指定配置目录参数):

```shell
vfox activate nushell $nu.default-config-dir | save --append $nu.config-path
vfox activate nu | save --append $nu.config-path
```

也可以将激活脚本保存到 Nushell 的 vendor autoload 目录:

```shell
mkdir ($nu.data-dir | path join "vendor/autoload")
vfox activate nu | save -f ($nu.data-dir | path join "vendor/autoload/vfox.nu")
```

::: tip 💡 提示
直接追加(`vfox activate nu | save --append ...`)以及 vendor autoload 方式生成的是激活脚本的静态快照。与配置目录方式不同,它们在升级 vfox 时不会自动更新。升级 vfox 时:
- 对于 vendor autoload 配置,重新运行该命令即可安全覆盖原文件(`save -f`)。
- 对于直接追加到 `$nu.config-path` 的配置,在重新追加前需先从 `$nu.config-path` 中移除原有的 vfox hook 代码块,以避免重复添加 hook。
:::

</TabItem>
</Tabs>

Expand Down
12 changes: 11 additions & 1 deletion docs/zh-hans/guides/uninstallation.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,17 @@ Invoke-Expression "$(vfox activate pwsh)"

::: details Nushell

打开您的 Nushell 配置文件(位置由 `$nu.config-path` 显示)并删除安装期间添加的 vfox 激活行。
根据您采用的配置方式:

- **配置目录方式**:打开您的 Nushell 配置文件(位置由 `$nu.config-path` 显示)并删除引用 `vfox.nu` 的行。然后删除生成的 `vfox.nu` 文件:
```shell
rm ($nu.default-config-dir | path join "vfox.nu")
```
- **直接写入配置方式**:打开您的 Nushell 配置文件(位置由 `$nu.config-path` 显示)并删除追加的 `vfox` 激活脚本代码块。
- **Vendor autoload 方式**:删除 vendor autoload 目录下的激活脚本:
```shell
rm ($nu.data-dir | path join "vendor/autoload/vfox.nu")
```

:::

Expand Down
45 changes: 29 additions & 16 deletions internal/shell/nushell.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ type nushell struct{}

var Nushell = nushell{}

const nushellConfig = `
# This script is automatically generated by vfox. It takes care of environment setup in Nushell.

# Make sure this script file is up to date when opening a new shell.
^'{{.SelfPath}}' activate nushell $nu.default-config-dir | ignore

const nushellHookBody = `
export-env {
# This function updates environment variables based on the output of the "vfox env" command.
def --env updateVfoxEnvironment [] {
Expand Down Expand Up @@ -66,18 +61,30 @@ export-env {
}
`

const nushellHook = `
# This script is automatically generated by vfox. It takes care of environment setup in Nushell.
` + nushellHookBody

const nushellConfig = `
# This script is automatically generated by vfox. It takes care of environment setup in Nushell.

# Make sure this script file is up to date when opening a new shell.
^'{{.SelfPath}}' activate nushell $nu.default-config-dir | ignore
` + nushellHookBody

// Activate implements shell.Activate by:
//
// 1. Creating a `vfox.nu` script file in Nushell's default configuration directory. This script takes care of vfox
// environment setup in Nushell.
// 2. Returning a short script to be added to the Nushell configuration file, which runs the `vfox.nu` script.
// 1. Returning the Nushell activation script directly when no config path argument is given.
// 2. Creating a 'vfox.nu' script file in Nushell's configuration directory and returning the source script
// when a config directory argument is provided (e.g. $nu.default-config-dir).
func (n nushell) Activate(config ActivateConfig) (string, error) {
if len(config.Args) == 0 {
return "", fmt.Errorf("config path is required")
templateContent := nushellHook
if len(config.Args) > 0 {
templateContent = nushellConfig
}

// Parse and execute the template
tmpl, err := template.New("nushell").Parse(nushellConfig)
tmpl, err := template.New("nushell").Parse(templateContent)
if err != nil {
return "", fmt.Errorf("failed to parse template: %w", err)
}
Expand All @@ -99,13 +106,19 @@ func (n nushell) Activate(config ActivateConfig) (string, error) {
return "", fmt.Errorf("failed to execute template: %w", err)
}

// Replace newlines for platform compatibility
nushellConfig := strings.ReplaceAll(buf.String(), "\n", env.Newline)
renderedConfig := strings.ReplaceAll(buf.String(), "\n", env.Newline)

if len(config.Args) == 0 {
return renderedConfig, nil
}

// write file to config
if err := os.MkdirAll(config.Args[0], 0755); err != nil {
return "", fmt.Errorf("failed to create config directory: %w", err)
}
targetPath := filepath.Join(config.Args[0], "vfox.nu")
if err := os.WriteFile(targetPath, []byte(nushellConfig), 0755); err != nil {
return "", fmt.Errorf("failed to write file: %s", err)
if err := os.WriteFile(targetPath, []byte(renderedConfig), 0755); err != nil {
return "", fmt.Errorf("failed to write file: %w", err)
}

script := "# vfox configuration" + env.Newline +
Expand Down
Loading
Loading