fix: reload preview immediately after watch file modification#2594
Conversation
| delete(app.nav.regCache, f.path) | ||
| if curr := app.nav.currFile(); curr == nil || curr.path != f.path { | ||
| delete(app.nav.regCache, f.path) | ||
| } |
There was a problem hiding this comment.
I'm not sure if this logic is needed - even if the modified file is the current file under the cursor, it will delete the cache entry and add it back in the app.ui.loadFile call below. So the end result is that there will be a cache entry with loading = true either way.
There was a problem hiding this comment.
Without this change, the old preview briefly disappears and reappears between the editor closing and the new preview showing up. Keeping the cache entry avoids that blank moment.
There was a problem hiding this comment.
I can understand the old preview disappearing since the cache entry is invalidated, but I don't understand why it would reappear after being deleted. I also don't understand why you would want to keep the stale cache entry instead of just deleting it and adding it back again immediately in app.ui.loadFile afterwards (it happens in the same loop cycle so theoretically it shouldn't create a flicker).
Do you know the exact root cause of this issue, and why this patch solves it?
There was a problem hiding this comment.
why would it reappear after being deleted
It isn't deleted at that point. The stale paint happens before fileChan fires.
After the editor exits, runCmdSync calls loadFile which runs checkReg
checkReg then updates loadTime and queues a refresh but leaves reg.loading=false and reg.lines untouched, so the next draw paints the stale lines. With set watch=true , fsnotify triggers the refresh and shows the new file content after the old one was already displayed, leading to flicker.
There was a problem hiding this comment.
After #2597, runCmdSync calling loadFile will now set reg.loading=true so the old preview will never be shown, and instead the preview will be blank until the new contents are loaded.
I guess there's still the issue where there are two competing mechanisms for reloading the file (the original checkReg and fsnotify). I'm not sure if it's a good idea for fileChan to delete the cache entry then since that could result in a double load, maybe it is sufficient to just call checkReg instead since that will not do anything if the file has already been reloaded after it was modified, something like this:
diff --git a/app.go b/app.go
index da8bd28..d03552d 100644
--- a/app.go
+++ b/app.go
@@ -466,8 +466,11 @@ func (app *app) loop() {
dir.sel(name, app.nav.height)
}
- delete(app.nav.regCache, f.path)
- app.ui.loadFile(app, false)
+ if r, ok := app.nav.regCache[f.path]; ok {
+ app.nav.checkReg(r)
+ } else {
+ // handle possible edge case if editor saves via a backup file?
+ }
onLoad(app, []string{f.path})
app.ui.draw(app.nav)
case path := <-app.nav.delChan:The filecChan handler should trigger for any file, so including logic that depends on the current file seems wrong to me.
There was a problem hiding this comment.
The guard isn't redundant. With watch=true and a fast save+quit, runCmdSync's loadFile finishes loading the new preview into regCache before app.loop processes the pending fileChan event. Without the guard, the handler then deletes that just-loaded entry and replaces it with an empty loading=true reg, which printReg renders as a blank pane until a second load completes. That blank flash is the flicker. The guard skips the delete when curr.path == f.path, so the loaded entry stays and there is no flash.
You're right that both paths end with a loading=true entry, but only the no-guard path discards an entry that already has real lines.
The flicker only happens when you use "save and quit" like Vim's "ZZ"
when you instead save and then quit after waiting a second, there is no flicker.
There was a problem hiding this comment.
Okay, so I think we both agree on the fact that runCmdSync triggers loading the new preview and that it finishes loading before processing the fileChan event. My understanding is that when processing the fileChan event, if deleting is skipped then loadFile will call checkReg but that won't do anything since the file hasn't been modified since the reload via runCmdSync, and therefore there is no second load.
So then my question is this: why should the cache entry be deleted in the first place? Deleting makes sense if the file is actually deleted, but a fileChan event represents a file modification so in theory refreshing it via checkReg should be sufficient.
There was a problem hiding this comment.
yes the deletion can be removed. fixed now
joelim-work
left a comment
There was a problem hiding this comment.
Looks good thanks.
Incidentally this fixes a bug where previews for files other than the current file don't get reloaded immediately, only the cache entry is deleted (can be observed by adding a sleep to the previewer).
When a file is opened by a terminal editor in lf, saving the file and returning to lf will trigger a flicker where the old contents of the text files are shown as preview for a split second, before the new updated content is drawn in the preview pane.
This is caused by checkReg not marking the cache entry, so printReg still printed the old content. Setting reg.loading now fixes printReg and skips the stale output, so the pane stays empty until the new reg lands.
Note: the bug only triggers when using external preview scripts.