Skip to content

Commit

Permalink
fix: Fix file listing. Better error handling and performance improvem…
Browse files Browse the repository at this point in the history
…ents
  • Loading branch information
lazysegtree committed Feb 4, 2025
1 parent 3e68275 commit ae9a586
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 66 deletions.
107 changes: 46 additions & 61 deletions src/internal/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"regexp"
"slices"
"sort"
"strconv"
"strings"
Expand Down Expand Up @@ -41,14 +42,23 @@ func returnFocusType(focusPanel focusPanelType) filePanelFocusType {
return secondFocus
}

func returnFolderElement(location string, displayDotFile bool, sortOptions sortOptionsModelData) (directoryElement []element) {
// Need to rename folder to directory.
func returnDirectoryElement(location string, displayDotFile bool, sortOptions sortOptionsModelData) (directoryElement []element) {
slog.Debug("returnFolderElement() called.")
files, err := os.ReadDir(location)
dirEntries, err := os.ReadDir(location)
if err != nil {
outPutLog("Return folder element function error", err)
return directoryElement
}
if len(files) == 0 {

dirEntries = slices.DeleteFunc(dirEntries, func(e os.DirEntry) bool {
// Should be deleted
_, err := e.Info()
return err != nil || (strings.HasPrefix(e.Name(), ".") && !displayDotFile)
})

// No files/directoes to process
if len(dirEntries) == 0 {
return directoryElement
}

Expand All @@ -60,92 +70,67 @@ func returnFolderElement(location string, displayDotFile bool, sortOptions sortO
case "Name":
order = func(i, j int) bool {
slog.Debug("sort func", "i", i, "j", j)
// This is inefficient, no need to read directories recurdsively here
// We just need their name to sort.
_, errI := os.ReadDir(location + "/" + files[i].Name())
_, errJ := os.ReadDir(location + "/" + files[j].Name())
if (files[i].IsDir() || errI == nil) && (!files[j].IsDir() && errJ != nil) {
return true
}
if (!files[i].IsDir() && errI != nil) && (files[j].IsDir() || errJ == nil) {
return false

// One of them is a directory, and other is not
if dirEntries[i].IsDir() != dirEntries[j].IsDir() {
return dirEntries[i].IsDir()
}

if Config.CaseSensitiveSort {
return files[i].Name() < files[j].Name() != reversed
return dirEntries[i].Name() < dirEntries[j].Name() != reversed
} else {
return strings.ToLower(files[i].Name()) < strings.ToLower(files[j].Name()) != reversed
return strings.ToLower(dirEntries[i].Name()) < strings.ToLower(dirEntries[j].Name()) != reversed
}
}
case "Size":
order = func(i, j int) bool {
// Directories at the top sorted by direct child count (not recursive)
// Files sorted by size
fileInfoI, _ := files[i].Info()
fileInfoJ, _ := files[j].Info()

// Hard coded "/" - Should only use filepath.join
_, errI := os.ReadDir(location + "/" + files[i].Name())
_, errJ := os.ReadDir(location + "/" + files[j].Name())

if (files[i].IsDir() || errI == nil) && (!files[j].IsDir() && errJ != nil) {
return true
}
if (!files[i].IsDir() && errI != nil) && (files[j].IsDir() || errJ == nil) {
return false

// One of them is a directory, and other is not
if dirEntries[i].IsDir() != dirEntries[j].IsDir() {
return dirEntries[i].IsDir()
}

// This needs to be improved, and we should sort by actual size only
// Repeated recursive read would be slow, so we could cache
if files[i].IsDir() && files[j].IsDir() {
filesI, err := os.ReadDir(filepath.Join(location, files[i].Name()))
// Repeated recursive read would be slow, so we could cache
if dirEntries[i].IsDir() && dirEntries[j].IsDir() {
filesI, err := os.ReadDir(filepath.Join(location, dirEntries[i].Name()))
// No need of early return, we only call len() on filesI, so nil would
// just result in 0
if err != nil {
outPutLog("Error when reading directory", err)
outPutLog("Error when reading directory during sort", err)
}
filesJ, err := os.ReadDir(filepath.Join(location, files[j].Name()))
filesJ, err := os.ReadDir(filepath.Join(location, dirEntries[j].Name()))
if err != nil {
outPutLog("Error when reading directory", err)
outPutLog("Error when reading directory during sort", err)
}
return len(filesI) < len(filesJ) != reversed
} else {
// No need for err check, we already filtered out dirEntries with err != nil in Info() call
fileInfoI, _ := dirEntries[i].Info()
fileInfoJ, _ := dirEntries[j].Info()
return fileInfoI.Size() < fileInfoJ.Size() != reversed
}
return fileInfoI.Size() < fileInfoJ.Size() != reversed

}
case "Date Modified":
order = func(i, j int) bool {
// You should make sure that files with info nil are filtered beforehand
fileInfoI, _ := files[i].Info()
fileInfoJ, _ := files[j].Info()
// No need for err check, we already filtered out dirEntries with err != nil in Info() call
fileInfoI, _ := dirEntries[i].Info()
fileInfoJ, _ := dirEntries[j].Info()
return fileInfoI.ModTime().After(fileInfoJ.ModTime()) != reversed
}
}

sort.Slice(files, order)
sort.Slice(dirEntries, order)

for _, item := range files {
fileInfo, err := item.Info()
// should have skipped these files way before
if err != nil {
continue
}

// If the dot files were not even needed, why consider them for sorting ?
if !displayDotFile && strings.HasPrefix(fileInfo.Name(), ".") {
continue
}
// should have skipped before .
if fileInfo == nil {
continue
}
newElement := element{
for _, item := range dirEntries {
directoryElement = append(directoryElement, element{
name: item.Name(),
directory: item.IsDir(),
}
// Redundant.
if location == "/" {
newElement.location = location + item.Name()
} else {
newElement.location = filepath.Join(location, item.Name())
}
directoryElement = append(directoryElement, newElement)
location: filepath.Join(location, item.Name()),
})
}

slog.Debug("returnFolderElement() returning.", "directoryElement", directoryElement)
Expand Down
8 changes: 3 additions & 5 deletions src/internal/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (m model) Init() tea.Cmd {
// Update function for bubble tea to provide internal communication to the
// application
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
slog.Debug("model.Update() called.", "current width", m.fullWidth,
slog.Debug("model.Update() called.", "current width", m.fullWidth,
"current height", m.fullHeight)
var cmd tea.Cmd

Expand All @@ -70,7 +70,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.updateSidebarState(msg, &cmd)
m.sidebarModel.directories = getDirectories()


// check if there already have listening message
if !ListeningMessage {
slog.Debug("ListeningMessage is false in model Update()")
Expand All @@ -79,7 +78,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

m.getFilePanelItems()

slog.Debug("model.Update() returning.", "current width", m.fullWidth,
slog.Debug("model.Update() returning.", "current width", m.fullWidth,
"current height", m.fullHeight)

return m, tea.Batch(cmd)
Expand Down Expand Up @@ -415,7 +414,6 @@ func listenForChannelMessage(msg chan channelMessage) tea.Cmd {
// Render and update file panel items. Check for changes and updates in files and
// folders in the current directory.
func (m *model) getFilePanelItems() {
slog.Debug("getFilePanelItems() called")
focusPanel := m.fileModel.filePanels[m.filePanelFocusIndex]
for i, filePanel := range m.fileModel.filePanels {
var fileElement []element
Expand Down Expand Up @@ -447,7 +445,7 @@ func (m *model) getFilePanelItems() {
if filePanel.searchBar.Value() != "" {
fileElement = returnFolderElementBySearchString(filePanel.location, m.toggleDotFile, filePanel.searchBar.Value())
} else {
fileElement = returnFolderElement(filePanel.location, m.toggleDotFile, filePanel.sortOptions.data)
fileElement = returnDirectoryElement(filePanel.location, m.toggleDotFile, filePanel.sortOptions.data)
}
// Update file panel list
filePanel.element = fileElement
Expand Down

0 comments on commit ae9a586

Please sign in to comment.