Skip to content

Commit c8539cd

Browse files
authored
Update utils.go
1 parent aa9a58e commit c8539cd

1 file changed

Lines changed: 4 additions & 247 deletions

File tree

source-code/src/utils.go

Lines changed: 4 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -1,251 +1,8 @@
11
package src
22

3-
import (
4-
"fmt"
5-
"io"
6-
"io/fs"
7-
"net/http"
8-
"os"
9-
"os/exec"
10-
"path/filepath"
11-
"regexp"
12-
"sort"
13-
"strings"
14-
"syscall"
3+
import "os/exec"
154

16-
"github.com/alecthomas/chroma/v2/lexers"
17-
"github.com/charmbracelet/bubbles/list"
18-
)
19-
20-
func (m *Model) refreshPanel(idx int) {
21-
p := &m.panels[idx]
22-
items := []list.Item{}
23-
files, err := p.vfs.ReadDir(p.currentDir)
24-
if err != nil {
25-
m.statusMsg = errorStyle.Render(fmt.Sprintf("Error reading directory: %v", err))
26-
return
27-
}
28-
gitStatus := m.getGitStatus(p)
29-
sort.Slice(files, func(i, j int) bool {
30-
return files[i].Name() < files[j].Name()
31-
})
32-
for _, file := range files {
33-
info, _ := file.Info()
34-
desc := "File"
35-
if file.IsDir() {
36-
desc = "Directory"
37-
}
38-
status := gitStatus[file.Name()]
39-
items = append(items, item{
40-
title: file.Name(),
41-
desc: desc,
42-
status: status,
43-
isDir: file.IsDir(),
44-
size: info.Size(),
45-
modTime: info.ModTime(),
46-
})
47-
}
48-
p.fileList.SetItems(items)
49-
m.statusMsg = successStyle.Render("Panel refreshed")
50-
m.updatePreview(idx)
51-
m.updateGitBranch(idx)
52-
}
53-
54-
func (m *Model) getGitStatus(p *panel) map[string]string {
55-
statusMap := make(map[string]string)
56-
if _, ok := p.vfs.(localVFS); !ok {
57-
return statusMap
58-
}
59-
cmd := exec.Command("git", "status", "--porcelain")
60-
cmd.Dir = p.currentDir
61-
output, err := cmd.Output()
62-
if err != nil {
63-
return statusMap
64-
}
65-
lines := strings.Split(string(output), "\n")
66-
for _, line := range lines {
67-
if line == "" {
68-
continue
69-
}
70-
fields := strings.Fields(line)
71-
if len(fields) < 2 {
72-
continue
73-
}
74-
file := fields[1]
75-
st := line[:2]
76-
statusMap[file] = st
77-
}
78-
return statusMap
79-
}
80-
81-
func (m *Model) updateGitBranch(idx int) {
82-
p := &m.panels[idx]
83-
if _, ok := p.vfs.(localVFS); !ok {
84-
p.gitBranch = ""
85-
return
86-
}
87-
cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
88-
cmd.Dir = p.currentDir
89-
output, err := cmd.Output()
90-
if err != nil {
91-
p.gitBranch = ""
92-
return
93-
}
94-
p.gitBranch = strings.TrimSpace(string(output))
95-
}
96-
97-
func (m *Model) updatePreview(idx int) {
98-
p := &m.panels[idx]
99-
selected, ok := p.fileList.SelectedItem().(item)
100-
if !ok {
101-
p.preview.SetContent("")
102-
return
103-
}
104-
if selected.isDir {
105-
p.preview.SetContent("Directory")
106-
return
107-
}
108-
filePath := filepath.Join(p.currentDir, selected.title)
109-
f, err := p.vfs.Open(filePath)
110-
if err != nil {
111-
p.preview.SetContent("Error opening file")
112-
return
113-
}
114-
defer f.Close()
115-
stat, err := f.Stat()
116-
if err != nil {
117-
p.preview.SetContent("Error stat file")
118-
return
119-
}
120-
buf := make([]byte, 512)
121-
_, err = f.Read(buf)
122-
if err != nil && err != io.EOF {
123-
p.preview.SetContent("Error reading for MIME")
124-
return
125-
}
126-
mimeType := http.DetectContentType(buf)
127-
if mimeType == "application/octet-stream" {
128-
if _, ok := p.vfs.(localVFS); ok {
129-
cmd := exec.Command("file", "-b", "--mime-type", filePath)
130-
out, err := cmd.Output()
131-
if err == nil {
132-
mimeType = strings.TrimSpace(string(out))
133-
}
134-
}
135-
}
136-
if s, ok := f.(io.Seeker); ok {
137-
s.Seek(0, io.SeekStart)
138-
} else {
139-
f.Close()
140-
f, err = p.vfs.Open(filePath)
141-
if err != nil {
142-
p.preview.SetContent("Error reopening file")
143-
return
144-
}
145-
defer f.Close()
146-
}
147-
if strings.HasPrefix(mimeType, "image/") {
148-
p.preview.SetContent("Image preview not supported in text mode. MIME: " + mimeType)
149-
return
150-
}
151-
if !strings.HasPrefix(mimeType, "text/") {
152-
p.preview.SetContent("Non-text file: " + mimeType)
153-
return
154-
}
155-
if stat.Size() > maxFileSizeForEdit {
156-
p.preview.SetContent("File too large for preview")
157-
return
158-
}
159-
byteContent, err := io.ReadAll(f)
160-
if err != nil {
161-
p.preview.SetContent("Error reading file")
162-
return
163-
}
164-
content := string(byteContent)
165-
lines := strings.Split(content, "\n")
166-
if len(lines) > previewLines {
167-
lines = lines[:previewLines]
168-
content = strings.Join(lines, "\n") + "\n..."
169-
}
170-
lexer := lexers.Match(selected.title)
171-
if lexer == nil {
172-
lexer = lexers.Fallback
173-
}
174-
iterator, err := lexer.Tokenise(nil, content)
175-
if err != nil {
176-
p.preview.SetContent(content)
177-
return
178-
}
179-
var sb strings.Builder
180-
err = chromaFormatter.Format(&sb, chromaStyle, iterator)
181-
if err != nil {
182-
p.preview.SetContent(content)
183-
return
184-
}
185-
p.preview.SetContent(sb.String())
186-
}
187-
188-
func (m *Model) performFuzzySearch() {
189-
query := m.fuzzyInput.Value()
190-
if query == "" {
191-
return
192-
}
193-
var results []string
194-
err := filepath.Walk(m.panels[m.activePanel].currentDir, func(path string, info fs.FileInfo, err error) error {
195-
if err != nil {
196-
return err
197-
}
198-
if strings.Contains(strings.ToLower(filepath.Base(path)), strings.ToLower(query)) {
199-
rel, _ := filepath.Rel(m.panels[m.activePanel].currentDir, path)
200-
results = append(results, rel)
201-
}
202-
return nil
203-
})
204-
if err != nil {
205-
m.statusMsg = errorStyle.Render(err.Error())
206-
}
207-
m.fuzzyResults = results
208-
items := []list.Item{}
209-
for _, res := range results {
210-
items = append(items, item{title: res})
211-
}
212-
m.panels[m.activePanel].fileList.SetItems(items)
213-
}
214-
215-
func (m *Model) performBulkRename() {
216-
re, err := regexp.Compile(m.bulkRenameFrom)
217-
if err != nil {
218-
m.statusMsg = errorStyle.Render("Invalid regex")
219-
return
220-
}
221-
for file := range m.panels[m.activePanel].selectedFiles {
222-
newName := re.ReplaceAllString(filepath.Base(file), m.bulkRenameTo)
223-
newPath := filepath.Join(filepath.Dir(file), newName)
224-
err := os.Rename(file, newPath)
225-
if err != nil {
226-
m.statusMsg += errorStyle.Render(fmt.Sprintf("Rename failed for %s: %v\n", file, err))
227-
}
228-
}
229-
m.panels[m.activePanel].selectedFiles = make(map[string]bool)
230-
m.refreshPanel(m.activePanel)
231-
m.bulkRenameFrom = ""
232-
m.bulkRenameTo = ""
233-
}
234-
235-
func (m *Model) suspend() {
236-
pid := os.Getpid()
237-
syscall.Kill(pid, syscall.SIGTSTP)
238-
m.refreshPanel(m.activePanel)
239-
}
240-
241-
func (m *Model) openSubShell() {
242-
m.subShell = true
243-
cmd := exec.Command(os.Getenv("SHELL"))
244-
cmd.Stdin = os.Stdin
245-
cmd.Stdout = os.Stdout
246-
cmd.Stderr = os.Stderr
247-
cmd.Dir = m.panels[m.activePanel].currentDir
248-
cmd.Run()
249-
m.subShell = false
250-
m.refreshPanel(m.activePanel)
5+
// buildCmd is a thin wrapper so we can intercept exec calls in tests.
6+
func buildCmd(name string, args ...string) *exec.Cmd {
7+
return exec.Command(name, args...)
2518
}

0 commit comments

Comments
 (0)