My user config prefs
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}