├── .SRCINFO ├── .gitignore ├── PKGBUILD ├── README.md ├── go.mod ├── go.sum ├── install.sh ├── main ├── main.go └── research.code /.SRCINFO: -------------------------------------------------------------------------------- 1 | pkgbase = vimgo 2 | pkgdesc = A TUI file manager written in Go, ntegrated with NVIM 3 | pkgver = 1.0.0 4 | pkgrel = 1 5 | url = https://github.com/ethical-buddy/Vimgo 6 | arch = x86_64 7 | license = MIT 8 | source = https://github.com/ethical-buddy/Vimgo/releases/download/v1.0.0/vimgo 9 | sha256sums = SKIP 10 | 11 | pkgname = vimgo 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | *.o 3 | *.so 4 | 5 | # Editor temporary files 6 | .*.swp 7 | *.swo 8 | 9 | # System files 10 | .DS_Store 11 | .git 12 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | pkgname=vimgo 2 | pkgver=1.0.0 3 | pkgrel=1 4 | pkgdesc="A TUI file manager written in Go, ntegrated with NVIM" 5 | arch=('x86_64') 6 | url="https://github.com/ethical-buddy/Vimgo" 7 | license=('MIT') 8 | depends=() 9 | source=("https://github.com/ethical-buddy/Vimgo/releases/download/v${pkgver}/vimgo") 10 | sha256sums=('SKIP') # Replace SKIP with the actual checksum if you want 11 | 12 | package() { 13 | install -Dm755 "$srcdir/vimgo" "$pkgdir/usr/local/bin/vimgo" 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VIMGO : A Terminal-Based File Manager 2 | Overview 3 | FileManager is a terminal-based file manager written in Go. It provides a simple and intuitive way to navigate and manage files and directories in a terminal environment. 4 | 5 | Technical Stack 6 | Programming Language: Go (Golang) 7 | Terminal Library: tcell (github.com/gdamore/tcell/v2) 8 | UI Library: tview (github.com/rivo/tview) 9 | System Calls: os, syscall, and term (golang.org/x/term) 10 | Features 11 | File Navigation: Navigate through directories and files using a simple and intuitive interface. 12 | File Management: Create, delete, and rename files and directories. 13 | Vim Integration: Open files in Vim for editing. 14 | Details View: View detailed information about files and directories, including file type, size, permissions, owner, group, and modification time. 15 | Benefits 16 | Efficient: FileManager provides a fast and efficient way to manage files and directories in a terminal environment. 17 | Intuitive: The simple and intuitive interface makes it easy to use, even for users who are not familiar with terminal-based file managers. 18 | Customizable: The tool can be customized to fit the user's needs and preferences. 19 | Vim Integration: The integration with Vim provides a seamless editing experience. 20 | Getting Started 21 | To get started with FileManager, simply compile and run the tool in your terminal environment. Use the following commands to navigate and manage files and directories: 22 | 23 | Navigation: Use the arrow keys to navigate through directories and files. Press Enter to open a file or directory. 24 | File Management: Use the d key to delete a file or directory, and the r key to rename a file or directory. 25 | Vim Integration: Use the v key to open a file in Vim for editing. 26 | License 27 | FileManager is licensed under the MIT License. See the LICENSE file for more information. 28 | 29 | Contributing 30 | Contributions to FileManager are welcome! If you have a feature request or a bug fix, please submit a pull request on GitHub. 31 | 32 | Acknowledgments 33 | FileManager uses the following open-source libraries: 34 | 35 | tcell (github.com/gdamore/tcell/v2) 36 | tview (github.com/rivo/tview) 37 | 38 | INSTALLATION- 39 | 40 | $chmod +x install.sh 41 | 42 | $./install.sh 43 | 44 | 45 | $vimgo 46 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module filemanager 2 | 3 | go 1.18 4 | 5 | require github.com/rivo/tview v0.0.0-20240625185742-b0a7293b8130 6 | 7 | require ( 8 | github.com/gdamore/encoding v1.0.1 // indirect 9 | github.com/gdamore/tcell/v2 v2.7.4 // indirect 10 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 11 | github.com/mattn/go-runewidth v0.0.15 // indirect 12 | github.com/rivo/uniseg v0.4.7 // indirect 13 | golang.org/x/sys v0.21.0 // indirect 14 | golang.org/x/term v0.21.0 // indirect 15 | golang.org/x/text v0.16.0 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= 2 | github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= 3 | github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= 4 | github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= 5 | github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc= 6 | github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= 7 | github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU= 8 | github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= 9 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 10 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 11 | github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= 12 | github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 13 | github.com/rivo/tview v0.0.0-20240625185742-b0a7293b8130 h1:o1CYtoFOm6xJK3DvDAEG5wDJPLj+SoxUtUDFaQgt1iY= 14 | github.com/rivo/tview v0.0.0-20240625185742-b0a7293b8130/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss= 15 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 16 | github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 17 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 18 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 19 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 20 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 21 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 22 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 23 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 24 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 25 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 26 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 27 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 28 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 29 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 30 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 31 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 32 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 34 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 35 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 36 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= 38 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 39 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 40 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 41 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 42 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 43 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 44 | golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= 45 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 46 | golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= 47 | golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 48 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 49 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 50 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 51 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 52 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 53 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 54 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 55 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 56 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 57 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 58 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 59 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 60 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 61 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Build the file manager 4 | go build main.go 5 | 6 | # Move the executable to /usr/local/bin 7 | sudo mv main /usr/local/bin/vimgo 8 | 9 | # Make the executable runnable by everyone 10 | sudo chmod +x /usr/local/bin/vimgo 11 | 12 | # Add an alias to run the file manager 13 | echo "alias fm='vimgo'" >> ~/.bashrc 14 | source ~/.bashrc 15 | 16 | echo "vimgo installed successfully!" 17 | -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethical-buddy/Vimgo/697f917edcb8201438871e5f29154f49693b6154/main -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "sort" 9 | "strings" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/gdamore/tcell/v2" 14 | "github.com/rivo/tview" 15 | "golang.org/x/term" 16 | ) 17 | 18 | type FileManager struct { 19 | app *tview.Application 20 | list *tview.List 21 | fileDetails *tview.TextView 22 | gitDetails *tview.TextView 23 | commands *tview.TextView 24 | commandInput *tview.InputField // Input field for command mode 25 | path string 26 | items []string 27 | oldState *term.State // To store original terminal state 28 | flex *tview.Flex 29 | vimRunning bool 30 | commandMode bool // Flag to track if command mode is active 31 | } 32 | 33 | func NewFileManager(path string) *FileManager { 34 | fm := &FileManager{ 35 | app: tview.NewApplication(), 36 | list: tview.NewList(), 37 | fileDetails: tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignLeft), 38 | gitDetails: tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignLeft), 39 | commands: tview.NewTextView().SetTextAlign(tview.AlignCenter).SetDynamicColors(true), 40 | commandInput: tview.NewInputField().SetLabel("Command: ").SetFieldWidth(0).SetFieldBackgroundColor(tcell.ColorBlack).SetLabelColor(tcell.ColorYellow), 41 | path: path, 42 | flex: tview.NewFlex(), 43 | } 44 | 45 | fm.loadItems() 46 | return fm 47 | } 48 | 49 | func (fm *FileManager) loadItems() { 50 | fm.list.Clear() 51 | entries, err := os.ReadDir(fm.path) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | var dirs, files []string 57 | for _, entry := range entries { 58 | if entry.IsDir() { 59 | dirs = append(dirs, entry.Name()) 60 | } else { 61 | files = append(files, entry.Name()) 62 | } 63 | } 64 | 65 | sort.Strings(dirs) 66 | sort.Strings(files) 67 | 68 | fm.items = append(dirs, files...) 69 | for _, item := range fm.items { 70 | info, err := os.Stat(filepath.Join(fm.path, item)) 71 | if err != nil { 72 | continue 73 | } 74 | 75 | var color tcell.Color 76 | var symbol string 77 | 78 | switch mode := info.Mode(); { 79 | case mode.IsDir(): 80 | color = tcell.ColorBlue 81 | symbol = "📁" 82 | case (mode & os.ModeSymlink) != 0: 83 | color = tcell.ColorFuchsia 84 | symbol = "🔗" 85 | case (mode & os.ModeNamedPipe) != 0: 86 | color = tcell.ColorYellow 87 | symbol = "📜" 88 | case (mode & os.ModeSocket) != 0: 89 | color = tcell.ColorGreen 90 | symbol = "🔌" 91 | case (mode & os.ModeDevice) != 0: 92 | color = tcell.ColorRed 93 | symbol = "🔧" 94 | default: 95 | color = tcell.ColorWhite 96 | symbol = "📄" 97 | } 98 | 99 | fm.list.AddItem(fmt.Sprintf("%s %s", symbol, item), "", 0, func() { 100 | fm.navigate(item) 101 | }).SetMainTextColor(color) 102 | } 103 | 104 | // Update both file and git details simultaneously 105 | fm.updateDetails() 106 | fm.updateGitDetails() 107 | } 108 | 109 | func isGitRepo(path string) bool { 110 | gitPath := filepath.Join(path, ".git") 111 | stat, err := os.Stat(gitPath) 112 | if err != nil { 113 | return false 114 | } 115 | return stat.IsDir() || (stat.Mode()&os.ModeSymlink != 0) 116 | } 117 | 118 | func (fm *FileManager) navigate(item string) { 119 | newPath := filepath.Join(fm.path, item) 120 | info, err := os.Stat(newPath) 121 | if err != nil { 122 | return 123 | } 124 | 125 | if info.IsDir() { 126 | fm.path = newPath 127 | } else if item == ".." { 128 | fm.path = filepath.Dir(fm.path) 129 | } else { 130 | fm.openInVim(newPath) 131 | return 132 | } 133 | 134 | fm.loadItems() 135 | } 136 | 137 | func (fm *FileManager) openInVim(filepath string) { 138 | if fm.vimRunning { 139 | return 140 | } 141 | fm.vimRunning = true 142 | 143 | oldState, err := term.GetState(int(os.Stdin.Fd())) 144 | if err != nil { 145 | panic(err) 146 | } 147 | fm.oldState = oldState 148 | 149 | fm.app.Suspend(func() { 150 | cmd := exec.Command("nvim", filepath) 151 | cmd.Stdin = os.Stdin 152 | cmd.Stdout = os.Stdout 153 | cmd.Stderr = os.Stderr 154 | 155 | _ = cmd.Run() 156 | 157 | if fm.oldState != nil { 158 | _ = term.Restore(int(os.Stdin.Fd()), fm.oldState) 159 | } 160 | 161 | fm.vimRunning = false 162 | }) 163 | 164 | fm.loadItems() 165 | } 166 | 167 | func (fm *FileManager) updateDetails() { 168 | selectedIndex := fm.list.GetCurrentItem() 169 | if selectedIndex < 0 || selectedIndex >= len(fm.items) { 170 | fm.fileDetails.SetText("") 171 | return 172 | } 173 | 174 | selectedItem := fm.items[selectedIndex] 175 | fullPath := filepath.Join(fm.path, selectedItem) 176 | 177 | info, err := os.Stat(fullPath) 178 | if err != nil { 179 | fm.fileDetails.SetText(fmt.Sprintf("Error retrieving details: %v", err)) 180 | return 181 | } 182 | 183 | fileType := "File" 184 | if info.IsDir() { 185 | fileType = "Directory" 186 | } else if (info.Mode() & os.ModeSymlink) != 0 { 187 | fileType = "Symlink" 188 | } else if (info.Mode() & os.ModeNamedPipe) != 0 { 189 | fileType = "Named Pipe" 190 | } else if (info.Mode() & os.ModeSocket) != 0 { 191 | fileType = "Socket" 192 | } else if (info.Mode() & os.ModeDevice) != 0 { 193 | fileType = "Device" 194 | } 195 | 196 | permissions := info.Mode().String() 197 | owner := info.Sys().(*syscall.Stat_t).Uid 198 | group := info.Sys().(*syscall.Stat_t).Gid 199 | modTime := info.ModTime().Format(time.RFC1123) 200 | size := info.Size() 201 | 202 | details := fmt.Sprintf( 203 | "[yellow]Name:[white] %s\n[yellow]Type:[white] %s\n[yellow]Size:[white] %d bytes\n[yellow]Permissions:[white] %s\n[yellow]Owner:[white] %d\n[yellow]Group:[white] %d\n[yellow]Modified:[white] %s\n", 204 | info.Name(), 205 | fileType, 206 | size, 207 | permissions, 208 | owner, 209 | group, 210 | modTime, 211 | ) 212 | fm.fileDetails.SetText(details) 213 | } 214 | 215 | func (fm *FileManager) deleteSelectedItem() { 216 | selectedIndex := fm.list.GetCurrentItem() 217 | if selectedIndex < 0 || selectedIndex >= len(fm.items) { 218 | return 219 | } 220 | 221 | selectedItem := fm.items[selectedIndex] 222 | fullPath := filepath.Join(fm.path, selectedItem) 223 | 224 | err := os.RemoveAll(fullPath) // Remove files or directories 225 | if err != nil { 226 | fm.fileDetails.SetText(fmt.Sprintf("Error deleting file: %v", err)) 227 | return 228 | } 229 | 230 | fm.loadItems() 231 | } 232 | 233 | func (fm *FileManager) updateGitDetails() { 234 | if !isGitRepo(fm.path) { 235 | fm.gitDetails.SetText("") 236 | return 237 | } 238 | 239 | gitCommits := fm.gitLog() 240 | 241 | // Set Git details with commit hashes and messages 242 | fm.gitDetails.SetText(gitCommits) 243 | } 244 | 245 | func (fm *FileManager) gitLog() string { 246 | cmd := exec.Command("git", "-C", fm.path, "log", "--oneline", "-10") 247 | output, err := cmd.CombinedOutput() 248 | if err != nil { 249 | return fmt.Sprintf("Error getting git log: %v", err) 250 | } 251 | 252 | // Process git log output to include commit hashes and messages 253 | lines := strings.Split(string(output), "\n") 254 | var result strings.Builder 255 | for _, line := range lines { 256 | if line != "" { 257 | parts := strings.SplitN(line, " ", 2) 258 | if len(parts) >= 2 { 259 | result.WriteString(fmt.Sprintf("[yellow]%s [white]%s\n", parts[0], parts[1])) 260 | } 261 | } 262 | } 263 | return result.String() 264 | } 265 | 266 | func (fm *FileManager) run() { 267 | fm.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 268 | fm.navigate(mainText) 269 | }) 270 | 271 | fm.list.SetChangedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 272 | fm.updateDetails() 273 | fm.updateGitDetails() 274 | }) 275 | 276 | fm.commandInput.SetDoneFunc(func(key tcell.Key) { 277 | if key == tcell.KeyEscape { 278 | fm.commandMode = false 279 | fm.app.SetFocus(fm.list) // Return focus to list after exiting command mode 280 | } else if key == tcell.KeyEnter { 281 | // Process the command input 282 | command := fm.commandInput.GetText() 283 | fm.processCommand(command) 284 | fm.commandInput.SetText("") // Clear input field after processing command 285 | } 286 | }) 287 | 288 | fm.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 289 | if fm.commandMode { 290 | return event 291 | } 292 | 293 | switch event.Key() { 294 | case tcell.KeyBackspace, tcell.KeyLeft: 295 | if fm.path != filepath.VolumeName(fm.path) && fm.path != "/" { 296 | fm.path = filepath.Dir(fm.path) 297 | fm.loadItems() 298 | } 299 | return nil 300 | case tcell.KeyEnter, tcell.KeyRight: 301 | index := fm.list.GetCurrentItem() 302 | if index >= 0 && index < len(fm.items) { 303 | fm.navigate(fm.items[index]) 304 | } 305 | return nil 306 | case tcell.KeyCtrlD: 307 | fm.deleteSelectedItem() 308 | return nil 309 | case tcell.KeyRune: 310 | switch event.Rune() { 311 | case ':': 312 | fm.commandMode = true 313 | fm.app.SetFocus(fm.commandInput) // Switch focus to command input 314 | return nil 315 | case 'q': 316 | fm.app.Stop() 317 | return nil 318 | } 319 | } 320 | return event 321 | }) 322 | 323 | fm.commands.SetText("[red]Navigate: [yellow]← Back, → Enter, [green]Ctrl+D Delete, [blue]: Command, [blue]q Quit") 324 | 325 | // Adjust layout to have the commands at the bottom center 326 | fm.flex.SetDirection(tview.FlexRow). 327 | AddItem( 328 | tview.NewFlex(). 329 | SetDirection(tview.FlexColumn). 330 | AddItem(fm.list, 0, 1, true). 331 | AddItem(tview.NewTextView().SetText("").SetTextAlign(tview.AlignLeft), 1, 1, false). 332 | AddItem(fm.fileDetails, 0, 2, false). 333 | AddItem(tview.NewTextView().SetText("").SetTextAlign(tview.AlignLeft), 1, 1, false). 334 | AddItem(fm.gitDetails, 0, 2, false), 0, 1, true). 335 | AddItem(fm.commands, 3, 1, false). // Use a height of 3 for the commands 336 | AddItem(fm.commandInput, 1, 1, false) // Add command input field at the bottom 337 | 338 | fm.app.SetRoot(fm.flex, true) 339 | 340 | // Set a dark black background color 341 | fm.app.SetBeforeDrawFunc(func(screen tcell.Screen) bool { 342 | screen.Clear() 343 | bgStyle := tcell.StyleDefault.Background(tcell.ColorBlack) 344 | screen.SetStyle(bgStyle) 345 | return false 346 | }) 347 | 348 | if err := fm.app.Run(); err != nil { 349 | panic(err) 350 | } 351 | } 352 | 353 | func (fm *FileManager) processCommand(command string) { 354 | command = strings.TrimSpace(command) 355 | if command == "" { 356 | return 357 | } 358 | 359 | parts := strings.Fields(command) 360 | cmd := parts[0] 361 | args := parts[1:] 362 | 363 | switch cmd { 364 | case "mkdir": 365 | if len(args) < 1 { 366 | fm.printCommandOutput("Usage: mkdir ") 367 | return 368 | } 369 | dirName := args[0] 370 | err := os.Mkdir(filepath.Join(fm.path, dirName), 0755) 371 | if err != nil { 372 | fm.printCommandOutput(fmt.Sprintf("Error creating directory: %v", err)) 373 | } else { 374 | fm.printCommandOutput(fmt.Sprintf("Created directory '%s'", dirName)) 375 | fm.loadItems() 376 | } 377 | case "touch": 378 | if len(args) < 1 { 379 | fm.printCommandOutput("Usage: touch ") 380 | return 381 | } 382 | fileName := args[0] 383 | filePath := filepath.Join(fm.path, fileName) 384 | file, err := os.Create(filePath) 385 | if err != nil { 386 | fm.printCommandOutput(fmt.Sprintf("Error creating file: %v", err)) 387 | } else { 388 | file.Close() 389 | fm.printCommandOutput(fmt.Sprintf("Created file '%s'", fileName)) 390 | fm.loadItems() 391 | } 392 | case "ls": 393 | cmd := exec.Command("ls", args...) 394 | output, err := cmd.CombinedOutput() 395 | if err != nil { 396 | fm.printCommandOutput(fmt.Sprintf("Error running command: %v", err)) 397 | return 398 | } 399 | fm.printCommandOutput(string(output)) 400 | default: 401 | fm.printCommandOutput(fmt.Sprintf("Unknown command: %s", cmd)) 402 | } 403 | } 404 | 405 | func (fm *FileManager) printCommandOutput(output string) { 406 | fm.fileDetails.SetText(output) 407 | } 408 | 409 | func main() { 410 | fm := NewFileManager(".") 411 | fm.run() 412 | } 413 | -------------------------------------------------------------------------------- /research.code: -------------------------------------------------------------------------------- 1 | // THIS CODE PROVIDES MORE INFORMATION BUT NO COLORS AND SYMBOLS 2 | // package main 3 | 4 | // import ( 5 | // "fmt" 6 | // "os" 7 | // "os/exec" 8 | // "path/filepath" 9 | // "sort" 10 | // "syscall" 11 | // "time" 12 | 13 | // "github.com/gdamore/tcell/v2" 14 | // "github.com/rivo/tview" 15 | // "golang.org/x/term" 16 | // ) 17 | 18 | // type FileManager struct { 19 | // app *tview.Application 20 | // list *tview.List 21 | // details *tview.TextView 22 | // path string 23 | // items []string 24 | // oldState *term.State // To store original terminal state 25 | // flex *tview.Flex 26 | // vimView *tview.TextView 27 | // vimRunning bool 28 | // } 29 | 30 | // func NewFileManager(path string) *FileManager { 31 | // fm := &FileManager{ 32 | // app: tview.NewApplication(), 33 | // list: tview.NewList(), 34 | // details: tview.NewTextView(), 35 | // path: path, 36 | // flex: tview.NewFlex(), 37 | // } 38 | 39 | // fm.loadItems() 40 | // return fm 41 | // } 42 | 43 | // func (fm *FileManager) loadItems() { 44 | // fm.list.Clear() 45 | // entries, err := os.ReadDir(fm.path) 46 | // if err != nil { 47 | // panic(err) 48 | // } 49 | 50 | // var dirs, files []string 51 | // for _, entry := range entries { 52 | // if entry.IsDir() { 53 | // dirs = append(dirs, entry.Name()) 54 | // } else { 55 | // files = append(files, entry.Name()) 56 | // } 57 | // } 58 | 59 | // sort.Strings(dirs) 60 | // sort.Strings(files) 61 | 62 | // fm.items = append(dirs, files...) 63 | // for _, item := range fm.items { 64 | // color := tcell.ColorWhite 65 | // info, err := os.Stat(filepath.Join(fm.path, item)) 66 | // if err != nil { 67 | // continue 68 | // } 69 | // if info.IsDir() { 70 | // color = tcell.ColorBlue 71 | // } else if (info.Mode() & os.ModeSymlink) != 0 { 72 | // color = tcell.ColorFuchsia 73 | // } 74 | // fm.list.AddItem(item, "", 0, nil).SetMainTextColor(color) 75 | // } 76 | 77 | // fm.updateDetails() 78 | // } 79 | 80 | // func (fm *FileManager) navigate(item string) { 81 | // newPath := filepath.Join(fm.path, item) 82 | // info, err := os.Stat(newPath) 83 | // if err != nil { 84 | // return 85 | // } 86 | 87 | // if info.IsDir() { 88 | // fm.path = newPath 89 | // fm.loadItems() 90 | // } else { 91 | // fm.openInVim(newPath) 92 | // } 93 | // } 94 | 95 | // func (fm *FileManager) openInVim(filepath string) { 96 | // if fm.vimRunning { 97 | // return 98 | // } 99 | 100 | // fm.vimRunning = true 101 | 102 | // // Save current terminal state 103 | // oldState, err := term.GetState(int(os.Stdin.Fd())) 104 | // if err != nil { 105 | // panic(err) 106 | // } 107 | // fm.oldState = oldState 108 | 109 | // // Exit the tview application before opening Vim 110 | // fm.app.Suspend(func() { 111 | // cmd := exec.Command("vim", filepath) 112 | // cmd.Stdin = os.Stdin 113 | // cmd.Stdout = os.Stdout 114 | // cmd.Stderr = os.Stderr 115 | 116 | // if err := cmd.Run(); err != nil { 117 | // panic(err) 118 | // } 119 | 120 | // if fm.oldState != nil { 121 | // if err := term.Restore(int(os.Stdin.Fd()), fm.oldState); err != nil { 122 | // panic(err) 123 | // } 124 | // } 125 | 126 | // fm.vimRunning = false 127 | // fm.loadItems() // Reload items after returning from Vim 128 | // }) 129 | // } 130 | 131 | // func (fm *FileManager) updateDetails() { 132 | // selectedIndex := fm.list.GetCurrentItem() 133 | // if selectedIndex < 0 || selectedIndex >= len(fm.items) { 134 | // fm.details.SetText("") 135 | // return 136 | // } 137 | 138 | // selectedItem := fm.items[selectedIndex] 139 | // fullPath := filepath.Join(fm.path, selectedItem) 140 | 141 | // info, err := os.Stat(fullPath) 142 | // if err != nil { 143 | // fm.details.SetText(fmt.Sprintf("Error retrieving details: %v", err)) 144 | // return 145 | // } 146 | 147 | // fileType := "File" 148 | // if info.IsDir() { 149 | // fileType = "Directory" 150 | // } else if (info.Mode() & os.ModeSymlink) != 0 { 151 | // fileType = "Symlink" 152 | // } else if (info.Mode() & os.ModeNamedPipe) != 0 { 153 | // fileType = "Named Pipe" 154 | // } else if (info.Mode() & os.ModeSocket) != 0 { 155 | // fileType = "Socket" 156 | // } else if (info.Mode() & os.ModeDevice) != 0 { 157 | // fileType = "Device" 158 | // } 159 | 160 | // permissions := info.Mode().String() 161 | // owner := info.Sys().(*syscall.Stat_t).Uid 162 | // group := info.Sys().(*syscall.Stat_t).Gid 163 | // modTime := info.ModTime().Format(time.RFC1123) 164 | // size := info.Size() 165 | 166 | // details := fmt.Sprintf( 167 | // "Name: %s\nType: %s\nSize: %d bytes\nPermissions: %s\nOwner: %d\nGroup: %d\nModified: %s\n", 168 | // info.Name(), 169 | // fileType, 170 | // size, 171 | // permissions, 172 | // owner, 173 | // group, 174 | // modTime, 175 | // ) 176 | // fm.details.SetText(details) 177 | // } 178 | 179 | // func (fm *FileManager) deleteSelectedItem() { 180 | // selectedIndex := fm.list.GetCurrentItem() 181 | // if selectedIndex < 0 || selectedIndex >= len(fm.items) { 182 | // return 183 | // } 184 | 185 | // selectedItem := fm.items[selectedIndex] 186 | // fullPath := filepath.Join(fm.path, selectedItem) 187 | 188 | // err := os.RemoveAll(fullPath) // Remove files or directories 189 | // if err != nil { 190 | // fm.details.SetText(fmt.Sprintf("Error deleting file: %v", err)) 191 | // return 192 | // } 193 | 194 | // fm.loadItems() 195 | // } 196 | 197 | // func (fm *FileManager) run() { 198 | // fm.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 199 | // fm.navigate(mainText) 200 | // }) 201 | 202 | // fm.list.SetChangedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 203 | // fm.updateDetails() 204 | // }) 205 | 206 | // fm.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 207 | // if fm.vimRunning { 208 | // return event 209 | // } 210 | 211 | // switch event.Key() { 212 | // case tcell.KeyBackspace, tcell.KeyBackspace2: 213 | // fm.path = filepath.Dir(fm.path) 214 | // fm.loadItems() 215 | // return nil 216 | // case tcell.KeyCtrlD: 217 | // fm.deleteSelectedItem() 218 | // return nil 219 | // case tcell.KeyRune: 220 | // switch event.Rune() { 221 | // case 'q': 222 | // fm.app.Stop() 223 | // return nil 224 | // } 225 | // } 226 | // return event 227 | // }) 228 | 229 | // fm.flex.SetDirection(tview.FlexColumn). 230 | // AddItem(fm.list, 0, 1, true). 231 | // AddItem(fm.details, 0, 1, false) 232 | 233 | // fm.app.SetRoot(fm.flex, true) 234 | 235 | // if err := fm.app.Run(); err != nil { 236 | // panic(err) 237 | // } 238 | // } 239 | 240 | // func main() { 241 | // fm := NewFileManager(".") 242 | // fm.run() 243 | // } 244 | ////////////////////////////////////////////////// 245 | //////////////////////////////////////////// 246 | //////////////////////////////////////// 247 | /////////////////////////////// 248 | // THIS CODE HAS SYMBOLS AND COLORS . 249 | // package main 250 | 251 | // import ( 252 | // "fmt" 253 | // "os" 254 | // "os/exec" 255 | // "path/filepath" 256 | // "sort" 257 | // "syscall" 258 | // "time" 259 | 260 | // "github.com/gdamore/tcell/v2" 261 | // "github.com/rivo/tview" 262 | // "golang.org/x/term" 263 | // ) 264 | 265 | // type FileManager struct { 266 | // app *tview.Application 267 | // list *tview.List 268 | // details *tview.TextView 269 | // path string 270 | // items []string 271 | // oldState *term.State // To store original terminal state 272 | // flex *tview.Flex 273 | // vimView *tview.TextView 274 | // vimRunning bool 275 | // } 276 | 277 | // func NewFileManager(path string) *FileManager { 278 | // fm := &FileManager{ 279 | // app: tview.NewApplication(), 280 | // list: tview.NewList(), 281 | // details: tview.NewTextView(), 282 | // path: path, 283 | // flex: tview.NewFlex(), 284 | // } 285 | 286 | // fm.loadItems() 287 | // return fm 288 | // } 289 | 290 | // func (fm *FileManager) loadItems() { 291 | // fm.list.Clear() 292 | // entries, err := os.ReadDir(fm.path) 293 | // if err != nil { 294 | // panic(err) 295 | // } 296 | 297 | // var dirs, files []string 298 | // for _, entry := range entries { 299 | // if entry.IsDir() { 300 | // dirs = append(dirs, entry.Name()) 301 | // } else { 302 | // files = append(files, entry.Name()) 303 | // } 304 | // } 305 | 306 | // sort.Strings(dirs) 307 | // sort.Strings(files) 308 | 309 | // fm.items = append(dirs, files...) 310 | // for _, item := range fm.items { 311 | // info, err := os.Stat(filepath.Join(fm.path, item)) 312 | // if err != nil { 313 | // continue 314 | // } 315 | 316 | // var color tcell.Color 317 | // var symbol string 318 | 319 | // switch mode := info.Mode(); { 320 | // case mode.IsDir(): 321 | // color = tcell.ColorBlue 322 | // symbol = "📁" 323 | // case (mode & os.ModeSymlink) != 0: 324 | // color = tcell.ColorFuchsia 325 | // symbol = "🔗" 326 | // case (mode & os.ModeNamedPipe) != 0: 327 | // color = tcell.ColorYellow 328 | // symbol = "📜" 329 | // case (mode & os.ModeSocket) != 0: 330 | // color = tcell.ColorGreen 331 | // symbol = "🔌" 332 | // case (mode & os.ModeDevice) != 0: 333 | // color = tcell.ColorRed 334 | // symbol = "🔧" 335 | // default: 336 | // color = tcell.ColorWhite 337 | // symbol = "📄" 338 | // } 339 | 340 | // fm.list.AddItem(fmt.Sprintf("%s %s", symbol, item), "", 0, func() { 341 | // fm.navigate(item) 342 | // }).SetMainTextColor(color) 343 | // } 344 | 345 | // fm.updateDetails() 346 | // } 347 | 348 | // func (fm *FileManager) navigate(item string) { 349 | // newPath := filepath.Join(fm.path, item) 350 | // info, err := os.Stat(newPath) 351 | // if err != nil { 352 | // return 353 | // } 354 | 355 | // if info.IsDir() { 356 | // fm.path = newPath 357 | // fm.loadItems() 358 | // } else { 359 | // fm.openInVim(newPath) 360 | // } 361 | // } 362 | 363 | // func (fm *FileManager) openInVim(filepath string) { 364 | // if fm.vimRunning { 365 | // return 366 | // } 367 | 368 | // fm.vimRunning = true 369 | 370 | // // Save current terminal state 371 | // oldState, err := term.GetState(int(os.Stdin.Fd())) 372 | // if err != nil { 373 | // panic(err) 374 | // } 375 | // fm.oldState = oldState 376 | 377 | // // Exit the tview application before opening Vim 378 | // fm.app.Suspend(func() { 379 | // cmd := exec.Command("vim", filepath) 380 | // cmd.Stdin = os.Stdin 381 | // cmd.Stdout = os.Stdout 382 | // cmd.Stderr = os.Stderr 383 | 384 | // if err := cmd.Run(); err != nil { 385 | // panic(err) 386 | // } 387 | 388 | // if fm.oldState != nil { 389 | // if err := term.Restore(int(os.Stdin.Fd()), fm.oldState); err != nil { 390 | // panic(err) 391 | // } 392 | // } 393 | 394 | // fm.vimRunning = false 395 | // fm.loadItems() // Reload items after returning from Vim 396 | // }) 397 | // } 398 | 399 | // func (fm *FileManager) updateDetails() { 400 | // selectedIndex := fm.list.GetCurrentItem() 401 | // if selectedIndex < 0 || selectedIndex >= len(fm.items) { 402 | // fm.details.SetText("") 403 | // return 404 | // } 405 | 406 | // selectedItem := fm.items[selectedIndex] 407 | // fullPath := filepath.Join(fm.path, selectedItem) 408 | 409 | // info, err := os.Stat(fullPath) 410 | // if err != nil { 411 | // fm.details.SetText(fmt.Sprintf("Error retrieving details: %v", err)) 412 | // return 413 | // } 414 | 415 | // fileType := "File" 416 | // if info.IsDir() { 417 | // fileType = "Directory" 418 | // } else if (info.Mode() & os.ModeSymlink) != 0 { 419 | // fileType = "Symlink" 420 | // } else if (info.Mode() & os.ModeNamedPipe) != 0 { 421 | // fileType = "Named Pipe" 422 | // } else if (info.Mode() & os.ModeSocket) != 0 { 423 | // fileType = "Socket" 424 | // } else if (info.Mode() & os.ModeDevice) != 0 { 425 | // fileType = "Device" 426 | // } 427 | 428 | // permissions := info.Mode().String() 429 | // owner := info.Sys().(*syscall.Stat_t).Uid 430 | // group := info.Sys().(*syscall.Stat_t).Gid 431 | // modTime := info.ModTime().Format(time.RFC1123) 432 | // size := info.Size() 433 | 434 | // details := fmt.Sprintf( 435 | // "Name: %s\nType: %s\nSize: %d bytes\nPermissions: %s\nOwner: %d\nGroup: %d\nModified: %s\n", 436 | // info.Name(), 437 | // fileType, 438 | // size, 439 | // permissions, 440 | // owner, 441 | // group, 442 | // modTime, 443 | // ) 444 | // fm.details.SetText(details) 445 | // } 446 | 447 | // func (fm *FileManager) deleteSelectedItem() { 448 | // selectedIndex := fm.list.GetCurrentItem() 449 | // if selectedIndex < 0 || selectedIndex >= len(fm.items) { 450 | // return 451 | // } 452 | 453 | // selectedItem := fm.items[selectedIndex] 454 | // fullPath := filepath.Join(fm.path, selectedItem) 455 | 456 | // err := os.RemoveAll(fullPath) // Remove files or directories 457 | // if err != nil { 458 | // fm.details.SetText(fmt.Sprintf("Error deleting file: %v", err)) 459 | // return 460 | // } 461 | 462 | // fm.loadItems() 463 | // } 464 | 465 | // func (fm *FileManager) run() { 466 | // fm.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 467 | // fm.navigate(mainText) 468 | // }) 469 | 470 | // fm.list.SetChangedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 471 | // fm.updateDetails() 472 | // }) 473 | 474 | // fm.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 475 | // if fm.vimRunning { 476 | // return event 477 | // } 478 | 479 | // switch event.Key() { 480 | // case tcell.KeyBackspace, tcell.KeyBackspace2: 481 | // fm.path = filepath.Dir(fm.path) 482 | // fm.loadItems() 483 | // return nil 484 | // case tcell.KeyCtrlD: 485 | // fm.deleteSelectedItem() 486 | // return nil 487 | // case tcell.KeyRune: 488 | // switch event.Rune() { 489 | // case 'q': 490 | // fm.app.Stop() 491 | // return nil 492 | // } 493 | // } 494 | // return event 495 | // }) 496 | 497 | // fm.flex.SetDirection(tview.FlexRow). 498 | // AddItem(fm.list, 0, 1, true). 499 | // AddItem(fm.details, 3, 1, false) 500 | 501 | // fm.app.SetRoot(fm.flex, true) 502 | 503 | // if err := fm.app.Run(); err != nil { 504 | // panic(err) 505 | // } 506 | // } 507 | 508 | // func main() { 509 | // fm := NewFileManager(".") 510 | // fm.run() 511 | // } 512 | //////////////////////////////////////////// 513 | ////////////////////////////////////////// 514 | /////////////////////////////////////// 515 | /////////////////////////// 516 | 517 | // package main 518 | 519 | // import ( 520 | // "fmt" 521 | // "os" 522 | // "os/exec" 523 | // "path/filepath" 524 | // "sort" 525 | // "strings" 526 | // "syscall" 527 | // "time" 528 | 529 | // "github.com/gdamore/tcell/v2" 530 | // "github.com/rivo/tview" 531 | // "golang.org/x/term" 532 | // ) 533 | 534 | // type FileManager struct { 535 | // app *tview.Application 536 | // list *tview.List 537 | // fileDetails *tview.TextView 538 | // gitDetails *tview.TextView 539 | // commands *tview.TextView 540 | // path string 541 | // items []string 542 | // oldState *term.State // To store original terminal state 543 | // flex *tview.Flex 544 | // vimRunning bool 545 | // } 546 | 547 | // func NewFileManager(path string) *FileManager { 548 | // fm := &FileManager{ 549 | // app: tview.NewApplication(), 550 | // list: tview.NewList(), 551 | // fileDetails: tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignLeft), 552 | // gitDetails: tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignLeft), 553 | // commands: tview.NewTextView().SetTextAlign(tview.AlignCenter).SetDynamicColors(true), 554 | // path: path, 555 | // flex: tview.NewFlex(), 556 | // } 557 | 558 | // fm.loadItems() 559 | // return fm 560 | // } 561 | 562 | // func (fm *FileManager) loadItems() { 563 | // fm.list.Clear() 564 | // entries, err := os.ReadDir(fm.path) 565 | // if err != nil { 566 | // panic(err) 567 | // } 568 | 569 | // var dirs, files []string 570 | // for _, entry := range entries { 571 | // if entry.IsDir() { 572 | // dirs = append(dirs, entry.Name()) 573 | // } else { 574 | // files = append(files, entry.Name()) 575 | // } 576 | // } 577 | 578 | // sort.Strings(dirs) 579 | // sort.Strings(files) 580 | 581 | // fm.items = append(dirs, files...) 582 | // for _, item := range fm.items { 583 | // info, err := os.Stat(filepath.Join(fm.path, item)) 584 | // if err != nil { 585 | // continue 586 | // } 587 | 588 | // var color tcell.Color 589 | // var symbol string 590 | 591 | // switch mode := info.Mode(); { 592 | // case mode.IsDir(): 593 | // color = tcell.ColorBlue 594 | // symbol = "📁" 595 | // case (mode & os.ModeSymlink) != 0: 596 | // color = tcell.ColorFuchsia 597 | // symbol = "🔗" 598 | // case (mode & os.ModeNamedPipe) != 0: 599 | // color = tcell.ColorYellow 600 | // symbol = "📜" 601 | // case (mode & os.ModeSocket) != 0: 602 | // color = tcell.ColorGreen 603 | // symbol = "🔌" 604 | // case (mode & os.ModeDevice) != 0: 605 | // color = tcell.ColorRed 606 | // symbol = "🔧" 607 | // default: 608 | // color = tcell.ColorWhite 609 | // symbol = "📄" 610 | // } 611 | 612 | // fm.list.AddItem(fmt.Sprintf("%s %s", symbol, item), "", 0, func() { 613 | // fm.navigate(item) 614 | // }).SetMainTextColor(color) 615 | // } 616 | 617 | // // Update both file and git details simultaneously 618 | // fm.updateDetails() 619 | // fm.updateGitDetails() 620 | // } 621 | 622 | // func isGitRepo(path string) bool { 623 | // gitPath := filepath.Join(path, ".git") 624 | // stat, err := os.Stat(gitPath) 625 | // if err != nil { 626 | // return false 627 | // } 628 | // return stat.IsDir() || (stat.Mode()&os.ModeSymlink != 0) 629 | // } 630 | 631 | // func (fm *FileManager) navigate(item string) { 632 | // newPath := filepath.Join(fm.path, item) 633 | // info, err := os.Stat(newPath) 634 | // if err != nil { 635 | // return 636 | // } 637 | 638 | // if info.IsDir() { 639 | // fm.path = newPath 640 | // fm.loadItems() 641 | // } else { 642 | // fm.openInVim(newPath) 643 | // } 644 | // } 645 | 646 | // func (fm *FileManager) openInVim(filepath string) { 647 | // if fm.vimRunning { 648 | // return 649 | // } 650 | 651 | // fm.vimRunning = true 652 | 653 | // // Save current terminal state 654 | // oldState, err := term.GetState(int(os.Stdin.Fd())) 655 | // if err != nil { 656 | // panic(err) 657 | // } 658 | // fm.oldState = oldState 659 | 660 | // // Exit the tview application before opening Vim 661 | // fm.app.Suspend(func() { 662 | // cmd := exec.Command("vim", filepath) 663 | // cmd.Stdin = os.Stdin 664 | // cmd.Stdout = os.Stdout 665 | // cmd.Stderr = os.Stderr 666 | 667 | // if err := cmd.Run(); err != nil { 668 | // panic(err) 669 | // } 670 | 671 | // if fm.oldState != nil { 672 | // if err := term.Restore(int(os.Stdin.Fd()), fm.oldState); err != nil { 673 | // panic(err) 674 | // } 675 | // } 676 | 677 | // fm.vimRunning = false 678 | // fm.loadItems() // Reload items after returning from Vim 679 | // }) 680 | // } 681 | 682 | // func (fm *FileManager) updateDetails() { 683 | // selectedIndex := fm.list.GetCurrentItem() 684 | // if selectedIndex < 0 || selectedIndex >= len(fm.items) { 685 | // fm.fileDetails.SetText("") 686 | // return 687 | // } 688 | 689 | // selectedItem := fm.items[selectedIndex] 690 | // fullPath := filepath.Join(fm.path, selectedItem) 691 | 692 | // info, err := os.Stat(fullPath) 693 | // if err != nil { 694 | // fm.fileDetails.SetText(fmt.Sprintf("Error retrieving details: %v", err)) 695 | // return 696 | // } 697 | 698 | // fileType := "File" 699 | // if info.IsDir() { 700 | // fileType = "Directory" 701 | // } else if (info.Mode() & os.ModeSymlink) != 0 { 702 | // fileType = "Symlink" 703 | // } else if (info.Mode() & os.ModeNamedPipe) != 0 { 704 | // fileType = "Named Pipe" 705 | // } else if (info.Mode() & os.ModeSocket) != 0 { 706 | // fileType = "Socket" 707 | // } else if (info.Mode() & os.ModeDevice) != 0 { 708 | // fileType = "Device" 709 | // } 710 | 711 | // permissions := info.Mode().String() 712 | // owner := info.Sys().(*syscall.Stat_t).Uid 713 | // group := info.Sys().(*syscall.Stat_t).Gid 714 | // modTime := info.ModTime().Format(time.RFC1123) 715 | // size := info.Size() 716 | 717 | // details := fmt.Sprintf( 718 | // "[yellow]Name:[white] %s\n[yellow]Type:[white] %s\n[yellow]Size:[white] %d bytes\n[yellow]Permissions:[white] %s\n[yellow]Owner:[white] %d\n[yellow]Group:[white] %d\n[yellow]Modified:[white] %s\n", 719 | // info.Name(), 720 | // fileType, 721 | // size, 722 | // permissions, 723 | // owner, 724 | // group, 725 | // modTime, 726 | // ) 727 | // fm.fileDetails.SetText(details) 728 | // } 729 | 730 | // func (fm *FileManager) deleteSelectedItem() { 731 | // selectedIndex := fm.list.GetCurrentItem() 732 | // if selectedIndex < 0 || selectedIndex >= len(fm.items) { 733 | // return 734 | // } 735 | 736 | // selectedItem := fm.items[selectedIndex] 737 | // fullPath := filepath.Join(fm.path, selectedItem) 738 | 739 | // err := os.RemoveAll(fullPath) // Remove files or directories 740 | // if err != nil { 741 | // fm.fileDetails.SetText(fmt.Sprintf("Error deleting file: %v", err)) 742 | // return 743 | // } 744 | 745 | // fm.loadItems() 746 | // } 747 | 748 | // func (fm *FileManager) updateGitDetails() { 749 | // if !isGitRepo(fm.path) { 750 | // fm.gitDetails.SetText("") 751 | // return 752 | // } 753 | 754 | // gitCommits := fm.gitLog() 755 | 756 | // // Set Git details with commit hashes and messages 757 | // fm.gitDetails.SetText(gitCommits) 758 | // } 759 | 760 | // func (fm *FileManager) gitLog() string { 761 | // cmd := exec.Command("git", "-C", fm.path, "log", "--oneline", "-10") 762 | // output, err := cmd.CombinedOutput() 763 | // if err != nil { 764 | // return fmt.Sprintf("Error getting git log: %v", err) 765 | // } 766 | 767 | // // Process git log output to include commit messages 768 | // lines := strings.Split(string(output), "\n") 769 | // var result strings.Builder 770 | // for _, line := range lines { 771 | // if line != "" { 772 | // parts := strings.SplitN(line, " ", 2) 773 | // if len(parts) >= 2 { 774 | // result.WriteString(fmt.Sprintf("[yellow]%s [white]%s\n", parts[0], parts[1])) 775 | // } 776 | // } 777 | // } 778 | // return result.String() 779 | // } 780 | 781 | // func (fm *FileManager) run() { 782 | // fm.list.SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 783 | // fm.navigate(mainText) 784 | // }) 785 | 786 | // fm.list.SetChangedFunc(func(index int, mainText, secondaryText string, shortcut rune) { 787 | // fm.updateDetails() 788 | // fm.updateGitDetails() 789 | // }) 790 | 791 | // fm.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { 792 | // if fm.vimRunning { 793 | // return event 794 | // } 795 | 796 | // switch event.Key() { 797 | // case tcell.KeyBackspace, tcell.KeyBackspace2, tcell.KeyLeft: 798 | // fm.path = filepath.Dir(fm.path) 799 | // fm.loadItems() 800 | // return nil 801 | // case tcell.KeyEnter, tcell.KeyRight: 802 | // index := fm.list.GetCurrentItem() 803 | // if index >= 0 && index < len(fm.items) { 804 | // fm.navigate(fm.items[index]) 805 | // } 806 | // return nil 807 | // case tcell.KeyCtrlD: 808 | // fm.deleteSelectedItem() 809 | // return nil 810 | // case tcell.KeyRune: 811 | // switch event.Rune() { 812 | // case 'q': 813 | // fm.app.Stop() 814 | // return nil 815 | // } 816 | // } 817 | // return event 818 | // }) 819 | 820 | // fm.commands.SetText("[red]Navigate: [yellow]← Back, → Enter, [green]Ctrl+D Delete, [blue]q Quit") 821 | 822 | // fm.flex.SetDirection(tview.FlexRow). 823 | // AddItem( 824 | // tview.NewFlex(). 825 | // SetDirection(tview.FlexColumn). 826 | // AddItem(fm.list, 0, 1, true). 827 | // AddItem(fm.fileDetails, 0, 2, false). 828 | // AddItem(fm.gitDetails, 0, 2, false), 0, 1, true). 829 | // AddItem(fm.commands, 3, 1, false) 830 | 831 | // fm.app.SetRoot(fm.flex, true) 832 | 833 | // // Set a dark black background color 834 | // fm.app.SetBeforeDrawFunc(func(screen tcell.Screen) bool { 835 | // screen.Clear() 836 | // bgStyle := tcell.StyleDefault.Background(tcell.ColorBlack) 837 | // screen.SetStyle(bgStyle) 838 | // return false 839 | // }) 840 | 841 | // if err := fm.app.Run(); err != nil { 842 | // panic(err) 843 | // } 844 | // } 845 | 846 | // func main() { 847 | // fm := NewFileManager(".") 848 | // fm.run() 849 | // } 850 | --------------------------------------------------------------------------------