pwsetup v0
This commit is contained in:
276
files/files.go
Normal file
276
files/files.go
Normal file
@@ -0,0 +1,276 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
var (
|
||||
// Global channel to send the current directory to
|
||||
cwdch = make(chan string)
|
||||
|
||||
// Global channel to send info about the current file, and
|
||||
// general status updates. Has to be buffered else the program
|
||||
// enters a deadlock for some reason
|
||||
statusch = make(chan string, 1)
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
// Main application
|
||||
app = tview.NewApplication()
|
||||
|
||||
// Using a Grid layout allows us to put many primitives next to
|
||||
// each other and have them respond to the terminal size
|
||||
mainview = tview.NewGrid()
|
||||
|
||||
// Show the files and directories inside the current, previous,
|
||||
// and next directory panels
|
||||
previousdir = tview.NewTable()
|
||||
currentdir = tview.NewTable()
|
||||
nextdir = tview.NewTable()
|
||||
|
||||
// Small title showing the whole directory name
|
||||
cwd = tview.NewTextView()
|
||||
|
||||
// Simple status bar showing current file permissions and other info
|
||||
fileinfo = tview.NewTextView()
|
||||
)
|
||||
|
||||
// Init widgets
|
||||
initPanels(previousdir, currentdir, nextdir, app)
|
||||
initDirTitle(cwd, app)
|
||||
initFileInfo(fileinfo, app)
|
||||
|
||||
// Set our mainview to resemble ranger
|
||||
mainview.
|
||||
SetBorders(true).
|
||||
SetColumns(10, 0, 40).
|
||||
SetRows(1, 0, 1)
|
||||
|
||||
// Add the widgets
|
||||
mainview.
|
||||
AddItem(cwd, 0, 0, 1, 3, 1, 1, false).
|
||||
AddItem(previousdir, 1, 0, 1, 1, 1, 1, false).
|
||||
AddItem(currentdir, 1, 1, 1, 1, 1, 1, false).
|
||||
AddItem(nextdir, 1, 2, 1, 1, 1, 1, false).
|
||||
AddItem(fileinfo, 2, 0, 1, 3, 1, 1, true)
|
||||
|
||||
// Make the mainview the root widget, and fullscreen it
|
||||
app.SetRoot(mainview, true)
|
||||
|
||||
// Focus on the directory we are in
|
||||
app.SetFocus(currentdir)
|
||||
|
||||
// Run the application
|
||||
err := app.Run()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Preparation to properly initialize each panel upon start
|
||||
func initPanels(previousdir, currentdir, nextdir *tview.Table, app *tview.Application) {
|
||||
// Make each selectable
|
||||
previousdir.SetSelectable(true, false)
|
||||
currentdir.SetSelectable(true, false)
|
||||
nextdir.SetSelectable(true, false)
|
||||
|
||||
// Get the files and dirs of the current and previous directory
|
||||
getDirContents(previousdir, "..")
|
||||
getDirContents(currentdir, ".")
|
||||
|
||||
// Define the function to be called each time we move to a new file or dir
|
||||
preview := func(row, column int) {
|
||||
currentselect := currentdir.GetCell(row, 0).Text
|
||||
previewDir(currentselect, nextdir)
|
||||
}
|
||||
|
||||
// Use it on the first item
|
||||
preview(0, 0)
|
||||
|
||||
// Install it
|
||||
currentdir.SetSelectionChangedFunc(preview)
|
||||
|
||||
// Moving between dirs, and exiting
|
||||
currentdir.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Rune() {
|
||||
case 'h':
|
||||
// Move up
|
||||
err := os.Chdir("..")
|
||||
printError(err)
|
||||
|
||||
copyDirToPanel(currentdir, nextdir)
|
||||
copyDirToPanel(previousdir, currentdir)
|
||||
getDirContents(previousdir, "..")
|
||||
|
||||
// Update directory bar
|
||||
printCwd()
|
||||
|
||||
case 'l':
|
||||
// Move into a dir
|
||||
r, _ := currentdir.GetSelection()
|
||||
currentItem := currentdir.GetCell(r, 0).Text
|
||||
|
||||
// Get info about the current item
|
||||
fi, err := os.Stat(currentItem)
|
||||
printError(err)
|
||||
|
||||
if fi.IsDir() {
|
||||
err = os.Chdir(currentItem)
|
||||
printError(err)
|
||||
|
||||
copyDirToPanel(currentdir, previousdir)
|
||||
copyDirToPanel(nextdir, currentdir)
|
||||
preview(0, 0)
|
||||
}
|
||||
|
||||
// Update directory bar
|
||||
printCwd()
|
||||
|
||||
case 'q':
|
||||
app.Stop()
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func initFileInfo(fi *tview.TextView, app *tview.Application) {
|
||||
// Display colors given by text
|
||||
fi.SetDynamicColors(true)
|
||||
|
||||
// Constantly check and print file permissions etc
|
||||
go func() {
|
||||
var info string
|
||||
|
||||
for {
|
||||
info = <-statusch
|
||||
|
||||
fi.SetText(info)
|
||||
|
||||
app.Draw()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func initDirTitle(cwd *tview.TextView, app *tview.Application) {
|
||||
// Make the title blue and accept colors
|
||||
cwd.SetDynamicColors(true)
|
||||
cwd.SetTextColor(tcell.ColorBlue)
|
||||
|
||||
// Get initial dir
|
||||
wd, err := os.Getwd()
|
||||
printError(err)
|
||||
|
||||
// Check and print info about the current dir
|
||||
go func() {
|
||||
var dir string
|
||||
|
||||
for {
|
||||
dir = <-cwdch
|
||||
|
||||
cwd.SetText("[::b]" + filepath.Dir(dir) + "/[white]" + filepath.Base(dir))
|
||||
|
||||
app.Draw()
|
||||
}
|
||||
}()
|
||||
|
||||
cwdch <- wd
|
||||
}
|
||||
|
||||
func copyDirToPanel(previousdir, currentdir *tview.Table) {
|
||||
// Make sure there are no elements in the panel
|
||||
currentdir.Clear()
|
||||
|
||||
for i := 0; i < previousdir.GetRowCount(); i++ {
|
||||
currentdir.SetCell(i, 0, previousdir.GetCell(i, 0))
|
||||
}
|
||||
}
|
||||
|
||||
func previewDir(path string, paneldir *tview.Table) {
|
||||
// Clean the panel
|
||||
paneldir.Clear()
|
||||
|
||||
// Get info about the given path
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if fi.IsDir() {
|
||||
getDirContents(paneldir, path)
|
||||
}
|
||||
|
||||
// Send info about the current file
|
||||
statusch <- fmt.Sprintf("[fuchsia]%s[white]\t%s\t%s",
|
||||
fi.Mode().String(),
|
||||
fi.ModTime().Round(time.Second).String(),
|
||||
humanize.Bytes(uint64(fi.Size())))
|
||||
}
|
||||
|
||||
func getDirContents(dirtable *tview.Table, path string) {
|
||||
// Make sure to the table is empty
|
||||
dirtable.Clear()
|
||||
|
||||
// Get the contents of the directory
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, f := range files {
|
||||
var color = tcell.ColorWhite
|
||||
|
||||
// Create a new cell for the table, and make sure it takes up
|
||||
// all the horizontal space
|
||||
cell := tview.NewTableCell(f.Name())
|
||||
cell.SetExpansion(1)
|
||||
|
||||
// Colorize according to extension and mode
|
||||
if f.IsDir() {
|
||||
color = tcell.ColorNavy
|
||||
cell.SetAttributes(tcell.AttrBold)
|
||||
}
|
||||
|
||||
switch filepath.Ext(f.Name()) {
|
||||
case ".png", ".jpg":
|
||||
color = tcell.ColorYellow
|
||||
|
||||
case ".mp4", ".mkv", ".avi", ".webm", ".mp3", ".m4a", ".flac":
|
||||
color = tcell.ColorFuchsia
|
||||
|
||||
case ".zip", ".gz", ".iso":
|
||||
color = tcell.ColorRed
|
||||
}
|
||||
|
||||
if f.Mode()&os.ModeSymlink != 0 {
|
||||
color = tcell.ColorTeal
|
||||
}
|
||||
|
||||
cell.SetTextColor(color)
|
||||
|
||||
// Add it to the table on the i row, first column
|
||||
dirtable.SetCell(i, 0, cell)
|
||||
}
|
||||
}
|
||||
|
||||
func printCwd() {
|
||||
dir, err := os.Getwd()
|
||||
printError(err)
|
||||
|
||||
cwdch <- dir
|
||||
}
|
||||
|
||||
func printError(err error) {
|
||||
if err != nil {
|
||||
statusch <- fmt.Sprintf("[red]%s", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user