Remove cd w/ abbreviations (#10588)

# Description

This removes the old style "cd with abbreviations" that would attempt to
guess what directory you wanted to `cd` to. This would sometimes have
false positives, so we left it off by default in the config.

In the current main, we have much-improved path completions
(https://github.com/nushell/nushell/pull/10543) so you can now do `cd
a/b<tab>` and get a much better experience (because you can see the
directory you're about to cd to). This removes the need for the previous
abbreviation system.

# User-Facing Changes

This does remove the old abbreviation system. It will likely mean that
old config files that have settings for abbreviations will now get
errors.

update: here's an example of the error you'll see:


![image](https://github.com/nushell/nushell/assets/547158/6847a25d-895a-4b92-8251-278a57e8d29a)

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
This commit is contained in:
JT 2023-10-03 10:51:46 +13:00 committed by GitHub
parent 783f2a9342
commit 844cb1213b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 18 additions and 634 deletions

7
Cargo.lock generated
View File

@ -2897,7 +2897,6 @@ dependencies = [
"os_pipe",
"pathdiff",
"percent-encoding",
"powierza-coefficient",
"print-positions",
"quick-xml",
"quickcheck",
@ -4020,12 +4019,6 @@ version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794"
[[package]]
name = "powierza-coefficient"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04123079750026568dff0e68efe1ca676f6686023f3bf7686b87dab661c0375b"
[[package]]
name = "ppv-lite86"
version = "0.2.17"

View File

@ -66,7 +66,6 @@ open = "5.0"
os_pipe = "1.1"
pathdiff = "0.2"
percent-encoding = "2.3"
powierza-coefficient = "1.0"
print-positions = "0.6"
quick-xml = "0.30"
rand = "0.8"

View File

@ -1,4 +1,3 @@
use crate::filesystem::cd_query::query;
#[cfg(unix)]
use libc::gid_t;
use nu_engine::{current_dir, CallExt};
@ -62,8 +61,6 @@ impl Command for Cd {
) -> Result<PipelineData, ShellError> {
let path_val: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
let cwd = current_dir(engine_state, stack)?;
let config = engine_state.get_config();
let use_abbrev = config.cd_with_abbreviations;
let path_val = {
if let Some(path) = path_val {
@ -86,22 +83,10 @@ impl Command for Cd {
let path = match nu_path::canonicalize_with(path.clone(), &cwd) {
Ok(p) => p,
Err(_) => {
if use_abbrev {
match query(&path, None, v.span) {
Ok(p) => p,
Err(_) => {
return Err(ShellError::DirectoryNotFound(
v.span,
path.to_string_lossy().to_string(),
))
}
}
} else {
return Err(ShellError::DirectoryNotFound(
v.span,
path.to_string_lossy().to_string(),
));
}
return Err(ShellError::DirectoryNotFound(
v.span,
path.to_string_lossy().to_string(),
));
}
};
(path.to_string_lossy().to_string(), v.span)
@ -115,42 +100,17 @@ impl Command for Cd {
let path = match nu_path::canonicalize_with(path_no_whitespace, &cwd) {
Ok(p) => {
if !p.is_dir() {
if use_abbrev {
// if it's not a dir, let's check to see if it's something abbreviated
match query(&p, None, v.span) {
Ok(path) => path,
Err(_) => {
return Err(ShellError::DirectoryNotFound(
v.span,
p.to_string_lossy().to_string(),
))
}
};
} else {
return Err(ShellError::NotADirectory(v.span));
}
return Err(ShellError::NotADirectory(v.span));
};
p
}
// if canonicalize failed, let's check to see if it's abbreviated
Err(_) => {
if use_abbrev {
match query(&path_no_whitespace, None, v.span) {
Ok(path) => path,
Err(_) => {
return Err(ShellError::DirectoryNotFound(
v.span,
path_no_whitespace.to_string(),
))
}
}
} else {
return Err(ShellError::DirectoryNotFound(
v.span,
path_no_whitespace.to_string(),
));
}
return Err(ShellError::DirectoryNotFound(
v.span,
path_no_whitespace.to_string(),
));
}
};
(path.to_string_lossy().to_string(), v.span)
@ -188,11 +148,6 @@ impl Command for Cd {
example: r#"cd ~"#,
result: None,
},
Example {
description: "Change to a directory via abbreviations",
example: r#"cd d/s/9"#,
result: None,
},
Example {
description: "Change to the previous working directory ($OLDPWD)",
example: r#"cd -"#,

View File

@ -1,549 +0,0 @@
// Attribution:
// Thanks kn team https://github.com/micouy/kn
use alphanumeric_sort::compare_os_str;
use nu_protocol::ShellError;
use nu_protocol::Span;
use powierza_coefficient::powierża_coefficient;
use std::cmp::{Ord, Ordering};
use std::{
convert::AsRef,
ffi::{OsStr, OsString},
fs::DirEntry,
mem,
path::{Component, Path, PathBuf},
};
/// A path matching an abbreviation.
///
/// Stores [`Congruence`](Congruence)'s of its ancestors, with that of the
/// closest ancestors first (so that it can be compared
/// [lexicographically](std::cmp::Ord#lexicographical-comparison).
struct Finding {
file_name: OsString,
path: PathBuf,
congruence: Vec<Congruence>,
}
/// Returns an iterator over directory's children matching the abbreviation.
fn get_matching_children<'a, P>(
path: &'a P,
abbr: &'a Abbr,
parent_congruence: &'a [Congruence],
) -> impl Iterator<Item = Finding> + 'a
where
P: AsRef<Path>,
{
let filter_map_entry = move |entry: DirEntry| {
let file_type = entry.file_type().ok()?;
if file_type.is_dir() || file_type.is_symlink() {
let file_name: String = entry.file_name().into_string().ok()?;
if let Some(congruence) = abbr.compare(&file_name) {
let mut entry_congruence = parent_congruence.to_vec();
entry_congruence.insert(0, congruence);
return Some(Finding {
file_name: entry.file_name(),
congruence: entry_congruence,
path: entry.path(),
});
}
}
None
};
path.as_ref()
.read_dir()
.ok()
.map(|reader| {
reader
.filter_map(|entry| entry.ok())
.filter_map(filter_map_entry)
})
.into_iter()
.flatten()
}
/// The `query` subcommand.
///
/// It takes two args — `--abbr` and `--exclude` (optionally). The value of
/// `--abbr` gets split into a prefix containing components like `c:/`, `/`,
/// `~/`, and dots, and [`Abbr`](Abbr)'s. If there is more than one dir matching
/// the query, the value of `--exclude` is excluded from the search.
pub fn query<P>(arg: &P, excluded: Option<PathBuf>, span: Span) -> Result<PathBuf, ShellError>
where
P: AsRef<Path>,
{
// If the arg is a real path and not an abbreviation, return it. It
// prevents potential unexpected behavior due to abbreviation expansion.
// For example, `kn` doesn't allow for any component other than `Normal` in
// the abbreviation but the arg itself may be a valid path. `kn` should only
// behave differently from `cd` in situations where `cd` would fail.
if arg.as_ref().is_dir() {
return Ok(arg.as_ref().into());
}
let (prefix, abbrs) = parse_arg(&arg)?;
let start_dir = match prefix {
Some(start_dir) => start_dir,
None => std::env::current_dir()?,
};
match abbrs.as_slice() {
[] => Ok(start_dir),
[first_abbr, abbrs @ ..] => {
let mut current_level =
get_matching_children(&start_dir, first_abbr, &[]).collect::<Vec<_>>();
let mut next_level = vec![];
for abbr in abbrs {
let children = current_level.iter().flat_map(|parent| {
get_matching_children(&parent.path, abbr, &parent.congruence)
});
next_level.clear();
next_level.extend(children);
mem::swap(&mut next_level, &mut current_level);
}
let cmp_findings = |finding_a: &Finding, finding_b: &Finding| {
finding_a
.congruence
.cmp(&finding_b.congruence)
.then(compare_os_str(&finding_a.file_name, &finding_b.file_name))
};
let found_path = match excluded {
Some(excluded) if current_level.len() > 1 => current_level
.into_iter()
.filter(|finding| finding.path != excluded)
.min_by(cmp_findings)
.map(|Finding { path, .. }| path),
_ => current_level
.into_iter()
.min_by(cmp_findings)
.map(|Finding { path, .. }| path),
};
found_path.ok_or(ShellError::NotADirectory(span))
}
}
}
/// Checks if the component contains only dots and returns the equivalent number
/// of [`ParentDir`](Component::ParentDir) components if it does.
///
/// It is the number of dots, less one. For example, `...` is converted to
/// `../..`, `....` to `../../..` etc.
fn parse_dots(component: &str) -> Option<usize> {
component
.chars()
.try_fold(
0,
|n_dots, c| if c == '.' { Some(n_dots + 1) } else { None },
)
.and_then(|n_dots| if n_dots > 1 { Some(n_dots - 1) } else { None })
}
/// Extracts leading components of the path that are not parts of the
/// abbreviation.
///
/// The prefix is the path where the search starts. If there is no prefix (when
/// the path consists only of normal components), the search starts in the
/// current directory, just as you'd expect. The function collects each
/// [`Prefix`](Component::Prefix), [`RootDir`](Component::RootDir),
/// [`CurDir`](Component::CurDir), and [`ParentDir`](Component::ParentDir)
/// components and stops at the first [`Normal`](Component::Normal) component
/// **unless** it only contains dots. In this case, it converts it to as many
/// [`ParentDir`](Component::ParentDir)'s as there are dots in this component,
/// less one. For example, `...` is converted to `../..`, `....` to `../../..`
/// etc.
fn extract_prefix<'a, P>(
arg: &'a P,
) -> Result<(Option<PathBuf>, impl Iterator<Item = Component<'a>> + 'a), ShellError>
where
P: AsRef<Path> + ?Sized + 'a,
{
use Component::*;
let mut components = arg.as_ref().components().peekable();
let mut prefix: Option<PathBuf> = None;
let mut push_to_prefix = |component: Component| match &mut prefix {
None => prefix = Some(PathBuf::from(&component)),
Some(prefix) => prefix.push(component),
};
let parse_dots_os = |component_os: &OsStr| {
component_os
.to_os_string()
.into_string()
.map_err(|_| ShellError::NonUnicodeInput)
.map(|component| parse_dots(&component))
};
while let Some(component) = components.peek() {
match component {
Prefix(_) | RootDir | CurDir | ParentDir => push_to_prefix(*component),
Normal(component_os) => {
if let Some(n_dots) = parse_dots_os(component_os)? {
(0..n_dots).for_each(|_| push_to_prefix(ParentDir));
} else {
break;
}
}
}
let _consumed = components.next();
}
Ok((prefix, components))
}
/// Converts each component into [`Abbr`](Abbr) without checking
/// the component's type.
///
/// This may change in the future.
fn parse_abbrs<'a, I>(components: I) -> Result<Vec<Abbr>, ShellError>
where
I: Iterator<Item = Component<'a>> + 'a,
{
use Component::*;
let abbrs = components
.into_iter()
.map(|component| match component {
Prefix(_) | RootDir | CurDir | ParentDir => {
let component_string = component
.as_os_str()
.to_os_string()
.to_string_lossy()
.to_string();
Err(ShellError::UnexpectedAbbrComponent(component_string))
}
Normal(component_os) => component_os
.to_os_string()
.into_string()
.map_err(|_| ShellError::NonUnicodeInput)
.map(|string| Abbr::new_sanitized(&string)),
})
.collect::<Result<Vec<_>, _>>()?;
Ok(abbrs)
}
/// Parses the provided argument into a prefix and [`Abbr`](Abbr)'s.
fn parse_arg<P>(arg: &P) -> Result<(Option<PathBuf>, Vec<Abbr>), ShellError>
where
P: AsRef<Path>,
{
let (prefix, suffix) = extract_prefix(arg)?;
let abbrs = parse_abbrs(suffix)?;
Ok((prefix, abbrs))
}
#[cfg(test)]
mod test {
use super::*;
// // #[cfg(any(test, doc))]
// // #[macro_export]
// // macro_rules! assert_variant {
// // ($expression_in:expr , $( pat )|+ $( if $guard: expr )? $( => $expression_out:expr )? ) => {
// // match $expression_in {
// // $( $pattern )|+ $( if $guard )? => { $( $expression_out )? },
// // variant => panic!("{:?}", variant),
// // }
// // };
// // ($expression_in:expr , $( pat )|+ $( if $guard: expr )? $( => $expression_out:expr)? , $panic:expr) => {
// // match $expression_in {
// // $( $pattern )|+ $( if $guard )? => { $( $expression_out )? },
// // _ => panic!($panic),
// // }
// // };
// // }
// /// Asserts that the expression matches the variant. Optionally returns a value.
// ///
// /// Inspired by [`std::matches`](https://doc.rust-lang.org/stable/std/macro.matches.html).
// ///
// /// # Examples
// ///
// /// ```
// /// # fn main() -> Option<()> {
// /// use kn::Congruence::*;
// ///
// /// let abbr = Abbr::new_sanitized("abcjkl");
// /// let coeff_1 = assert_variant!(abbr.compare("abc_jkl"), Some(Subsequence(coeff)) => coeff);
// /// let coeff_2 = assert_variant!(abbr.compare("ab_cj_kl"), Some(Subsequence(coeff)) => coeff);
// /// assert!(coeff_1 < coeff_2);
// /// # Ok(())
// /// # }
// /// ```
// #[cfg(any(test, doc))]
// #[macro_export]
// macro_rules! assert_variant {
// ($expression_in:expr , $( $pattern:pat )+ $( if $guard: expr )? $( => $expression_out:expr )? ) => {
// match $expression_in {
// $( $pattern )|+ $( if $guard )? => { $( $expression_out )? },
// variant => panic!("{:?}", variant),
// }
// };
// ($expression_in:expr , $( $pattern:pat )+ $( if $guard: expr )? $( => $expression_out:expr)? , $panic:expr) => {
// match $expression_in {
// $( $pattern )|+ $( if $guard )? => { $( $expression_out )? },
// _ => panic!($panic),
// }
// };
// }
// #[test]
// fn test_parse_dots() {
// assert_variant!(parse_dots(""), None);
// assert_variant!(parse_dots("."), None);
// assert_variant!(parse_dots(".."), Some(1));
// assert_variant!(parse_dots("..."), Some(2));
// assert_variant!(parse_dots("...."), Some(3));
// assert_variant!(parse_dots("xyz"), None);
// assert_variant!(parse_dots("...dot"), None);
// }
#[test]
fn test_extract_prefix() {
{
let (prefix, suffix) = extract_prefix("suf/fix").unwrap();
let suffix = suffix.collect::<PathBuf>();
assert_eq!(prefix, None);
assert_eq!(as_path(&suffix), as_path("suf/fix"));
}
{
let (prefix, suffix) = extract_prefix("./.././suf/fix").unwrap();
let suffix = suffix.collect::<PathBuf>();
assert_eq!(prefix.unwrap(), as_path("./.."));
assert_eq!(as_path(&suffix), as_path("suf/fix"));
}
{
let (prefix, suffix) = extract_prefix(".../.../suf/fix").unwrap();
let suffix = suffix.collect::<PathBuf>();
assert_eq!(prefix.unwrap(), as_path("../../../.."));
assert_eq!(as_path(&suffix), as_path("suf/fix"));
}
}
#[test]
fn test_parse_arg_invalid_unicode() {
#[cfg(unix)]
{
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
let source = [0x66, 0x6f, 0x80, 0x6f];
let non_unicode_input = OsStr::from_bytes(&source[..]).to_os_string();
let result = parse_arg(&non_unicode_input);
assert!(result.is_err());
}
#[cfg(windows)]
{
use std::os::windows::prelude::*;
let source = [0x0066, 0x006f, 0xd800, 0x006f];
let os_string = OsString::from_wide(&source[..]);
let result = parse_arg(&os_string);
assert!(result.is_err());
}
}
#[test]
fn test_congruence_ordering() {
assert!(Complete < Prefix);
assert!(Complete < Subsequence(1));
assert!(Prefix < Subsequence(1));
assert!(Subsequence(1) < Subsequence(1000));
}
// #[test]
// fn test_compare_abbr() {
// let abbr = Abbr::new_sanitized("abcjkl");
// assert_variant!(abbr.compare("abcjkl"), Some(Complete));
// assert_variant!(abbr.compare("abcjkl_"), Some(Prefix));
// assert_variant!(abbr.compare("_abcjkl"), Some(Subsequence(0)));
// assert_variant!(abbr.compare("abc_jkl"), Some(Subsequence(1)));
// assert_variant!(abbr.compare("xyz"), None);
// assert_variant!(abbr.compare(""), None);
// }
// #[test]
// fn test_compare_abbr_different_cases() {
// let abbr = Abbr::new_sanitized("AbCjKl");
// assert_variant!(abbr.compare("aBcJkL"), Some(Complete));
// assert_variant!(abbr.compare("AbcJkl_"), Some(Prefix));
// assert_variant!(abbr.compare("_aBcjKl"), Some(Subsequence(0)));
// assert_variant!(abbr.compare("abC_jkL"), Some(Subsequence(1)));
// }
// #[test]
// fn test_empty_abbr_empty_component() {
// let empty = "";
// let abbr = Abbr::new_sanitized(empty);
// assert_variant!(abbr.compare("non empty component"), None);
// let abbr = Abbr::new_sanitized("non empty abbr");
// assert_variant!(abbr.compare(empty), None);
// }
#[test]
fn test_order_paths() {
fn sort<'a>(paths: &'a [&'a str], abbr: &str) -> Vec<&'a str> {
let abbr = Abbr::new_sanitized(abbr);
let mut paths = paths.to_owned();
paths.sort_by_key(|path| abbr.compare(path).unwrap());
paths
}
let paths = vec!["playground", "plotka"];
assert_eq!(paths, sort(&paths, "pla"));
let paths = vec!["veccentric", "vehiccles"];
assert_eq!(paths, sort(&paths, "vecc"));
}
}
/// Shorthand for `AsRef<Path>::as_ref(&x)`.
#[cfg(any(test, doc))]
pub fn as_path<P>(path: &P) -> &Path
where
P: AsRef<Path> + ?Sized,
{
path.as_ref()
}
/// A component of the user's query.
///
/// It is used in comparing and ordering of found paths. Read more in
/// [`Congruence`'s docs](Congruence).
#[derive(Debug, Clone)]
pub enum Abbr {
/// Wildcard matches every component with congruence
/// [`Complete`](Congruence::Complete).
Wildcard,
/// Literal abbreviation.
Literal(String),
}
impl Abbr {
/// Constructs [`Abbr::Wildcard`](Abbr::Wildcard) if the
/// string slice is '-', otherwise constructs
/// wrapped [`Abbr::Literal`](Abbr::Literal) with the abbreviation
/// mapped to its ASCII lowercase equivalent.
pub fn new_sanitized(abbr: &str) -> Self {
if abbr == "-" {
Self::Wildcard
} else {
Self::Literal(abbr.to_ascii_lowercase())
}
}
/// Compares a component against the abbreviation.
pub fn compare(&self, component: &str) -> Option<Congruence> {
// What about characters with accents? [https://eev.ee/blog/2015/09/12/dark-corners-of-unicode/]
let component = component.to_ascii_lowercase();
match self {
Self::Wildcard => Some(Congruence::Complete),
Self::Literal(literal) => {
if literal.is_empty() || component.is_empty() {
None
} else if *literal == component {
Some(Congruence::Complete)
} else if component.starts_with(literal) {
Some(Congruence::Prefix)
} else {
powierża_coefficient(literal, &component).map(Congruence::Subsequence)
}
}
}
}
}
/// The strength of the match between an abbreviation and a component.
///
/// [`Congruence`](Congruence) is used to order path components in the following
/// way:
///
/// 1. Components are first ordered based on how well they match the
/// abbreviation — first [`Complete`](Congruence::Complete), then
/// [`Prefix`](Congruence::Prefix), then
/// [`Subsequence`](Congruence::Subsequence).
/// 2. Components with congruence [`Subsequence`](Congruence::Subsequence) are
/// ordered by their [Powierża coefficient](https://github.com/micouy/powierza-coefficient).
/// 3. If the order of two components cannot be determined based on the above, [`alphanumeric_sort`](https://docs.rs/alphanumeric-sort) is used.
///
/// Below are the results of matching components against abbreviation `abc`:
///
/// | Component | Match strength |
/// |-------------|------------------------------------------|
/// | `abc` | [`Complete`](Congruence::Complete) |
/// | `abc___` | [`Prefix`](Congruence::Prefix) |
/// | `_a_b_c_` | [`Subsequence`](Congruence::Subsequence) |
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Congruence {
/// Either the abbreviation and the component are the same or the
/// abbreviation is a wildcard.
Complete,
/// The abbreviation is a prefix of the component.
Prefix,
/// The abbreviation's characters form a subsequence of the component's
/// characters. The field contains the Powierża coefficient of the pair of
/// strings.
Subsequence(u32),
}
use Congruence::*;
impl PartialOrd for Congruence {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}
impl Ord for Congruence {
fn cmp(&self, other: &Self) -> Ordering {
use Ordering::*;
match (self, other) {
(Complete, Complete) => Equal,
(Complete, Prefix) => Less,
(Complete, Subsequence(_)) => Less,
(Prefix, Complete) => Greater,
(Prefix, Prefix) => Equal,
(Prefix, Subsequence(_)) => Less,
(Subsequence(_), Complete) => Greater,
(Subsequence(_), Prefix) => Greater,
(Subsequence(dist_a), Subsequence(dist_b)) => dist_a.cmp(dist_b),
}
}
}

View File

@ -1,5 +1,4 @@
mod cd;
mod cd_query;
mod cp;
mod glob;
mod ls;
@ -16,7 +15,6 @@ mod watch;
pub use self::open::Open;
pub use cd::Cd;
pub use cd_query::query;
pub use cp::Cp;
pub use glob::Glob;
pub use ls::Ls;

View File

@ -57,7 +57,7 @@ fn test_no_color_flag() {
"#);
assert_eq!(
actual.out,
r"<html><style>body { background-color:white;color:black; }</style><body>Change directory.<br><br>Usage:<br> &gt; cd (path) <br><br>Flags:<br> -h, --help - Display the help message for this command<br><br>Signatures:<br> &lt;nothing&gt; | cd &lt;string?&gt; -&gt; &lt;nothing&gt;<br> &lt;string&gt; | cd &lt;string?&gt; -&gt; &lt;nothing&gt;<br><br>Parameters:<br> path &lt;directory&gt;: the path to change to (optional)<br><br>Examples:<br> Change to your home directory<br> &gt; cd ~<br><br> Change to a directory via abbreviations<br> &gt; cd d/s/9<br><br> Change to the previous working directory ($OLDPWD)<br> &gt; cd -<br><br></body></html>"
r"<html><style>body { background-color:white;color:black; }</style><body>Change directory.<br><br>Usage:<br> &gt; cd (path) <br><br>Flags:<br> -h, --help - Display the help message for this command<br><br>Signatures:<br> &lt;nothing&gt; | cd &lt;string?&gt; -&gt; &lt;nothing&gt;<br> &lt;string&gt; | cd &lt;string?&gt; -&gt; &lt;nothing&gt;<br><br>Parameters:<br> path &lt;directory&gt;: the path to change to (optional)<br><br>Examples:<br> Change to your home directory<br> &gt; cd ~<br><br> Change to the previous working directory ($OLDPWD)<br> &gt; cd -<br><br></body></html>"
)
}

View File

@ -100,7 +100,6 @@ pub struct Config {
pub shell_integration: bool,
pub buffer_editor: Value,
pub table_index_mode: TableIndexMode,
pub cd_with_abbreviations: bool,
pub case_sensitive_completions: bool,
pub enable_external_completion: bool,
pub trim_strategy: TrimStrategy,
@ -128,8 +127,6 @@ impl Default for Config {
rm_always_trash: false,
cd_with_abbreviations: false,
table_mode: "rounded".into(),
table_index_mode: TableIndexMode::Always,
table_show_empty: true,
@ -387,19 +384,14 @@ impl Value {
for index in (0..cols.len()).rev() {
let value = &vals[index];
let key2 = cols[index].as_str();
match key2 {
"abbreviations" => {
try_bool!(cols, vals, index, span, cd_with_abbreviations)
}
x => {
invalid_key!(
cols,
vals,
index,
Some(value.span()),
"$env.config.{key}.{x} is an unknown config setting"
);
}
{
invalid_key!(
cols,
vals,
index,
Some(value.span()),
"$env.config.{key}.{key2} is an unknown config setting"
);
}
}
} else {

View File

@ -150,10 +150,6 @@ $env.config = {
always_trash: false # always act as if -t was given. Can be overridden with -p
}
cd: {
abbreviations: false # allows `cd s/o/f` to expand to `cd some/other/folder`
}
table: {
mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other
index_mode: always # "always" show indexes, "never" show indexes, "auto" = show indexes when a table has "index" column