Skip to content

Commit fede383

Browse files
committed
Improve globbing
1 parent 1cfe710 commit fede383

File tree

3 files changed

+75
-54
lines changed

3 files changed

+75
-54
lines changed

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
- [ ] Add a mode for writing books under Kitty, with the editor to the left and a preview to the right, nice text flow and a typewriter-like user experience.
66
- [ ] There is some duplication between this source code and the source code of the `syntax` package. Refactor and fix.
7-
- [ ] When globbing, prioritize `.sh` over `.bat`, unless on Windows.
7+
- [x] When globbing, prioritize `.sh` over `.bat`, unless on Windows.
88
- [ ] Let the default theme have a 256-color variant that is like the default theme, only nicer.
99
- [ ] If Orbiton is launched by a symlink starting with `p` (for `preview`), then act as an auto-reloading Markdown preview program.
1010
- [ ] `o -b` should also build Go projects, Rust projects etc. (similar to `ctrl-space`), not only C and C++.

v2/glob.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package main
2+
3+
import (
4+
"runtime"
5+
"sort"
6+
"strings"
7+
8+
"github.com/xyproto/files"
9+
"github.com/xyproto/globi"
10+
)
11+
12+
// editPriority returns a sort weight for a filename when choosing which file to open.
13+
// Lower values are preferred. Tiers:
14+
//
15+
// 0 – regular text file
16+
// 1 – low-priority extension (.lock, .bak, …)
17+
// 2 – Windows-only script on a non-Windows platform (.bat, .cmd)
18+
// 3 – binary file
19+
func editPriority(filename string) int {
20+
if files.IsBinaryAccurate(filename) {
21+
return 3
22+
}
23+
if runtime.GOOS != "windows" && hasSuffix(filename, []string{".bat", ".cmd"}) {
24+
return 2
25+
}
26+
if hasSuffix(filename, probablyDoesNotWantToEditExtensions) || !strings.Contains(filename, ".") {
27+
return 1
28+
}
29+
return 0
30+
}
31+
32+
// sortByEditPriority sorts filenames so the most desirable file to edit comes first.
33+
// Within the same priority tier the names are ordered alphabetically.
34+
func sortByEditPriority(matches []string) {
35+
sort.SliceStable(matches, func(i, j int) bool {
36+
pi, pj := editPriority(matches[i]), editPriority(matches[j])
37+
if pi != pj {
38+
return pi < pj
39+
}
40+
return matches[i] < matches[j]
41+
})
42+
}
43+
44+
// approximateFilename tries to find an existing file that matches the given
45+
// incomplete filename, using glob expansion and weighted sorting.
46+
// It returns the best match, or the original name if no match is found.
47+
func approximateFilename(filename string) string {
48+
if strings.HasSuffix(filename, ".") {
49+
// Tab-completion left a trailing dot: glob for everything that starts with this prefix
50+
matches, err := globi.Glob(filename + "*")
51+
if err == nil && len(matches) > 0 {
52+
sortByEditPriority(matches)
53+
if len(matches[0]) > 0 {
54+
return matches[0]
55+
}
56+
}
57+
} else if !strings.Contains(filename, ".") && allLower(filename) {
58+
// No dot, all lowercase: more than one file may start with this name
59+
matches, err := globi.Glob(filename + "*")
60+
if err == nil && len(matches) > 1 {
61+
sortByEditPriority(matches)
62+
return matches[0]
63+
}
64+
} else {
65+
// Also match eg. "PKGBUILD" if just "Pk" was entered
66+
matches, err := globi.Glob(strings.ToTitle(filename) + "*")
67+
if err == nil && len(matches) >= 1 {
68+
sortByEditPriority(matches)
69+
return matches[0]
70+
}
71+
}
72+
return filename
73+
}

v2/main.go

Lines changed: 1 addition & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/xyproto/digraph"
1717
"github.com/xyproto/env/v2"
1818
"github.com/xyproto/files"
19-
"github.com/xyproto/globi"
2019
"github.com/xyproto/megafile"
2120
"github.com/xyproto/vt"
2221
)
@@ -448,58 +447,7 @@ func main() {
448447
// Check if the given filename is not a file, symlink or directory
449448
if !noApproxMatchFlag {
450449
if !files.IsFileOrSymlink(fnord.filename) && !files.IsDir(fnord.filename) {
451-
if strings.HasSuffix(fnord.filename, ".") {
452-
// If the filename ends with "." and the file does not exist, assume this was a result of tab-completion going wrong.
453-
// If there are multiple files that exist that start with the given filename, open the one first in the alphabet (.cpp before .o)
454-
matches, err := globi.Glob(fnord.filename + "*")
455-
if err == nil && len(matches) > 0 { // no error and at least 1 match
456-
// Filter out any binary files
457-
matches = files.FilterOutBinaryFilesAccurate(matches)
458-
if len(matches) > 0 {
459-
sort.Strings(matches)
460-
// If the matches contains low priority suffixes, such as ".lock", then move it last
461-
matchesRegular := make([]string, 0, len(matches))
462-
matchesLowPri := make([]string, 0, len(matches))
463-
for _, fn := range matches {
464-
if !hasSuffix(fn, probablyDoesNotWantToEditExtensions) && strings.Contains(fn, ".") {
465-
matchesRegular = append(matchesRegular, fn)
466-
} else {
467-
// Store as a low-priority match
468-
matchesLowPri = append(matchesLowPri, fn)
469-
}
470-
}
471-
// Combine the regular and the low-priority matches
472-
matches = append(matchesRegular, matchesLowPri...)
473-
if len(matches) > 0 && len(matches[0]) > 0 {
474-
// Use the first filename in the list of matches
475-
fnord.filename = matches[0]
476-
}
477-
}
478-
}
479-
} else if !strings.Contains(fnord.filename, ".") && allLower(fnord.filename) {
480-
// The filename has no ".", is written in lowercase and it does not exist,
481-
// but more than one file that starts with the filename exists. Assume tab-completion failed.
482-
matches, err := globi.Glob(fnord.filename + "*")
483-
if err == nil && len(matches) > 1 { // no error and more than 1 match
484-
// Use the first non-binary match of the sorted results
485-
matches = files.FilterOutBinaryFilesAccurate(matches)
486-
if len(matches) > 0 {
487-
sort.Strings(matches)
488-
fnord.filename = matches[0]
489-
}
490-
}
491-
} else {
492-
// Also match ie. "PKGBUILD" if just "Pk" was entered
493-
matches, err := globi.Glob(strings.ToTitle(fnord.filename) + "*")
494-
if err == nil && len(matches) >= 1 { // no error and at least 1 match
495-
// Use the first non-binary match of the sorted results
496-
matches = files.FilterOutBinaryFilesAccurate(matches)
497-
if len(matches) > 0 {
498-
sort.Strings(matches)
499-
fnord.filename = matches[0]
500-
}
501-
}
502-
}
450+
fnord.filename = approximateFilename(fnord.filename)
503451
} // !noApproxMatchFlag
504452
}
505453
}

0 commit comments

Comments
 (0)