mirror of
https://github.com/rwinkhart/MUTN.git
synced 2026-08-28 04:46:41 -04:00
197 lines
8.8 KiB
Go
197 lines
8.8 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/rwinkhart/MUTN/src/offline"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// global constants used only in this file
|
|
const (
|
|
ansiAlternateEntryColor = "\033[38;5;8m"
|
|
ansiDirectoryHeader = "\033[38;5;7;48;5;8m"
|
|
ansiEmptyDirectoryWarning = "\033[38;5;11m"
|
|
)
|
|
|
|
// calculates and returns the final visual indentation multiplier (needed to adjust indentation for skipped parent directories) - also subtracts "old" text from directory header
|
|
func indentSubtractor(skippedDirList []bool, dirList []string, currentDirIndex int, indent int) (int, string) {
|
|
var subtractor int // tracks how much to subtract from expected indentation multiplier
|
|
var lastPrefixIndex int // tracks the index (in both skippedDirList and dirList) of the last displayed parent directory
|
|
var trimmedDirectory = dirList[currentDirIndex]
|
|
|
|
for i, skipped := range skippedDirList[:currentDirIndex] { // checks each skipped directory to determine if it is a parent to the current directory
|
|
if strings.HasPrefix(trimmedDirectory, dirList[i]+offline.PathSeparator) { // if the current directory is the child of this iteration's directory...
|
|
if skipped { // ...and this iteration's directory was skipped...
|
|
subtractor++ // increment the subtractor to indicate that the visual indentation should be reduced
|
|
} else {
|
|
lastPrefixIndex = i
|
|
}
|
|
}
|
|
}
|
|
|
|
indent = indent - subtractor // calculates final visual indentation multiplier
|
|
|
|
if indent < 0 { // disallow negative indentation multipliers
|
|
indent = 0
|
|
} else if indent > 0 { // trim the most recently displayed parent directory from the directory header to avoid displaying redundant information
|
|
trimmedDirectory = strings.Replace(trimmedDirectory, dirList[lastPrefixIndex], "", 1)
|
|
}
|
|
|
|
return indent, trimmedDirectory
|
|
}
|
|
|
|
// processing for printing file entries (determines color, line wrapping, and prints)
|
|
func printFileEntry(entry string, lastSlash int, charCounter int, colorAlternator int8, indent int) (int, int8) {
|
|
// determine color to print fileEntryName (alternate each time function is run)
|
|
var colorCode string
|
|
if colorAlternator > 0 {
|
|
colorCode = ""
|
|
} else {
|
|
colorCode = ansiAlternateEntryColor
|
|
}
|
|
colorAlternator = -colorAlternator
|
|
|
|
// trim the containing directory and file extension from the entry to determine fileEntryName
|
|
fileEntryName := entry[lastSlash:]
|
|
fileEntryName = fileEntryName[:len(fileEntryName)-4]
|
|
|
|
if charCounter == 0 { // indent first line of entries for each directory header
|
|
fmt.Print(strings.Repeat(" ", indent*2))
|
|
}
|
|
|
|
// determine whether to wrap to a new line (+1 is to account for trailing spaces)
|
|
charCounter += len(fileEntryName) + 1
|
|
if indentation := indent * 2; charCounter+(indentation) >= width {
|
|
charCounter = len(fileEntryName) + 1
|
|
fmt.Print("\n" + strings.Repeat(" ", indentation)) // indent each line
|
|
}
|
|
|
|
// print fileEntryName to screen
|
|
fmt.Printf("%s%s%s ", colorCode, fileEntryName, offline.AnsiReset)
|
|
|
|
return charCounter, colorAlternator
|
|
}
|
|
|
|
// EntryListGen generates and displays full libmutton entry list
|
|
func EntryListGen() {
|
|
fmt.Print("\n" + ansiBlackOnWhite + "libmutton entries:" + offline.AnsiReset)
|
|
|
|
// define file/directory containing slices so that they may be accessed by the anonymous WalkDir function
|
|
var fileList []string
|
|
var dirList []string
|
|
|
|
// walk entry directory
|
|
_ = filepath.WalkDir(offline.EntryRoot,
|
|
func(fullPath string, entry fs.DirEntry, err error) error {
|
|
|
|
// check for errors encountered while walking directory
|
|
if err != nil {
|
|
// create EntryRoot if the error is the result of it not existing on the system
|
|
if os.IsNotExist(err) {
|
|
_ = os.Mkdir(offline.EntryRoot, 0700)
|
|
dirList = append(dirList, "")
|
|
} else {
|
|
// otherwise, print the source of the error
|
|
fmt.Println(offline.AnsiError + "An unexpected error occurred while generating the entry list: " + err.Error() + offline.AnsiReset)
|
|
}
|
|
// quit walking EntryRoot and return nil to allow the program to continue
|
|
return nil
|
|
}
|
|
|
|
// trim root path from each path before storing
|
|
trimmedPath := fullPath[rootLength:]
|
|
|
|
// create three separate slices for root-level entries, all other entries, and all subdirectories
|
|
// root-level entries get their own slice so that they can be alphabetically sorted without the chance of directories being placed in from of them
|
|
if !entry.IsDir() {
|
|
fileList = append(fileList, trimmedPath)
|
|
} else {
|
|
dirList = append(dirList, trimmedPath)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
// dirList iteration
|
|
dirListLength := len(dirList) // save length for multiple references below
|
|
var skippedDirList = make([]bool, dirListLength) // stores whether each directory was skipped during printout (later used to determine appropriate visual indentation)
|
|
charCounter := 0 // track whether to line-wrap based on character count in line
|
|
var colorAlternator int8 = 1 // track alternating colors for each printed entry name
|
|
var containsSubdirectory bool // indicates whether the current directory contains a subdirectory
|
|
var indent int // visual indentation multiplier
|
|
var vanityDirectory string // directory header printed to end-user - visual only, not used in any processing
|
|
for i, directory := range dirList {
|
|
|
|
// reset formatting variables for new directory
|
|
charCounter = 0
|
|
colorAlternator = 1
|
|
|
|
// default to assuming this directory will be skipped (unless it is the root)
|
|
if i == 0 {
|
|
skippedDirList[i] = false
|
|
} else {
|
|
skippedDirList[i] = true
|
|
}
|
|
|
|
// determine directory's indentation multiplier based on PathSeparator occurrences
|
|
indent = strings.Count(directory, offline.PathSeparator) - 1 // subtract 1 to avoid indenting root-level directories
|
|
|
|
// check if next directory is within the current one
|
|
if dirListLength > i+1 {
|
|
if nextDir := dirList[i+1]; directory == nextDir[:strings.LastIndex(nextDir, offline.PathSeparator)] {
|
|
containsSubdirectory = true
|
|
} else {
|
|
containsSubdirectory = false
|
|
}
|
|
} else {
|
|
containsSubdirectory = false
|
|
}
|
|
|
|
// fileList iteration
|
|
containsFiles := false // indicates whether the current directory contains files (entries)
|
|
for _, file := range fileList {
|
|
|
|
// print the current file if it belongs in the current directory - otherwise, break the loop and move on to the next directory
|
|
if lastSlash := strings.LastIndex(file, offline.PathSeparator) + 1; file[:lastSlash-1] == directory {
|
|
|
|
// print directory header if this is the first run of the loop
|
|
if !containsFiles {
|
|
containsFiles = true
|
|
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
|
indent, vanityDirectory = indentSubtractor(skippedDirList, dirList, i, indent) // calculate the final indentation multiplier
|
|
if !offline.Windows { // for consistency, format directories with UNIX-style path separators on all platforms
|
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+offline.AnsiReset+"\n", vanityDirectory)
|
|
} else {
|
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+offline.AnsiReset+"\n", strings.ReplaceAll(vanityDirectory, offline.PathSeparator, "/"))
|
|
}
|
|
}
|
|
|
|
charCounter, colorAlternator = printFileEntry(file, lastSlash, charCounter, colorAlternator, indent)
|
|
}
|
|
}
|
|
|
|
if !containsFiles { // if the current directory contains no files...
|
|
if !containsSubdirectory { // nor does it contain any subdirectories...
|
|
if dirListLength > 1 { // and directories besides the root-level exist... display directory header and empty directory warning
|
|
skippedDirList[i] = false // the directory header is being printed, indicate that it is not being skipped
|
|
indent, vanityDirectory = indentSubtractor(skippedDirList, dirList, i, indent) // calculate the final indentation multiplier
|
|
if !offline.Windows { // for consistency, format directories with UNIX-style path separators on all platforms
|
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+offline.AnsiReset+"\n", vanityDirectory)
|
|
} else {
|
|
fmt.Printf("\n\n"+strings.Repeat(" ", indent*2)+ansiDirectoryHeader+"%s/"+offline.AnsiReset+"\n", strings.ReplaceAll(vanityDirectory, offline.PathSeparator, "/"))
|
|
}
|
|
fmt.Print(strings.Repeat(" ", indent*2) + ansiEmptyDirectoryWarning + "-empty directory-" + offline.AnsiReset)
|
|
} else { // warn if the only thing that exists is the root-level directory
|
|
fmt.Print("\n\nNothing's here! For help creating your first entry, run \"mutn help\".")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// print trailing new lines for proper spacing after entry list is complete
|
|
fmt.Print("\n\n")
|
|
}
|