mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-19 02:51:07 +00:00
remote_file kitten: correct pythonSplitExt to match os.path.splitext semantics
This commit is contained in:
parent
f9e242e308
commit
062ff5fed1
2 changed files with 65 additions and 13 deletions
|
|
@ -99,23 +99,43 @@ func hostname_matches(from_hyperlink, actual string) bool {
|
|||
return fl == al
|
||||
}
|
||||
|
||||
// pythonSplitExt mirrors Python's os.path.splitext behavior for dotfiles.
|
||||
// For a file like "/x/.bashrc" (basename ".bashrc"), Python returns ("/x/.bashrc", "").
|
||||
// Go's filepath.Ext would return (".bashrc", "/x/"), losing the filename.
|
||||
// Rule: if basename starts with '.' and has no other dots, treat extension as empty.
|
||||
// pythonSplitExt mirrors Python's os.path.splitext behavior exactly.
|
||||
// Algorithm: skip all leading dots in basename; find last dot in remainder;
|
||||
// if found, split at that dot; otherwise no extension.
|
||||
// Examples:
|
||||
// "/x/.bashrc" → ("/x/.bashrc", "") [1 leading dot, no dot in remainder]
|
||||
// "/x/.bashrc.bak" → ("/x/.bashrc", ".bak") [1 leading dot, 1 dot in remainder]
|
||||
// "/x/a.b.c" → ("/x/a.b", ".c") [0 leading dots, last dot at position 3]
|
||||
// "....txt" → ("....txt", "") [4 leading dots, no dot in remainder]
|
||||
func pythonSplitExt(path string) (string, string) {
|
||||
ext := filepath.Ext(path)
|
||||
if ext == "" {
|
||||
return path, ""
|
||||
}
|
||||
basename := filepath.Base(path)
|
||||
// If basename starts with '.' and has exactly one dot (the leading dot),
|
||||
// then this is a dotfile with no extension.
|
||||
if strings.HasPrefix(basename, ".") && strings.Count(basename, ".") == 1 {
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
// Skip all leading dots in basename
|
||||
i := 0
|
||||
for i < len(basename) && basename[i] == '.' {
|
||||
i++
|
||||
}
|
||||
|
||||
// Find the last dot in the remainder (after leading dots)
|
||||
remainder := basename[i:]
|
||||
idx := strings.LastIndex(remainder, ".")
|
||||
|
||||
if idx == -1 {
|
||||
// No extension found
|
||||
return path, ""
|
||||
}
|
||||
base := strings.TrimSuffix(path, ext)
|
||||
return base, ext
|
||||
|
||||
// Extension starts at position i + idx in the original basename
|
||||
extStart := i + idx
|
||||
baseName := basename[:extStart]
|
||||
ext := basename[extStart:]
|
||||
|
||||
// Reconstruct the full path
|
||||
if dir == "." {
|
||||
return baseName, ext
|
||||
}
|
||||
return filepath.Join(dir, baseName), ext
|
||||
}
|
||||
|
||||
func auto_rename_dest(dest string, exists func(string) bool) string {
|
||||
|
|
|
|||
|
|
@ -95,3 +95,35 @@ func TestParseConnDataSSHKittenEdge(t *testing.T) {
|
|||
t.Fatalf("ssh kitten edge case (single item): %+v, SSHKittenCmdline=%v", c, c.SSHKittenCmdline)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPythonSplitExt(t *testing.T) {
|
||||
// Table-driven test for pythonSplitExt matching os.path.splitext semantics.
|
||||
// Expected values from Python 3: python3 -c "import os; print(os.path.splitext(f))" for each.
|
||||
// Reference run: 2026-08-12
|
||||
for _, tc := range []struct {
|
||||
input string
|
||||
wantBase string
|
||||
wantExt string
|
||||
}{
|
||||
// Dotfiles with no extension in remainder (leading dots only)
|
||||
{".bashrc", ".bashrc", ""},
|
||||
{"..bashrc", "..bashrc", ""},
|
||||
{"...", "...", ""},
|
||||
{"..", "..", ""},
|
||||
{"....txt", "....txt", ""}, // 4 leading dots, then "txt" with no dot
|
||||
|
||||
// Dotfiles with extension in remainder
|
||||
{".bashrc.bak", ".bashrc", ".bak"},
|
||||
|
||||
// Normal files
|
||||
{"a.b.c", "a.b", ".c"},
|
||||
{"a.txt", "a", ".txt"},
|
||||
{"plain", "plain", ""},
|
||||
} {
|
||||
base, ext := pythonSplitExt(tc.input)
|
||||
if base != tc.wantBase || ext != tc.wantExt {
|
||||
t.Fatalf("pythonSplitExt(%q):\n got (%q, %q)\n want (%q, %q)",
|
||||
tc.input, base, ext, tc.wantBase, tc.wantExt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue