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:
parent
c39a41ad2d
commit
c93ce14fc9
1 changed files with 157 additions and 24 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue