1
Fork 0
mirror of https://github.com/RGBCube/nu_scripts synced 2025-08-01 06:37:46 +00:00

Improved CDPATH (#644)

This updated `c` command supports:
* Changing to the previous directory with `c -`
* Changing to absolute directories
* Completion of CDPATH entries
* Completion of absolute paths
* Completion of children

When completing CDPATH entries the parent directory is shown to provide
context in case multiple CDPATH entries have the same child directory
names.

Fixes #244
This commit is contained in:
Eric Hodel 2023-10-19 04:35:32 -07:00 committed by GitHub
parent c39a41ad2d
commit c93ce14fc9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,36 +1,169 @@
def-env c [dir = ""] {
let default = if $nu.os-info.name == "windows" {
# You must set $env.CDPATH, try:
#
# $env.CDPATH = [
# ".",
# "~",
# "~/path/to/repositories",
# ]
#
# The above $env.CDPATH will complete:
# * Entries under the current directory ($env.PWD)
# * Entries in your home directory ($env.HOME)
# * Entries where you check out repositories
# * Children of those entries
#
# This CDPATH implementation also completes absolute paths to help you use `c`
# instead of `cd`
#
# Bugs:
# * `c` does not complete paths that start with "~". This should support both
# directories ("~/.config/nu"), users ("~notme"), and both combined
# ("~notme/.config/nu")
module cdpath {
# $env.CDPATH with unique, expanded, existing paths
def cdpath [] {
$env.CDPATH
| path expand
| uniq
| filter {|| $in | path exists }
}
# Children of a path
def children [path: string] {
ls -a $path
| where type == "dir"
| get name
}
# Completion for `c`
#
# `contains` is used instead of `starts-with` to behave similar to fuzzy
# completion behavior.
#
# During completion of a CDPATH entry the description contains the parent
# directory you will complete under. This allows you to tell which entry in
# your CDPATH your are completing to if you have the same directory under
# multiple entries.
def complete [context: string] {
let context_dir = $context | parse "c {context_dir}" | get context_dir | first
let path = $context_dir | path split
let no_trailing_slash = not ($context_dir | str ends-with "/")
# completion with no context
if ( $path | is-empty ) {
complete_from_cdpath
# Complete an entry in CDPATH
#
# This appends a / to allow continuation to the last step
} else if $no_trailing_slash and (1 == ( $path | length )) {
let first = $path | first
complete_from_cdpath
| filter {|| $in.value | str contains $first }
| upsert value {|| $"($in.value)/" }
# Complete a child of a CDPATH entry
} else {
let prefix = if 1 == ($path | length) {
$path | first
} else {
$path | first (($path | length) - 1) | path join
}
let last = if 1 == ($path | length) {
""
} else {
$path | last
}
let chosen_path = if ( $path | first) == "/" {
if $no_trailing_slash {
$prefix
} else {
$context_dir
}
} else {
cdpath
| each {||
$in | path join $prefix
}
| filter {||
$in | path exists
}
| first
}
children $chosen_path
| filter {||
$in | str contains $last
}
| each {|child|
$"($chosen_path | path join $child)/"
}
}
}
def complete_from_cdpath [] {
cdpath
| each { |path|
children $path
| path basename
| sort
| each { |child| { value: $child, description: $path } }
}
| flatten
| uniq-by value
}
# Change directory with $env.CDPATH
export def-env c [dir = "": string@complete] {
let span = (metadata $dir).span
let default = if $nu.os-info.name == "windows" {
$env.USERPROFILE
} else {
} else {
$env.HOME
}
}
let complete_dir = if $dir == "" {
let target_dir = if $dir == "" {
$default
} else if $dir == "-" {
if "OLDPWD" in $env {
$env.OLDPWD
} else {
$default
}
} else {
$env.CDPATH
| reduce -f "" { |$it, $acc| if $acc == "" {
let new_path = ([$it $dir] | path join)
cdpath
| reduce -f "" { |$it, $acc|
if $acc == "" {
let new_path = ([$it $dir] | path join)
if ($new_path | path exists) {
$new_path
$new_path
} else {
""
""
}
} else { $acc }}
}
let complete_dir = if $complete_dir == "" {
error make --unspanned {msg: "No such path"}
} else if (($complete_dir | path expand | path type) != "dir") {
error make --unspanned {msg: "Not a directory"}
} else {
($complete_dir | path expand)
} else {
$acc
}
}
}
cd $complete_dir
let target_dir = if $target_dir == "" {
let cdpath = $env.CDPATH | str join ", "
error make {
msg: $"No such child under: ($cdpath)",
label: {
text: "Child directory",
start: $span.start,
end: $span.end,
}
}
} else {
$target_dir
}
cd $target_dir
}
}
# You need to have $env.CDPATH variable declared, my suggestion from config.nu:
# $env.CDPATH = [".", $env.HOME, "/", ([$env.HOME, ".config"] | path join)]
# WINDOWS:
# $env.CDPATH = ["", $env.USERPROFILE, ([$env.USERPROFILE, "AppData\\Roaming\\"] | path join)]
use cdpath c