feat(shortcuts): regex rewrite rules for host shortcuts
shortcuts.rewrites applies regex replacements after the domain suffix is stripped, e.g. ^([a-z]+)[0-9]+- -> $1 turns fsn1-bx1.hoster.de into fsnbx1. The shortcut works for connecting (cached hosts), shell aliases, and Tab completion, where it is offered when the full name does not match the typed text. Rules are editable in netssh configure, and netssh alias --test HOSTNAME shows a name's shortcut and the cached hosts it matches. The shortcut lookup only ran when domains or strip_hyphens were set; it now also runs for rewrite-only configurations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d51c040d9f
commit
878701aba7
12 files changed
+408
-21
No files matched your search
+56
-6
@@ -41,7 +41,9 @@ func normalizeTarget(cfg config.ShortcutsConfig, qualified bool) func(string) st
|
||||
// all instances is offered bare ("web01"), so it completes like with a single
|
||||
// NetBox; connecting to it still checks every instance for ambiguity.
|
||||
// Names present in several instances stay qualified ("prod/web01").
|
||||
func hostCompletions(entries []cache.Entry, toComplete string) []string {
|
||||
// Hosts whose full name does not match are offered by their configured
|
||||
// shortcut instead ("fsnb" → "fsnbx1" for fsn1-bx1.example.com).
|
||||
func hostCompletions(entries []cache.Entry, toComplete string, sc config.ShortcutsConfig) []string {
|
||||
prefix := strings.ToLower(toComplete)
|
||||
qualifiedInput := strings.Contains(prefix, "/")
|
||||
instances := make(map[string]int)
|
||||
@@ -49,21 +51,69 @@ func hostCompletions(entries []cache.Entry, toComplete string) []string {
|
||||
_, name := netbox.SplitTarget(e.Name)
|
||||
instances[strings.ToLower(name)]++
|
||||
}
|
||||
var out []string
|
||||
var unmatched []cache.Entry
|
||||
var names []string
|
||||
for _, e := range entries {
|
||||
source, name := netbox.SplitTarget(e.Name)
|
||||
target := strings.ToLower(e.Name)
|
||||
switch {
|
||||
case source != "" && !qualifiedInput && strings.HasPrefix(strings.ToLower(name), prefix):
|
||||
if instances[strings.ToLower(name)] == 1 {
|
||||
out = append(out, name)
|
||||
names = append(names, name)
|
||||
} else {
|
||||
out = append(out, e.Name)
|
||||
names = append(names, e.Name)
|
||||
}
|
||||
case strings.HasPrefix(target, prefix):
|
||||
out = append(out, e.Name)
|
||||
names = append(names, e.Name)
|
||||
default:
|
||||
unmatched = append(unmatched, e)
|
||||
}
|
||||
}
|
||||
if sc.Enabled() {
|
||||
names = append(names, shortcutCompletions(entries, unmatched, prefix, sc)...)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
// shortcutCompletions offers the shortcut of each unmatched host when it starts
|
||||
// with prefix and selects exactly one host, qualified by instance if needed.
|
||||
func shortcutCompletions(all, unmatched []cache.Entry, prefix string, sc config.ShortcutsConfig) []string {
|
||||
bare := make(map[string]int)
|
||||
qualified := make(map[string]int)
|
||||
short := func(e cache.Entry) (string, string) {
|
||||
source, name := netbox.SplitTarget(e.Name)
|
||||
s := shortcuts.Normalize(name, sc)
|
||||
if source == "" {
|
||||
return s, s
|
||||
}
|
||||
return s, strings.ToLower(source) + "/" + s
|
||||
}
|
||||
for _, e := range all {
|
||||
b, q := short(e)
|
||||
bare[b]++
|
||||
qualified[q]++
|
||||
}
|
||||
seen := make(map[string]bool)
|
||||
var out []string
|
||||
for _, e := range unmatched {
|
||||
b, q := short(e)
|
||||
candidate := ""
|
||||
switch {
|
||||
case strings.Contains(prefix, "/"):
|
||||
if qualified[q] == 1 && strings.HasPrefix(q, prefix) {
|
||||
candidate = q
|
||||
}
|
||||
case !strings.HasPrefix(b, prefix):
|
||||
case bare[b] == 1:
|
||||
candidate = b
|
||||
case q != b && qualified[q] == 1:
|
||||
candidate = q // like full names: zsh accepts it, bash/fish ignore it
|
||||
}
|
||||
if candidate != "" && !seen[candidate] {
|
||||
seen[candidate] = true
|
||||
out = append(out, candidate)
|
||||
}
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
Reference in new issue
Block a user