Skip to content

Commit deb61b3

Browse files
authored
Create commands.go
1 parent e16b723 commit deb61b3

1 file changed

Lines changed: 305 additions & 0 deletions

File tree

source-code/src/commands.go

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
package src
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
11+
"golang.org/x/sync/errgroup"
12+
)
13+
14+
func (m *Model) executeCommand(cmdStr string) {
15+
args := strings.Fields(cmdStr)
16+
if len(args) == 0 {
17+
m.statusMsg = errorStyle.Render("Empty command")
18+
return
19+
}
20+
cmdName := args[0]
21+
p := &m.panels[m.activePanel]
22+
switch cmdName {
23+
case "cd":
24+
if len(args) < 2 {
25+
m.statusMsg = errorStyle.Render("cd requires a directory")
26+
return
27+
}
28+
newDir := args[1]
29+
if !filepath.IsAbs(newDir) {
30+
newDir = filepath.Join(p.currentDir, newDir)
31+
}
32+
err := p.vfs.Chdir(newDir)
33+
if err != nil {
34+
m.statusMsg = errorStyle.Render(fmt.Sprintf("cd failed: %v", err))
35+
return
36+
}
37+
p.currentDir, _ = p.vfs.Getwd()
38+
m.refreshPanel(m.activePanel)
39+
m.statusMsg = successStyle.Render("Changed directory to " + p.currentDir)
40+
case "mv":
41+
m.mode = progressMode
42+
go m.moveWithProgress(args)
43+
case "rm":
44+
m.mode = progressMode
45+
go m.deleteWithProgress()
46+
case "cp":
47+
m.mode = progressMode
48+
go m.copyWithProgress(args)
49+
case "touch":
50+
if len(args) < 2 {
51+
m.statusMsg = errorStyle.Render("touch requires filename")
52+
return
53+
}
54+
file := filepath.Join(p.currentDir, args[1])
55+
f, err := os.Create(file)
56+
if err != nil {
57+
m.statusMsg = errorStyle.Render(fmt.Sprintf("touch failed: %v", err))
58+
return
59+
}
60+
f.Close()
61+
m.refreshPanel(m.activePanel)
62+
case "mkdir":
63+
if len(args) < 2 {
64+
m.statusMsg = errorStyle.Render("mkdir requires directory name")
65+
return
66+
}
67+
dir := filepath.Join(p.currentDir, args[1])
68+
err := os.MkdirAll(dir, os.ModePerm)
69+
if err != nil {
70+
m.statusMsg = errorStyle.Render(fmt.Sprintf("mkdir failed: %v", err))
71+
return
72+
}
73+
m.refreshPanel(m.activePanel)
74+
case "hedit":
75+
if len(args) < 2 {
76+
m.statusMsg = errorStyle.Render("hedit requires filename")
77+
return
78+
}
79+
file := filepath.Join(p.currentDir, args[1])
80+
stat, err := p.vfs.Stat(file)
81+
if err != nil {
82+
m.statusMsg = errorStyle.Render(fmt.Sprintf("hedit failed: %v", err))
83+
return
84+
}
85+
if stat.Size() > maxFileSizeForEdit {
86+
m.statusMsg = errorStyle.Render("File too large to edit")
87+
return
88+
}
89+
f, err := p.vfs.Open(file)
90+
if err != nil {
91+
m.statusMsg = errorStyle.Render(fmt.Sprintf("hedit failed: %v", err))
92+
return
93+
}
94+
content, err := io.ReadAll(f)
95+
f.Close()
96+
if err != nil {
97+
m.statusMsg = errorStyle.Render(fmt.Sprintf("hedit failed: %v", err))
98+
return
99+
}
100+
m.editor.SetValue(string(content))
101+
m.editorFile = file
102+
m.mode = editorMode
103+
m.editor.Focus()
104+
case "sftp":
105+
if len(args) < 2 {
106+
m.statusMsg = errorStyle.Render("sftp requires url")
107+
return
108+
}
109+
url := args[1]
110+
if !strings.HasPrefix(url, "sftp://") {
111+
url = "sftp://" + url
112+
}
113+
parts := strings.SplitN(strings.TrimPrefix(url, "sftp://"), "@", 2)
114+
if len(parts) < 2 {
115+
m.statusMsg = errorStyle.Render("Invalid sftp url")
116+
return
117+
}
118+
userpass := strings.SplitN(parts[0], ":", 2)
119+
user := userpass[0]
120+
pass := ""
121+
if len(userpass) > 1 {
122+
pass = userpass[1]
123+
}
124+
host := parts[1]
125+
vfs, err := newSFTPVFS(host, user, pass)
126+
if err != nil {
127+
m.statusMsg = errorStyle.Render(err.Error())
128+
return
129+
}
130+
p.vfs = vfs
131+
p.currentDir, _ = vfs.Getwd()
132+
m.refreshPanel(m.activePanel)
133+
default:
134+
m.runSystemCommand(cmdName, args[1:]...)
135+
}
136+
}
137+
138+
func (m *Model) mountArchive(file string) {
139+
ext := filepath.Ext(file)
140+
var avfs vfsHandler
141+
var err error
142+
fullPath := filepath.Join(m.panels[m.activePanel].currentDir, file)
143+
if ext == ".zip" {
144+
avfs, err = newZipVFS(fullPath)
145+
} else if ext == ".tar" || ext == ".gz" {
146+
avfs, err = newTarVFS(fullPath)
147+
} else {
148+
m.statusMsg = errorStyle.Render("Unsupported archive")
149+
return
150+
}
151+
if err != nil {
152+
m.statusMsg = errorStyle.Render(err.Error())
153+
return
154+
}
155+
m.panels[m.activePanel].vfs = avfs
156+
m.panels[m.activePanel].currentDir = fullPath
157+
m.refreshPanel(m.activePanel)
158+
}
159+
160+
func isArchive(file string) bool {
161+
ext := filepath.Ext(file)
162+
return ext == ".zip" || ext == ".tar" || strings.HasSuffix(file, ".tar.gz")
163+
}
164+
165+
func (m *Model) runSystemCommand(name string, arg ...string) {
166+
go func() {
167+
cmd := exec.Command(name, arg...)
168+
cmd.Dir = m.panels[m.activePanel].currentDir
169+
output, err := cmd.CombinedOutput()
170+
m.ResultChan <- CommandResult{Output: string(output), Err: err}
171+
}()
172+
}
173+
174+
func (m *Model) copyToOtherPanel() {
175+
dstPanel := 1 - m.activePanel
176+
args := []string{"cp", ".", m.panels[dstPanel].currentDir}
177+
m.mode = progressMode
178+
go m.copyWithProgress(args)
179+
}
180+
181+
func (m *Model) moveToOtherPanel() {
182+
dstPanel := 1 - m.activePanel
183+
args := []string{"mv", ".", m.panels[dstPanel].currentDir}
184+
m.mode = progressMode
185+
go m.moveWithProgress(args)
186+
}
187+
188+
func (m *Model) copyWithProgress(args []string) {
189+
var sources []string
190+
p := m.panels[m.activePanel]
191+
dst := m.panels[1-m.activePanel].currentDir
192+
for f := range p.selectedFiles {
193+
if p.selectedFiles[f] {
194+
sources = append(sources, f)
195+
}
196+
}
197+
if len(sources) == 0 && len(args) > 2 {
198+
sources = []string{filepath.Join(p.currentDir, args[1])}
199+
dst = args[2]
200+
}
201+
total := len(sources)
202+
eg := errgroup.Group{}
203+
for i, src := range sources {
204+
i, src := i, src
205+
eg.Go(func() error {
206+
err := copyFileVFS(p.vfs, m.panels[1-m.activePanel].vfs, src, filepath.Join(dst, filepath.Base(src)), func(percent float64) {
207+
overall := (float64(i) + percent) / float64(total)
208+
m.ProgressChan <- ProgressMsg{Percent: overall}
209+
})
210+
return err
211+
})
212+
}
213+
err := eg.Wait()
214+
p.selectedFiles = make(map[string]bool)
215+
m.ProgressChan <- ProgressMsg{Percent: 1.0}
216+
if err != nil {
217+
m.ResultChan <- CommandResult{Err: err}
218+
} else {
219+
m.ResultChan <- CommandResult{Output: "Copy completed"}
220+
}
221+
}
222+
223+
func copyFileVFS(srcVFS vfsHandler, dstVFS vfsHandler, src, dst string, progressCb func(float64)) error {
224+
s, err := srcVFS.Open(src)
225+
if err != nil {
226+
return err
227+
}
228+
defer s.Close()
229+
stat, err := s.Stat()
230+
if err != nil {
231+
return err
232+
}
233+
if stat.IsDir() {
234+
return fmt.Errorf("dir copy not implemented")
235+
}
236+
d, err := os.Create(dst)
237+
if err != nil {
238+
return err
239+
}
240+
defer d.Close()
241+
totalSize := stat.Size()
242+
var written int64
243+
buf := make([]byte, 4096)
244+
for {
245+
n, err := s.Read(buf)
246+
if err != nil && err != io.EOF {
247+
return err
248+
}
249+
if n == 0 {
250+
break
251+
}
252+
_, err = d.Write(buf[:n])
253+
if err != nil {
254+
return err
255+
}
256+
written += int64(n)
257+
progressCb(float64(written) / float64(totalSize))
258+
}
259+
return nil
260+
}
261+
262+
func (m *Model) moveWithProgress(args []string) {
263+
var sources []string
264+
p := m.panels[m.activePanel]
265+
dst := m.panels[1-m.activePanel].currentDir
266+
for f := range p.selectedFiles {
267+
if p.selectedFiles[f] {
268+
sources = append(sources, f)
269+
}
270+
}
271+
if len(sources) == 0 && len(args) > 2 {
272+
sources = []string{filepath.Join(p.currentDir, args[1])}
273+
dst = args[2]
274+
}
275+
total := len(sources)
276+
for i, src := range sources {
277+
newPath := filepath.Join(dst, filepath.Base(src))
278+
err := os.Rename(src, newPath)
279+
if err != nil {
280+
m.ResultChan <- CommandResult{Err: err}
281+
return
282+
}
283+
m.ProgressChan <- ProgressMsg{Percent: float64(i+1) / float64(total)}
284+
}
285+
p.selectedFiles = make(map[string]bool)
286+
m.ProgressChan <- ProgressMsg{Percent: 1.0}
287+
m.ResultChan <- CommandResult{Output: "Move completed"}
288+
}
289+
290+
func (m *Model) deleteWithProgress() {
291+
p := m.panels[m.activePanel]
292+
var targets []string
293+
for f := range p.selectedFiles {
294+
if p.selectedFiles[f] {
295+
targets = append(targets, f)
296+
}
297+
}
298+
total := len(targets)
299+
for i, target := range targets {
300+
os.RemoveAll(target)
301+
m.ProgressChan <- ProgressMsg{Percent: float64(i+1) / float64(total)}
302+
}
303+
p.selectedFiles = make(map[string]bool)
304+
m.ResultChan <- CommandResult{Output: "Delete completed"}
305+
}

0 commit comments

Comments
 (0)