mirror of
https://github.com/octoleo/syncthing.git
synced 2025-02-02 03:48:26 +00:00
cmd/syncthing: Make directory auto-complete case insensitive (fixes #1347)
This commit is contained in:
parent
aec91d8f32
commit
add12b43aa
@ -1542,6 +1542,24 @@ func (s *apiService) getSystemBrowse(w http.ResponseWriter, r *http.Request) {
|
||||
sendJSON(w, browseFiles(current, fsType))
|
||||
}
|
||||
|
||||
const (
|
||||
matchExact int = iota
|
||||
matchCaseIns
|
||||
noMatch
|
||||
)
|
||||
|
||||
func checkPrefixMatch(s, prefix string) int {
|
||||
if strings.HasPrefix(s, prefix) {
|
||||
return matchExact
|
||||
}
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(s), strings.ToLower(prefix)) {
|
||||
return matchCaseIns
|
||||
}
|
||||
|
||||
return noMatch
|
||||
}
|
||||
|
||||
func browseFiles(current string, fsType fs.FilesystemType) []string {
|
||||
if current == "" {
|
||||
filesystem := fs.NewFilesystem(fsType, "")
|
||||
@ -1567,16 +1585,29 @@ func browseFiles(current string, fsType fs.FilesystemType) []string {
|
||||
|
||||
fs := fs.NewFilesystem(fsType, searchDir)
|
||||
|
||||
subdirectories, _ := fs.Glob(searchFile + "*")
|
||||
subdirectories, _ := fs.DirNames(".")
|
||||
|
||||
exactMatches := make([]string, 0, len(subdirectories))
|
||||
caseInsMatches := make([]string, 0, len(subdirectories))
|
||||
|
||||
ret := make([]string, 0, len(subdirectories))
|
||||
for _, subdirectory := range subdirectories {
|
||||
info, err := fs.Stat(subdirectory)
|
||||
if err == nil && info.IsDir() {
|
||||
ret = append(ret, filepath.Join(searchDir, subdirectory)+pathSeparator)
|
||||
if err != nil || !info.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
switch checkPrefixMatch(subdirectory, searchFile) {
|
||||
case matchExact:
|
||||
exactMatches = append(exactMatches, filepath.Join(searchDir, subdirectory)+pathSeparator)
|
||||
case matchCaseIns:
|
||||
caseInsMatches = append(caseInsMatches, filepath.Join(searchDir, subdirectory)+pathSeparator)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
|
||||
// sort to return matches in deterministic order (don't depend on file system order)
|
||||
sort.Strings(exactMatches)
|
||||
sort.Strings(caseInsMatches)
|
||||
return append(exactMatches, caseInsMatches...)
|
||||
}
|
||||
|
||||
func (s *apiService) getCPUProf(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -988,10 +988,14 @@ func TestBrowse(t *testing.T) {
|
||||
if err := ioutil.WriteFile(filepath.Join(tmpDir, "file"), []byte("hello"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Mkdir(filepath.Join(tmpDir, "MiXEDCase"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// We expect completion to return the full path to the completed
|
||||
// directory, with an ending slash.
|
||||
dirPath := filepath.Join(tmpDir, "dir") + pathSep
|
||||
mixedCaseDirPath := filepath.Join(tmpDir, "MiXEDCase") + pathSep
|
||||
|
||||
cases := []struct {
|
||||
current string
|
||||
@ -1002,13 +1006,15 @@ func TestBrowse(t *testing.T) {
|
||||
// With slash it's completed to its contents.
|
||||
// Dirs are given pathSeps.
|
||||
// Files are not returned.
|
||||
{tmpDir + pathSep, []string{dirPath}},
|
||||
{tmpDir + pathSep, []string{mixedCaseDirPath, dirPath}},
|
||||
// Globbing is automatic based on prefix.
|
||||
{tmpDir + pathSep + "d", []string{dirPath}},
|
||||
{tmpDir + pathSep + "di", []string{dirPath}},
|
||||
{tmpDir + pathSep + "dir", []string{dirPath}},
|
||||
{tmpDir + pathSep + "f", nil},
|
||||
{tmpDir + pathSep + "q", nil},
|
||||
// Globbing is case-insensitve
|
||||
{tmpDir + pathSep + "mixed", []string{mixedCaseDirPath}},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
@ -1019,6 +1025,26 @@ func TestBrowse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrefixMatch(t *testing.T) {
|
||||
cases := []struct {
|
||||
s string
|
||||
prefix string
|
||||
expected int
|
||||
}{
|
||||
{"aaaA", "aaa", matchExact},
|
||||
{"AAAX", "BBB", noMatch},
|
||||
{"AAAX", "aAa", matchCaseIns},
|
||||
{"äÜX", "äü", matchCaseIns},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
ret := checkPrefixMatch(tc.s, tc.prefix)
|
||||
if ret != tc.expected {
|
||||
t.Errorf("checkPrefixMatch(%q, %q) => %v, expected %v", tc.s, tc.prefix, ret, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func equalStrings(a, b []string) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
|
Loading…
x
Reference in New Issue
Block a user