diff options
-rw-r--r-- | conf/name.go | 61 | ||||
-rw-r--r-- | conf/path_windows.go | 2 | ||||
-rw-r--r-- | ui/listview.go | 8 | ||||
-rw-r--r-- | ui/tray.go | 5 | ||||
-rw-r--r-- | ui/tunnelspage.go | 3 |
5 files changed, 70 insertions, 9 deletions
diff --git a/conf/name.go b/conf/name.go index 00479c04..87c463af 100644 --- a/conf/name.go +++ b/conf/name.go @@ -7,6 +7,7 @@ package conf import ( "regexp" + "strconv" "strings" ) @@ -49,3 +50,63 @@ func TunnelNameIsValid(name string) bool { } return allowedNameFormat.MatchString(name) } + +type naturalSortToken struct { + maybeString string + maybeNumber int +} +type naturalSortString struct { + originalString string + tokens []naturalSortToken +} + +var naturalSortDigitFinder = regexp.MustCompile(`\d+|\D+`) + +func newNaturalSortString(s string) (t naturalSortString) { + t.originalString = s + s = strings.ToLower(strings.Join(strings.Fields(s), " ")) + x := naturalSortDigitFinder.FindAllString(s, -1) + t.tokens = make([]naturalSortToken, len(x)) + for i, s := range x { + if n, err := strconv.Atoi(s); err == nil { + t.tokens[i].maybeNumber = n + } else { + t.tokens[i].maybeString = s + } + } + return +} + +func (f1 naturalSortToken) Cmp(f2 naturalSortToken) int { + if len(f1.maybeString) == 0 { + if len(f2.maybeString) > 0 || f1.maybeNumber < f2.maybeNumber { + return -1 + } else if f1.maybeNumber > f2.maybeNumber { + return 1 + } + } else if len(f2.maybeString) == 0 || f1.maybeString > f2.maybeString { + return 1 + } else if f1.maybeString < f2.maybeString { + return -1 + } + return 0 +} + +func TunnelNameIsLess(a, b string) bool { + if a == b { + return false + } + na, nb := newNaturalSortString(a), newNaturalSortString(b) + for i, t := range nb.tokens { + if i == len(na.tokens) { + return true + } + switch na.tokens[i].Cmp(t) { + case -1: + return true + case 1: + return false + } + } + return false +} diff --git a/conf/path_windows.go b/conf/path_windows.go index 96c68738..8b23e399 100644 --- a/conf/path_windows.go +++ b/conf/path_windows.go @@ -65,4 +65,4 @@ func RootDirectory() (string, error) { } cachedRootDir = c return cachedRootDir, nil -}
\ No newline at end of file +} diff --git a/ui/listview.go b/ui/listview.go index c4f5186b..2e9ee3a7 100644 --- a/ui/listview.go +++ b/ui/listview.go @@ -6,8 +6,8 @@ package ui import ( + "golang.zx2c4.com/wireguard/windows/conf" "sort" - "strings" "sync/atomic" "github.com/lxn/walk" @@ -43,8 +43,7 @@ func (t *ListModel) Value(row, col int) interface{} { func (t *ListModel) Sort(col int, order walk.SortOrder) error { sort.SliceStable(t.tunnels, func(i, j int) bool { - //TODO: use real string comparison for sorting with proper tunnel order - return t.tunnels[i].Name < t.tunnels[j].Name + return conf.TunnelNameIsLess(t.tunnels[i].Name, t.tunnels[j].Name) }) return t.SorterBase.Sort(col, order) @@ -201,8 +200,7 @@ func (tv *ListView) Load(asyncUI bool) { firstTunnelName := "" for tunnel := range newTunnels { if !oldTunnels[tunnel] { - //TODO: use proper tunnel string sorting/comparison algorithm, as the other comments indicate too. - if len(firstTunnelName) == 0 || strings.Compare(firstTunnelName, tunnel.Name) > 0 { + if len(firstTunnelName) == 0 || !conf.TunnelNameIsLess(firstTunnelName, tunnel.Name) { firstTunnelName = tunnel.Name } tv.model.tunnels = append(tv.model.tunnels, tunnel) @@ -7,6 +7,7 @@ package ui import ( "fmt" + "golang.zx2c4.com/wireguard/windows/conf" "sort" "strings" @@ -168,7 +169,9 @@ func (tray *Tray) addTunnelAction(tunnel *service.Tunnel) { for name := range tray.tunnels { names = append(names, name) } - sort.Strings(names) //TODO: use correct sorting order for this + sort.SliceStable(names, func(i, j int) bool { + return conf.TunnelNameIsLess(names[i], names[j]) + }) var ( idx int diff --git a/ui/tunnelspage.go b/ui/tunnelspage.go index 8bcf6aaa..c2521a6b 100644 --- a/ui/tunnelspage.go +++ b/ui/tunnelspage.go @@ -314,8 +314,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { // Add in reverse order so that the first one is selected. sort.Slice(unparsedConfigs, func(i, j int) bool { - //TODO: use proper tunnel string sorting/comparison algorithm, as the other comments indicate too. - return strings.Compare(unparsedConfigs[i].Name, unparsedConfigs[j].Name) > 0 + return conf.TunnelNameIsLess(unparsedConfigs[j].Name, unparsedConfigs[i].Name) }) existingTunnelList, err := service.IPCClientTunnels() |