@@ -125,97 +125,106 @@ func returnDirElementBySearchString(location string, displayDotFile bool, search
125125}
126126
127127func sortFileElement (sortOptions sortOptionsModelData , dirEntries []os.DirEntry , location string ) []element {
128- // Sort files
129- sort .Slice (dirEntries , getOrderingFunc (location , dirEntries ,
130- sortOptions .reversed , sortOptions .options [sortOptions .selected ]))
131- // Preallocate for efficiency
132- directoryElement := make ([]element , 0 , len (dirEntries ))
128+ elements := make ([]element , 0 , len (dirEntries ))
133129 for _ , item := range dirEntries {
134- directoryElement = append (directoryElement , element {
130+ info , err := item .Info ()
131+ if err != nil {
132+ slog .Error ("Error while retrieving file info during sort" ,
133+ "error" , err , "path" , filepath .Join (location , item .Name ()))
134+ continue
135+ }
136+
137+ elements = append (elements , element {
135138 name : item .Name (),
136- directory : item .IsDir (),
139+ directory : item .IsDir () || isSymlinkToDir ( location , info , item . Name ()) ,
137140 location : filepath .Join (location , item .Name ()),
141+ info : info ,
138142 })
139143 }
140- return directoryElement
144+
145+ sort .Slice (elements , getOrderingFunc (elements ,
146+ sortOptions .reversed , sortOptions .options [sortOptions .selected ]))
147+
148+ return elements
149+ }
150+
151+ // Symlinks to directories are to be identified as directories
152+ func isSymlinkToDir (location string , info os.FileInfo , name string ) bool {
153+ if info .Mode ()& os .ModeSymlink != 0 {
154+ targetInfo , errStat := os .Stat (filepath .Join (location , name ))
155+ return errStat == nil && targetInfo .IsDir ()
156+ }
157+ return false
141158}
142159
143- func getOrderingFunc (location string , dirEntries []os. DirEntry , reversed bool , sortOption string ) sliceOrderFunc {
160+ func getOrderingFunc (elements [] element , reversed bool , sortOption string ) sliceOrderFunc {
144161 var order func (i , j int ) bool
145162 switch sortOption {
146163 case string (sortingName ):
147164 order = func (i , j int ) bool {
148165 // One of them is a directory, and other is not
149- if dirEntries [i ].IsDir () != dirEntries [j ].IsDir () {
150- return dirEntries [i ].IsDir ()
166+ if elements [i ].directory != elements [j ].directory {
167+ return elements [i ].directory
151168 }
152169 if common .Config .CaseSensitiveSort {
153- return dirEntries [i ].Name () < dirEntries [j ].Name () != reversed
170+ return elements [i ].name < elements [j ].name != reversed
154171 }
155- return strings .ToLower (dirEntries [i ].Name ()) < strings .ToLower (dirEntries [j ].Name () ) != reversed
172+ return strings .ToLower (elements [i ].name ) < strings .ToLower (elements [j ].name ) != reversed
156173 }
157174 case string (sortingSize ):
158- order = getSizeOrderingFunc (dirEntries , reversed , location )
175+ order = getSizeOrderingFunc (elements , reversed )
159176 case string (sortingDateModified ):
160177 order = func (i , j int ) bool {
161- // No need for err check, we already filtered out dirEntries with err != nil in Info() call
162- fileInfoI , _ := dirEntries [i ].Info ()
163- fileInfoJ , _ := dirEntries [j ].Info ()
164- // Note : If ModTime matches, the comparator returns false both ways; order becomes non-deterministic
165- // TODO: Fix this
166- return fileInfoI .ModTime ().After (fileInfoJ .ModTime ()) != reversed
178+ return elements [i ].info .ModTime ().After (elements [j ].info .ModTime ()) != reversed
167179 }
168180 case string (sortingFileType ):
169- order = getTypeOrderingFunc (dirEntries , reversed )
181+ order = getTypeOrderingFunc (elements , reversed )
170182 }
171183 return order
172184}
173185
174- func getSizeOrderingFunc (dirEntries []os. DirEntry , reversed bool , location string ) sliceOrderFunc {
186+ func getSizeOrderingFunc (elements []element , reversed bool ) sliceOrderFunc {
175187 return func (i , j int ) bool {
176188 // Directories at the top sorted by direct child count (not recursive)
177189 // Files sorted by size
178190
179191 // One of them is a directory, and other is not
180- if dirEntries [i ].IsDir () != dirEntries [j ].IsDir () {
181- return dirEntries [i ].IsDir ()
192+ if elements [i ].directory != elements [j ].directory {
193+ return elements [i ].directory
182194 }
183195
184196 // This needs to be improved, and we should sort by actual size only
185197 // Repeated recursive read would be slow, so we could cache
186- if dirEntries [i ].IsDir () && dirEntries [j ].IsDir () {
187- filesI , err := os .ReadDir (filepath . Join ( location , dirEntries [i ].Name ()) )
198+ if elements [i ].directory && elements [j ].directory {
199+ filesI , err := os .ReadDir (elements [i ].location )
188200 // No need of early return, we only call len() on filesI, so nil would
189201 // just result in 0
190202 if err != nil {
191203 slog .Error ("Error when reading directory during sort" , "error" , err )
192204 }
193- filesJ , err := os .ReadDir (filepath . Join ( location , dirEntries [j ].Name ()) )
205+ filesJ , err := os .ReadDir (elements [j ].location )
194206 if err != nil {
195207 slog .Error ("Error when reading directory during sort" , "error" , err )
196208 }
197209 return len (filesI ) < len (filesJ ) != reversed
198210 }
199- // No need for err check, we already filtered out dirEntries with err != nil in Info() call
200- fileInfoI , _ := dirEntries [i ].Info ()
201- fileInfoJ , _ := dirEntries [j ].Info ()
202- return fileInfoI .Size () < fileInfoJ .Size () != reversed
211+ return elements [i ].info .Size () < elements [j ].info .Size () != reversed
203212 }
204213}
205214
206- func getTypeOrderingFunc (dirEntries []os. DirEntry , reversed bool ) sliceOrderFunc {
215+ func getTypeOrderingFunc (elements []element , reversed bool ) sliceOrderFunc {
207216 return func (i , j int ) bool {
208217 // One of them is a directory, and the other is not
209- if dirEntries [i ].IsDir () != dirEntries [j ].IsDir () {
210- return dirEntries [i ].IsDir ()
218+ if elements [i ].directory != elements [j ].directory {
219+ return elements [i ].directory
211220 }
212221
213222 var extI , extJ string
214- if ! dirEntries [i ].IsDir () {
215- extI = strings .ToLower (filepath .Ext (dirEntries [i ].Name () ))
223+ if ! elements [i ].directory {
224+ extI = strings .ToLower (filepath .Ext (elements [i ].name ))
216225 }
217- if ! dirEntries [j ].IsDir () {
218- extJ = strings .ToLower (filepath .Ext (dirEntries [j ].Name () ))
226+ if ! elements [j ].directory {
227+ extJ = strings .ToLower (filepath .Ext (elements [j ].name ))
219228 }
220229
221230 // Compare by extension/type
@@ -225,10 +234,10 @@ func getTypeOrderingFunc(dirEntries []os.DirEntry, reversed bool) sliceOrderFunc
225234
226235 // If same type, fall back to name
227236 if common .Config .CaseSensitiveSort {
228- return (dirEntries [i ].Name () < dirEntries [j ].Name () ) != reversed
237+ return (elements [i ].name < elements [j ].name ) != reversed
229238 }
230239
231- return (strings .ToLower (dirEntries [i ].Name ()) < strings .ToLower (dirEntries [j ].Name () )) != reversed
240+ return (strings .ToLower (elements [i ].name ) < strings .ToLower (elements [j ].name )) != reversed
232241 }
233242}
234243
0 commit comments