Skip to content

Add completions for lftp #1113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions custom-completions/lftp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# LFTP completions

[`LFTP`](https://lftp.yar.ru/) is a file transfer program supporting many protocols (FTP, HTTP, SFTP, FISH, TORRENT). It is shell-like, reliable and can transfer several files in parallel.

## Install completion script

### Method 1: Use from Git repo

- `git clone https://github.com/nushell/nu_scripts.git`

- Add this to `$nu.config-path` file.

```nu
source repo/custom-completions/lftp/lftp-completions.nu
```

### Method 2: Selectively copy


- Copy the _lftp-completions.nu_ to Nu standard place.

```nu
cp custom-completions/lftp/lftp-completions.nu ($nu.data-dir | path join 'completions')
```

- Open `$nu.config-path` file and add this:

```nu
source lftp-completions.nu
```

98 changes: 98 additions & 0 deletions custom-completions/lftp/lftp-completions.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# nu-version: 0.102.0

module lftp-completion-utils {
def extract-host []: list<string> -> record<name: string, addr: string> {
# Host is a list of lines, like:
# ╭───┬──────────────────────────────╮
# │ 0 │ Host quanweb │
# │ 1 │ User quan │
# │ 2 │ Hostname quan.hoabinh.vn │
# │ 3 │ # ProxyJump farmb-omz │
# │ 4 │ │
# ╰───┴──────────────────────────────╯
let host = $in
let $first_line = try { $host | first | str trim } catch { null }
# Don't accept blocks like "Host *"
if ($first_line | is-empty) or '*' in $first_line {
null
} else {
let name = $first_line | split row -r '\s+' | get 1
# May not contain hostname
match ($host | slice 1.. | find -ir '^\s*Hostname\s') {
[] => null,
$addr => { name: $name, addr: ($addr | str trim | split row -n 2 -r '\s+' | get 1) }
}
}
}

# Process a SSH config file
export def process []: string -> record<hosts: list<record<name: string, addr: string>>, includes: list<string>> {
let lines = $in | lines
# Get 'Include' lines
let include_lines = $lines | find -n -ir '^Include\s' | str trim | each { $in | split row -n 2 -r '\s+' | get 1 | str trim -c '"'}
# Find "Host" blocks
let marks = $lines | enumerate | find -n -ir '^Host\s'
let mark_indices = $marks | get index | append ($lines | length)
let hosts = $mark_indices | window 2 | each {|w| $lines | slice $w.0..<($w.1) }
{
hosts: ($hosts | each { $in | extract-host }),
includes: $include_lines
}
}

export def get-ssh-sites []: nothing -> table<value: string, description: string> {
let files = [
'/etc/ssh/ssh_config',
'~/.ssh/config'
] | filter {|file| $file | path exists }


let first_result: record<hosts: list<record<name: string, addr: string>>, includes: list<string>> = $files | par-each {|file|
let folder = $file | path expand | path dirname
let r = $file | open --raw | process
$r | update includes { each {|f| $folder | path join $f } }
} | reduce {|it| merge deep $it --strategy=append }

let $includes: list<string> = $first_result.includes | each {|f|
if '*' in $f {
glob $f
} else if ($f | path exists) {
[$f]
} else []
} | flatten

# Process include files
let included_hosts: list<record<name: string, addr: string>> = (if ($includes | is-empty) { [] } else {
let second_result = $includes | par-each {|p| $p | open --raw | process } | reduce {|it| merge deep $it --strategy=append }
$second_result.hosts
})

let hosts = $first_result.hosts ++ $included_hosts
$hosts | each {|h| [$"sftp://($h.name)" $"fish://($h.name)"] | wrap value | insert description $h.addr } | flatten
}

export def get-bookmark-sites []: nothing -> table<value: string, description: string> {
const FILE_PATH = '~/.local/share/lftp/bookmarks' | path expand
let sites = open --raw $FILE_PATH | lines | split column -n 2 -r \s+ value description
$sites | update value { $"bm:($in)" }
}
}


def "nu-complete lftp-site" [] {
use lftp-completion-utils get-ssh-sites
use lftp-completion-utils get-bookmark-sites
get-ssh-sites | append (get-bookmark-sites)
}

export extern 'lftp' [
-c: string # Execute the commands and exit
-d # Switch on debugging mode
-e: string # Execute the command just after selecting
-p: int # Use the port for connection
-u: string # Use the user/password for authentication
--norc # Don't execute rc files from the home directory
--help # Print the help and exit
--version # Print lftp version and exit
site?: string@"nu-complete lftp-site" # Host name, URL or bookmark name
]