From 3bb21ede976a2be51ec2d07d488e8c2d7433cfe6 Mon Sep 17 00:00:00 2001
From: Ajeet D'Souza <98ajeet@gmail.com>
Date: Thu, 29 Jul 2021 09:20:40 +0530
Subject: [PATCH] zoxide-{add,remove} should accept multiple arguments (#243)
---
CHANGELOG.md | 6 +++
Cargo.lock | 70 +++++++++++++++++++++------------
Cargo.toml | 9 +++--
README.md | 20 +++++-----
contrib/completions/_zoxide | 4 +-
contrib/completions/zoxide.bash | 4 +-
man/zoxide-add.1 | 2 +-
man/zoxide-init.1 | 4 +-
man/zoxide-remove.1 | 2 +-
src/app/_app.rs | 8 ++--
src/app/add.rs | 42 ++++++++++++--------
src/app/import.rs | 8 ++--
src/app/query.rs | 8 +++-
src/app/remove.rs | 30 +++++---------
src/db/mod.rs | 20 ++++------
templates/powershell.txt | 4 +-
16 files changed, 134 insertions(+), 107 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 75629b7..2300c4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+
+
# Changelog
All notable changes to this project will be documented in this file.
@@ -7,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+### Added
+
+- `zoxide add` and `zoxide remove` now accept multiple arguments.
+
### Fixed
- Nushell: errors on 0.33.0.
diff --git a/Cargo.lock b/Cargo.lock
index 9b2ee9f..cafafbe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,10 +1,12 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
+version = 3
+
[[package]]
name = "anyhow"
-version = "1.0.41"
+version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
+checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
[[package]]
name = "arrayvec"
@@ -55,9 +57,9 @@ dependencies = [
[[package]]
name = "assert_cmd"
-version = "1.0.5"
+version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a88b6bd5df287567ffdf4ddf4d33060048e1068308e5f62d81c6f9824a045a48"
+checksum = "3d20831bd004dda4c7c372c19cdabff369f794a95e955b3f13fe460e3e1ae95f"
dependencies = [
"bstr",
"doc-comment",
@@ -170,10 +172,10 @@ dependencies = [
]
[[package]]
-name = "difference"
-version = "2.0.0"
+name = "difflib"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
+checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "dirs-next"
@@ -208,6 +210,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
+[[package]]
+name = "either"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+
[[package]]
name = "funty"
version = "1.1.0"
@@ -233,9 +241,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hashbrown"
-version = "0.9.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
@@ -248,23 +256,32 @@ dependencies = [
[[package]]
name = "hermit-abi"
-version = "0.1.18"
+version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
-version = "1.6.2"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
+checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
dependencies = [
"autocfg",
"hashbrown",
]
+[[package]]
+name = "itertools"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -286,9 +303,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.97"
+version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
+checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "memchr"
@@ -320,9 +337,9 @@ dependencies = [
[[package]]
name = "ordered-float"
-version = "2.5.1"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f100fcfb41e5385e0991f74981732049f9b896821542a219420491046baafdc2"
+checksum = "039f02eb0f69271f26abe3202189275d7aa2258b903cb0281b5de710a2570ff3"
dependencies = [
"num-traits",
]
@@ -350,11 +367,12 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
-version = "1.0.8"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
+checksum = "bc3d91237f5de3bcd9d927e24d03b495adb6135097b001cea7403e2d573d00a9"
dependencies = [
- "difference",
+ "difflib",
+ "itertools",
"predicates-core",
]
@@ -400,9 +418,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.27"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
+checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
dependencies = [
"unicode-xid",
]
@@ -576,9 +594,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
-version = "1.0.73"
+version = "1.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
+checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
dependencies = [
"proc-macro2",
"quote",
@@ -637,9 +655,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-segmentation"
-version = "1.7.1"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
diff --git a/Cargo.toml b/Cargo.toml
index 35c6f48..b30a3e2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,18 +11,21 @@ categories = ["command-line-utilities", "filesystem"]
[dependencies]
anyhow = "1.0.32"
-askama = { version="0.10.3", default-features=false }
+askama = { version = "0.10.3", default-features = false }
bincode = "1.3.1"
clap = "3.0.0-beta.2"
dirs-next = "2.0.0"
dunce = "1.0.1"
glob = "0.3.0"
ordered-float = "2.0.0"
-serde = { version="1.0.116", features=["derive"] }
+serde = { version = "1.0.116", features = ["derive"] }
tempfile = "3.1.0"
[target.'cfg(windows)'.dependencies]
-rand = { version="0.8.4", features=["getrandom", "small_rng"], default-features=false }
+rand = { version = "0.8.4", features = [
+ "getrandom",
+ "small_rng",
+], default-features = false }
[dev-dependencies]
assert_cmd = "1.0.1"
diff --git a/README.md b/README.md
index 0732449..44857ac 100644
--- a/README.md
+++ b/README.md
@@ -31,9 +31,9 @@ Read more about the matching algorithm [here][algorithm-matching].
## Getting started
-### Step 1: Install `zoxide`
+### *Step 1: Install `zoxide`*
-`zoxide` supports most major platforms. If your platform isn't listed below,
+`zoxide` runs on most major platforms. If your platform isn't listed below,
please [open an issue][issues].
@@ -131,12 +131,12 @@ To install `zoxide`, use a package manager:
-### Step 2: Install `fzf` (optional)
+### *Step 2: Install `fzf` (optional)*
[`fzf`][fzf] is a command-line fuzzy finder, used by `zoxide` for interactive
-selection ([installation instructions][fzf-installation]).
+selection. It can be installed from [here][fzf-installation].
-### Step 3: Add `zoxide` to your shell
+### *Step 3: Import your data (optional)*
If you currently use any of the following utilities, you may want to import
your data into `zoxide`:
@@ -159,7 +159,9 @@ zoxide import --from z path/to/db
-Now, initialize `zoxide` on your shell:
+### *Step 4: Add `zoxide` to your shell*
+
+To start using `zoxide`, add it to your shell.
bash
@@ -195,7 +197,7 @@ zoxide init fish | source
-nushell 0.32+
+nushell v0.32+
Initialize the `zoxide` script:
@@ -217,7 +219,7 @@ You can replace `__zoxide_prompt` with a custom prompt.
powershell
-Add this to your configuration (the location is stored in `$profile`):
+Add this to your configuration (find it with `echo $profile`):
```powershell
Invoke-Expression (& {
@@ -251,7 +253,7 @@ eval "$(zoxide init zsh)"
-Any POSIX shell
+any POSIX shell
Add this to your configuration:
diff --git a/contrib/completions/_zoxide b/contrib/completions/_zoxide
index 0865087..1548e9b 100644
--- a/contrib/completions/_zoxide
+++ b/contrib/completions/_zoxide
@@ -32,7 +32,7 @@ _zoxide() {
_arguments "${_arguments_options[@]}" \
'-h[Prints help information]' \
'--help[Prints help information]' \
-':path:_files -/' \
+'*::paths:_files -/' \
&& ret=0
;;
(import)
@@ -75,7 +75,7 @@ _arguments "${_arguments_options[@]}" \
'()*--interactive=[]' \
'-h[Prints help information]' \
'--help[Prints help information]' \
-'::path:_files -/' \
+'*::paths:_files -/' \
&& ret=0
;;
esac
diff --git a/contrib/completions/zoxide.bash b/contrib/completions/zoxide.bash
index 2ab89f9..749b1ef 100644
--- a/contrib/completions/zoxide.bash
+++ b/contrib/completions/zoxide.bash
@@ -51,7 +51,7 @@ _zoxide() {
;;
zoxide__add)
- opts=" -h --help "
+ opts=" -h --help ... "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
@@ -127,7 +127,7 @@ _zoxide() {
return 0
;;
zoxide__remove)
- opts=" -i -h --interactive --help "
+ opts=" -i -h --interactive --help ... "
if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
diff --git a/man/zoxide-add.1 b/man/zoxide-add.1
index 707fca8..d82b12a 100644
--- a/man/zoxide-add.1
+++ b/man/zoxide-add.1
@@ -2,7 +2,7 @@
.SH NAME
zoxide-add - add a new directory or increment its rank
.SH SYNOPSIS
-.B zoxide add \fIPATH\fR
+.B zoxide add \fI[PATHS]\fR
.SH DESCRIPTION
If the directory is not already in the database, this command creates a new
entry for it with a default score of \fI1\fR, otherwise, it increments the
diff --git a/man/zoxide-init.1 b/man/zoxide-init.1
index 2286443..3efffbf 100644
--- a/man/zoxide-init.1
+++ b/man/zoxide-init.1
@@ -44,7 +44,7 @@ Add this to your configuration (usually \fI~/.config/nu/config.toml\fR):
You can replace \fB__zoxide_prompt\fR with a custom prompt.
.TP
.B powershell
-Add this to your configuration (the location is stored in \fI$profile\fR):
+Add this to your configuration (find it with \fIecho $profile\fR):
.sp
.nf
\fBInvoke-Expression (& {
@@ -67,7 +67,7 @@ Add this to your configuration (usually \fI~/.zshrc\fR):
\fBeval "$(zoxide init zsh)"\fR
.fi
.TP
-.B Any POSIX shell
+.B any POSIX shell
.sp
Add this to your configuration:
.sp
diff --git a/man/zoxide-remove.1 b/man/zoxide-remove.1
index f0f186e..ee71640 100644
--- a/man/zoxide-remove.1
+++ b/man/zoxide-remove.1
@@ -2,7 +2,7 @@
.SH NAME
zoxide-remove - remove a directory from the database
.SH SYNOPSIS
-.B zoxide remove \fIPATH [OPTIONS]\fR
+.B zoxide remove \fI[PATHS] [OPTIONS]\fR
.SH DESCRIPTION
If you'd like to permanently exclude a directory from the database, see the
\fB_ZO_EXCLUDE_DIRS\fR environment variable in \fBzoxide\fR(1).
diff --git a/src/app/_app.rs b/src/app/_app.rs
index 7362a24..0ce7b2a 100644
--- a/src/app/_app.rs
+++ b/src/app/_app.rs
@@ -33,8 +33,8 @@ pub enum App {
/// Add a new directory or increment its rank
#[derive(Clap, Debug)]
pub struct Add {
- #[clap(value_hint = ValueHint::DirPath)]
- pub path: PathBuf,
+ #[clap(min_values = 1, required = true, value_hint = ValueHint::DirPath)]
+ pub paths: Vec,
}
/// Import entries from another application
@@ -126,12 +126,12 @@ pub struct Query {
#[derive(Clap, Debug)]
pub struct Remove {
// Use interactive selection
- #[clap(conflicts_with = "path", long, short, value_name = "keywords")]
+ #[clap(conflicts_with = "paths", long, short, value_name = "keywords")]
pub interactive: Option>,
#[clap(
conflicts_with = "interactive",
required_unless_present = "interactive",
value_hint = ValueHint::DirPath
)]
- pub path: Option,
+ pub paths: Vec,
}
diff --git a/src/app/add.rs b/src/app/add.rs
index 7dff860..7f73400 100644
--- a/src/app/add.rs
+++ b/src/app/add.rs
@@ -9,33 +9,41 @@ use std::path::Path;
impl Run for Add {
fn run(&self) -> Result<()> {
- let path = if config::resolve_symlinks() {
- util::canonicalize(&self.path)
- } else {
- util::resolve_path(&self.path)
- }?;
- let path = util::path_to_str(&path)?;
- let now = util::current_time()?;
-
// These characters can't be printed cleanly to a single line, so they
// can cause confusion when writing to fzf / stdout.
const EXCLUDE_CHARS: &[char] = &['\n', '\r'];
- let mut exclude_dirs = config::exclude_dirs()?.into_iter();
- if exclude_dirs.any(|pattern| pattern.matches(path)) || path.contains(EXCLUDE_CHARS) {
- return Ok(());
- }
- if !Path::new(path).is_dir() {
- bail!("not a directory: {}", path);
- }
let data_dir = config::data_dir()?;
+ let exclude_dirs = config::exclude_dirs()?;
let max_age = config::maxage()?;
+ let now = util::current_time()?;
let mut db = DatabaseFile::new(data_dir);
let mut db = db.open()?;
- db.add(path, now);
- db.age(max_age);
+ for path in self.paths.iter() {
+ let path = if config::resolve_symlinks() {
+ util::canonicalize(path)
+ } else {
+ util::resolve_path(path)
+ }?;
+ let path = util::path_to_str(&path)?;
+
+ // Ignore path if it contains unsupported characters, or if it's in
+ // the exclude list.
+ if path.contains(EXCLUDE_CHARS) || exclude_dirs.iter().any(|glob| glob.matches(path)) {
+ continue;
+ }
+ if !Path::new(path).is_dir() {
+ bail!("not a directory: {}", path);
+ }
+ db.add(path, now);
+ }
+
+ if db.modified {
+ db.age(max_age);
+ db.save()?;
+ }
Ok(())
}
diff --git a/src/app/import.rs b/src/app/import.rs
index 128c3ec..2b23749 100644
--- a/src/app/import.rs
+++ b/src/app/import.rs
@@ -8,7 +8,7 @@ use std::fs;
impl Run for Import {
fn run(&self) -> Result<()> {
- let buffer = &fs::read_to_string(&self.path).with_context(|| {
+ let buffer = fs::read_to_string(&self.path).with_context(|| {
format!("could not open database for importing: {}", &self.path.display())
})?;
@@ -20,12 +20,12 @@ impl Run for Import {
}
match self.from {
- ImportFrom::Autojump => from_autojump(db, buffer),
- ImportFrom::Z => from_z(db, buffer),
+ ImportFrom::Autojump => from_autojump(db, &buffer),
+ ImportFrom::Z => from_z(db, &buffer),
}
.context("import error")?;
- Ok(())
+ db.save()
}
}
diff --git a/src/app/query.rs b/src/app/query.rs
index 3dfee24..5abd28d 100644
--- a/src/app/query.rs
+++ b/src/app/query.rs
@@ -1,6 +1,6 @@
use crate::app::{Query, Run};
use crate::config;
-use crate::db::DatabaseFile;
+use crate::db::{Database, DatabaseFile};
use crate::error::BrokenPipeHandler;
use crate::fzf::Fzf;
use crate::util;
@@ -14,6 +14,12 @@ impl Run for Query {
let data_dir = config::data_dir()?;
let mut db = DatabaseFile::new(data_dir);
let mut db = db.open()?;
+ self.query(&mut db).and(db.save())
+ }
+}
+
+impl Query {
+ fn query(&self, db: &mut Database) -> Result<()> {
let now = util::current_time()?;
let mut stream = db.stream(now).with_keywords(&self.keywords);
diff --git a/src/app/remove.rs b/src/app/remove.rs
index 99eb48c..8d83264 100644
--- a/src/app/remove.rs
+++ b/src/app/remove.rs
@@ -28,35 +28,25 @@ impl Run for Remove {
selection = fzf.wait_select()?;
let paths = selection.lines().filter_map(|line| line.get(5..));
- let mut not_found = Vec::new();
for path in paths {
- if !db.remove(&path) {
- not_found.push(path);
+ if !db.remove(path) {
+ bail!("path not found in database: {}", path);
}
}
-
- if !not_found.is_empty() {
- let mut err = "path not found in database:".to_string();
- for path in not_found {
- err.push_str("\n ");
- err.push_str(path.as_ref());
- }
- bail!(err);
- }
}
None => {
- // unwrap is safe here because path is required_unless_present = "interactive"
- let path = self.path.as_ref().unwrap();
- if !db.remove(path) {
- let path_abs = util::resolve_path(&path)?;
- let path_abs = util::path_to_str(&path_abs)?;
- if path_abs != path && !db.remove(path) {
- bail!("path not found in database:\n {}", &path)
+ for path in self.paths.iter() {
+ if !db.remove(path) {
+ let path_abs = util::resolve_path(path)?;
+ let path_abs = util::path_to_str(&path_abs)?;
+ if path_abs != path && !db.remove(path_abs) {
+ bail!("path not found in database: {} ({})", path, path_abs)
+ }
}
}
}
}
- Ok(())
+ db.save()
}
}
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 6808f06..3d198a3 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -15,7 +15,7 @@ use std::path::{Path, PathBuf};
pub struct Database<'file> {
pub dirs: DirList<'file>,
pub modified: bool,
- pub data_dir: &'file PathBuf,
+ pub data_dir: &'file Path,
}
impl<'file> Database<'file> {
@@ -25,7 +25,7 @@ impl<'file> Database<'file> {
}
let buffer = self.dirs.to_bytes()?;
- let mut file = NamedTempFile::new_in(&self.data_dir).with_context(|| {
+ let mut file = NamedTempFile::new_in(self.data_dir).with_context(|| {
format!("could not create temporary database in: {}", self.data_dir.display())
})?;
@@ -125,16 +125,6 @@ impl<'file> Database<'file> {
}
}
-impl Drop for Database<'_> {
- fn drop(&mut self) {
- // Since the error can't be properly handled here,
- // pretty-print it instead.
- if let Err(e) = self.save() {
- let _ = writeln!(io::stderr(), "zoxide: {:?}", e);
- }
- }
-}
-
#[cfg(windows)]
fn persist>(mut file: NamedTempFile, path: P) -> Result<(), PersistError> {
use rand::distributions::{Distribution, Uniform};
@@ -168,7 +158,7 @@ fn persist>(mut file: NamedTempFile, path: P) -> Result<(), Persi
#[cfg(unix)]
fn persist>(file: NamedTempFile, path: P) -> Result<(), PersistError> {
- file.persist(&path)?;
+ file.persist(path)?;
Ok(())
}
@@ -231,6 +221,7 @@ mod tests {
let mut db = db.open().unwrap();
db.add(path, now);
db.add(path, now);
+ db.save().unwrap();
}
{
let mut db = DatabaseFile::new(data_dir.path());
@@ -253,17 +244,20 @@ mod tests {
let mut db = DatabaseFile::new(data_dir.path());
let mut db = db.open().unwrap();
db.add(path, now);
+ db.save().unwrap();
}
{
let mut db = DatabaseFile::new(data_dir.path());
let mut db = db.open().unwrap();
assert!(db.remove(path));
+ db.save().unwrap();
}
{
let mut db = DatabaseFile::new(data_dir.path());
let mut db = db.open().unwrap();
assert!(db.dirs.is_empty());
assert!(!db.remove(path));
+ db.save().unwrap();
}
}
}
diff --git a/templates/powershell.txt b/templates/powershell.txt
index ede8619..2d30a40 100644
--- a/templates/powershell.txt
+++ b/templates/powershell.txt
@@ -115,7 +115,7 @@ Set-Alias {{cmd}}i __zoxide_zi
{%- endmatch %}
{{ section }}
-# To initialize zoxide, add this to your configuration (the location is stored
-# in $profile):
+# To initialize zoxide, add this to your configuration (find it with
+# `echo $profile`):
#
# Invoke-Expression (& { $hook = if ($PSVersionTable.PSVersion.Major -ge 6) { 'pwd' } else { 'prompt' } (zoxide init powershell --hook $hook) -join "`n" })