acme: transparent remount on stale FUSE mount

This commit is contained in:
Levi Neely 2026-05-21 09:00:06 +02:00
parent c8e6c50e53
commit 992050d462
2 changed files with 46 additions and 9 deletions

46
main.go
View File

@ -4,6 +4,7 @@ package main
import ( import (
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@ -22,6 +23,37 @@ func init() {
} }
} }
func remount() {
exec.Command("ollie-remount").Run() //nolint:errcheck
}
func readFileRetry(path string) ([]byte, error) {
data, err := os.ReadFile(path)
if err != nil {
remount()
data, err = os.ReadFile(path)
}
return data, err
}
func writeFileRetry(path string, data []byte) error {
err := os.WriteFile(path, data, 0644)
if err != nil {
remount()
err = os.WriteFile(path, data, 0644)
}
return err
}
func statRetry(path string) (os.FileInfo, error) {
fi, err := os.Stat(path)
if err != nil {
remount()
fi, err = os.Stat(path)
}
return fi, err
}
func main() { func main() {
acme.AutoExit(true) acme.AutoExit(true)
if err := newSessionList(); err != nil { if err := newSessionList(); err != nil {
@ -53,7 +85,7 @@ func newSessionList() error {
} }
func (sl *sessionList) refresh() { func (sl *sessionList) refresh() {
data, err := os.ReadFile(filepath.Join(ollie, "s", "idx")) data, err := readFileRetry(filepath.Join(ollie, "s", "idx"))
if err != nil { if err != nil {
sl.win.Errf("read idx: %v", err) sl.win.Errf("read idx: %v", err)
return return
@ -113,7 +145,7 @@ func (sl *sessionList) eventLoop() {
arg := strings.TrimSpace(string(e.Arg)) arg := strings.TrimSpace(string(e.Arg))
sid := sl.resolveFullSID(arg) sid := sl.resolveFullSID(arg)
if sid != "" { if sid != "" {
os.WriteFile(filepath.Join(ollie, "s", sid, "ctl"), []byte("kill"), 0644) writeFileRetry(filepath.Join(ollie, "s", sid, "ctl"), []byte("kill"))
sl.refresh() sl.refresh()
} }
continue continue
@ -136,7 +168,7 @@ func (sl *sessionList) eventLoop() {
} }
func sessionExists(sid string) bool { func sessionExists(sid string) bool {
_, err := os.Stat(filepath.Join(ollie, "s", sid)) _, err := statRetry(filepath.Join(ollie, "s", sid))
return err == nil return err == nil
} }
@ -163,7 +195,7 @@ func openChat(sid string) {
return return
} }
winName := "ollie/sessions/" + sid winName := "ollie/sessions/" + sid
if data, err := os.ReadFile(filepath.Join(ollie, "s", "idx")); err == nil { if data, err := readFileRetry(filepath.Join(ollie, "s", "idx")); err == nil {
for _, line := range strings.Split(string(data), "\n") { for _, line := range strings.Split(string(data), "\n") {
fields := strings.Split(line, "\t") fields := strings.Split(line, "\t")
if len(fields) >= 3 && fields[0] == sid { if len(fields) >= 3 && fields[0] == sid {
@ -181,7 +213,7 @@ func openChat(sid string) {
// Load existing chat. // Load existing chat.
chatPath := filepath.Join(ollie, "s", sid, "chat") chatPath := filepath.Join(ollie, "s", sid, "chat")
if data, err := os.ReadFile(chatPath); err == nil && len(data) > 0 { if data, err := readFileRetry(chatPath); err == nil && len(data) > 0 {
w.Write("body", data) w.Write("body", data)
cw.offset = len(data) cw.offset = len(data)
} }
@ -201,7 +233,7 @@ func (cw *chatWin) eventLoop() {
case "Prompt": case "Prompt":
openFile(filepath.Join(ollie, "s", cw.sid, "prompt")) openFile(filepath.Join(ollie, "s", cw.sid, "prompt"))
case "Stop": case "Stop":
os.WriteFile(filepath.Join(ollie, "s", cw.sid, "ctl"), []byte("stop"), 0644) writeFileRetry(filepath.Join(ollie, "s", cw.sid, "ctl"), []byte("stop"))
case "Ctl": case "Ctl":
openFile(filepath.Join(ollie, "s", cw.sid, "ctl")) openFile(filepath.Join(ollie, "s", cw.sid, "ctl"))
default: default:
@ -218,7 +250,7 @@ func (cw *chatWin) tail() {
chatPath := filepath.Join(ollie, "s", cw.sid, "chat") chatPath := filepath.Join(ollie, "s", cw.sid, "chat")
for { for {
fi, err := os.Stat(chatPath) fi, err := statRetry(chatPath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return return

View File

@ -4,6 +4,7 @@ set -euo pipefail
cmd=${0##*/} cmd=${0##*/}
die() { printf '%s: %s\n' "$cmd" "$*" >&2; exit 1; } die() { printf '%s: %s\n' "$cmd" "$*" >&2; exit 1; }
try() { "$@" 2>/dev/null || { ollie-remount && "$@"; }; }
[ -z "${winid:-}" ] || [ "$winid" = "0" ] && die "must be run from an acme window" [ -z "${winid:-}" ] || [ "$winid" = "0" ] && die "must be run from an acme window"
@ -72,7 +73,7 @@ if [ -s "$tmpdir/hover_cur" ]; then
dfile=${defn%%:*} dfile=${defn%%:*}
dline=$(echo "$defn" | cut -d: -f2) dline=$(echo "$defn" | cut -d: -f2)
dcol=$(echo "$defn" | cut -d: -f3) dcol=$(echo "$defn" | cut -d: -f3)
result=$(python3 $OLLIE/t/lsp_hover "$dfile" "$dline" "$dcol" 2>/dev/null || true) result=$(try python3 $OLLIE/t/lsp_hover "$dfile" "$dline" "$dcol" || true)
[ -n "$result" ] && [ "$result" != "(no hover info)" ] && { printf '%s\n' "$result" | tee -a "$tmpdir/hover_embedded" >> "$tmpdir/hover_cur"; continue; } [ -n "$result" ] && [ "$result" != "(no hover info)" ] && { printf '%s\n' "$result" | tee -a "$tmpdir/hover_embedded" >> "$tmpdir/hover_cur"; continue; }
# Fallback: read raw source block # Fallback: read raw source block
sed -n "${dline},/^}/p" "$dfile" 2>/dev/null | tee -a "$tmpdir/hover_embedded" >> "$tmpdir/hover_cur" || true sed -n "${dline},/^}/p" "$dfile" 2>/dev/null | tee -a "$tmpdir/hover_embedded" >> "$tmpdir/hover_cur" || true
@ -103,6 +104,10 @@ $(cat "$tmpdir/hover_embedded")
" "
# Send prefix+selection as prefix context; selection is the focal point # Send prefix+selection as prefix context; selection is the focal point
result=$(printf '%s%s' "$prefix" "$sel" | $OLLIE/u/complete -file "$filename" -suffix "$suffix" -context "$context" -cwd "$modroot" "$@") _input=$(printf '%s%s' "$prefix" "$sel")
result=$(printf '%s' "$_input" | $OLLIE/u/complete -file "$filename" -suffix "$suffix" -context "$context" -cwd "$modroot" "$@") || {
ollie-remount
result=$(printf '%s' "$_input" | $OLLIE/u/complete -file "$filename" -suffix "$suffix" -context "$context" -cwd "$modroot" "$@")
}
printf '%s' "$result" printf '%s' "$result"