at main 4.2 kB view raw
1export def ensure-cache [cache paths action] { 2 mut cfgs = [] 3 for i in $paths { 4 let cs = (do -i {ls $i}) 5 if not ($cs | is-empty) { 6 $cfgs = ($cfgs | append $cs) 7 } 8 } 9 let cfgs = $cfgs 10 let ts = ($cfgs | sort-by modified | reverse | get 0.modified) 11 if ($ts | is-empty) { return false } 12 let tc = (do -i { ls $cache | get 0.modified }) 13 if not (($cache | path exists) and ($ts < $tc)) { 14 mkdir ($cache | path dirname) 15 do $action | save -f $cache 16 } 17 open $cache 18} 19 20export def 'str max-length' [] { 21 $in | reduce -f 0 {|x, a| 22 if ($x|is-empty) { return $a } 23 let l = ($x | str length) 24 if $l > $a { $l } else { $a } 25 } 26} 27 28def "nu-complete ssh host" [] { 29 rg -LNI '^Host [a-z0-9_\-\.]+' ~/.ssh | lines | each {|x| $x | split row ' '| get 1} 30} 31 32export def parse-ssh-file [group] { 33 $in 34 | parse -r '(?P<k>Host|HostName|User|Port|IdentityFile)\s+(?P<v>.+)' 35 | append { k: Host, v: null} 36 | reduce -f { rst: [], item: {Host: null} } {|it, acc| 37 if $it.k == 'Host' { 38 $acc | upsert rst ($acc.rst | append $acc.item) 39 | upsert item { Host : $it.v, HostName: null, Port: null, User: null, IdentityFile: null, Group: $group } 40 } else { 41 $acc | upsert item ($acc.item | upsert $it.k $it.v) 42 } 43 } 44 | get rst 45 | where {|x| not (($x.Host | is-empty) or $x.Host =~ '\*')} 46} 47 48export def ssh-list [] { 49 rg -L -l 'Host' ~/.ssh 50 | lines 51 | each {|x| cat $x | parse-ssh-file $x} 52 | flatten 53} 54 55def fmt-group [p] { 56 $p | str replace $"($env.HOME)/.ssh/" '' 57} 58 59def "ssh-hosts" [] { 60 let cache = $'($env.HOME)/.cache/nu-complete/ssh.json' 61 ensure-cache $cache [~/.ssh/config ~/.ssh/config*/* ] { || 62 let data = (ssh-list | each {|x| 63 let uri = $"($x.User)@($x.HostName):($x.Port)" 64 { 65 value: $x.Host, 66 uri: $uri, 67 group: $"(fmt-group $x.Group)", 68 identfile: $"($x.IdentityFile)", 69 } 70 }) 71 72 let max = { 73 value: ($data.value | str max-length), 74 uri: ($data.uri | str max-length), 75 group: ($data.group | str max-length), 76 identfile: ($data.identfile | str max-length), 77 } 78 79 {max: $max, completion: $data} 80 } 81} 82 83def "nu-complete ssh" [] { 84 let data = (ssh-hosts) 85 $data.completion 86 | each { |x| 87 let uri = ($x.uri | fill -a l -w $data.max.uri -c ' ') 88 let group = ($x.group | fill -a l -w $data.max.group -c ' ') 89 let id = ($x.identfile | fill -a l -w $data.max.identfile -c ' ') 90 {value: $x.value, description: $"\t($uri) ($group) ($id)" } 91 } 92} 93 94export extern main [ 95 host: string@"nu-complete ssh" # host 96 ...cmd # cmd 97 -v # verbose 98 -i: string # key 99 -p: int # port 100 -N # n 101 -T # t 102 -L # l 103 -R # r 104 -D # d 105 -J: string # j 106 -W: string # w 107] 108 109 110def "nu-complete scp" [cmd: string, offset: int] { 111 let argv = ($cmd | str substring ..$offset | split row ' ') 112 let p = if ($argv | length) > 2 { $argv | get 2 } else { $argv | get 1 } 113 let ssh = (ssh-hosts | get completion 114 | each {|x| {value: $"($x.value):" description: $x.uri} } 115 ) 116 let n = ($p | split row ':') 117 if $"($n | get 0):" in ($ssh | get value) { 118 ^ssh ($n | get 0) $"sh -c 'ls -dp ($n | get 1)*'" 119 | lines 120 | each {|x| $"($n | get 0):($x)"} 121 } else { 122 let files = (do -i { 123 ls -a $"($p)*" 124 | each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }} 125 }) 126 $files | append $ssh 127 } 128} 129 130export def scp [ 131 lhs: string@"nu-complete scp", 132 rhs: string@"nu-complete scp" 133] { 134 ^scp -r $lhs $rhs 135}