From d598157590cadbe15cd1031147e8cb1bcfdcbd01 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 9 Oct 2025 08:11:47 +0530 Subject: [PATCH] Make goroutines in choose_files panic safe --- kittens/choose_files/graphics.go | 3 ++ kittens/choose_files/image_preview.go | 41 +++++++++++++++++++-------- kittens/choose_files/preview.go | 10 +++++-- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/kittens/choose_files/graphics.go b/kittens/choose_files/graphics.go index 6a2648f02..3ee4f3634 100644 --- a/kittens/choose_files/graphics.go +++ b/kittens/choose_files/graphics.go @@ -269,6 +269,9 @@ func (self *GraphicsHandler) RenderImagePreview(h *Handler, p *ImagePreview, x, img_metadata, cached_data := self.cached_resized_image(p.disk_cache.ResultsDir(), cache_key) var img *images.ImageData if len(cached_data) == 0 { + if err = p.ensure_source_image(); err != nil { + return + } img = p.source_img final_width, final_height := images.FitImage(img.Width, img.Height, px_width, px_height) if final_width != img.Width || final_height != img.Height { diff --git a/kittens/choose_files/image_preview.go b/kittens/choose_files/image_preview.go index 4dccca34a..5944ec12a 100644 --- a/kittens/choose_files/image_preview.go +++ b/kittens/choose_files/image_preview.go @@ -110,17 +110,26 @@ func (p *ImagePreview) ensure_source_image() (err error) { return } +func (p *ImagePreview) render_image(h *Handler, x, y, width, height int) { + defer func() { + if r := recover(); r != nil { + text, _ := utils.Format_stacktrace_on_panic(r) + h.err_chan <- fmt.Errorf("%s", text) + p.WakeupMainThread() + } + }() + + offset := p.renderer.ShowMetadata(h, ShowData{ + abspath: p.abspath, metadata: p.metadata, x: x, y: y, width: width, height: height, cached_data: p.cached_data, + img_metadata: p.img_metadata, + }) + h.graphics_handler.RenderImagePreview(h, p, x, y+offset, width, height-offset) +} + func (p *ImagePreview) Render(h *Handler, x, y, width, height int) { if p.render_channel == nil { if p.render_err == nil { - offset := p.renderer.ShowMetadata(h, ShowData{ - abspath: p.abspath, metadata: p.metadata, x: x, y: y, width: width, height: height, cached_data: p.cached_data, - img_metadata: p.img_metadata, - }) - y += offset - height -= offset - h.graphics_handler.RenderImagePreview(h, p, x, y, width, height) - + p.render_image(h, x, y, width, height) } else { p.render_err.Render(h, x, y, width, height) } @@ -132,19 +141,28 @@ func (p *ImagePreview) Render(h *Handler, x, y, width, height int) { p.cached_data = hd.cached_data p.source_img = hd.img p.img_metadata = hd.img_metadata - p.render_err = NewErrorPreview(fmt.Errorf("Failed to render the preview with error: %w", hd.err)) + if hd.err != nil { + p.render_err = NewErrorPreview(fmt.Errorf("Failed to render the preview with error: %w", hd.err)) + } p.Render(h, x, y, width, height) return default: } if p.file_metadata_preview == nil { p.file_metadata_preview = NewFileMetadataPreview(p.abspath, p.metadata) + m := p.file_metadata_preview.(*MessagePreview) + m.trailers = append(m.trailers, "", "Rendering image preview, please wait…") } p.file_metadata_preview.Render(h, x, y, width, height) } func (p *ImagePreview) start_rendering() { defer func() { + if r := recover(); r != nil { + text, _ := utils.Format_stacktrace_on_panic(r) + p.render_channel <- render_data{err: fmt.Errorf("%s", text)} + } + close(p.render_channel) p.WakeupMainThread() }() key, ans, err := p.disk_cache.GetPath(p.abspath) @@ -202,8 +220,7 @@ func (p ImagePreviewRenderer) ShowMetadata(h *Handler, s ShowData) int { if s.img_metadata != nil { text = fmt.Sprintf("%s: %dx%d %s", s.img_metadata.Format_uppercase, s.img_metadata.Width, s.img_metadata.Height, humanize.Bytes(uint64(s.metadata.Size()))) } - h.render_wrapped_text_in_region(text, s.x, s.y, s.width, s.height, false) - return 0 + return h.render_wrapped_text_in_region(text, s.x, s.y, s.width, s.height, false) } func NewImagePreview( @@ -211,7 +228,7 @@ func NewImagePreview( ) (Preview, error) { dc_size.Store(opts.DiskCacheSize()) ans := &ImagePreview{ - abspath: abspath, metadata: metadata, render_channel: make(chan render_data), + abspath: abspath, metadata: metadata, render_channel: make(chan render_data, 1), WakeupMainThread: WakeupMainThread, renderer: r, } if dc, err := preview_cache(); err != nil { diff --git a/kittens/choose_files/preview.go b/kittens/choose_files/preview.go index da860803d..76ffbe578 100644 --- a/kittens/choose_files/preview.go +++ b/kittens/choose_files/preview.go @@ -259,13 +259,19 @@ func (pm *PreviewManager) highlight_file_async(path string, output chan highligh s := style_resolver{light: use_light_colors, syntax_aliases: pm.settings.SyntaxAliases()} s.light_style, s.dark_style = pm.settings.HighlightStyles() go func() { + defer func() { + if r := recover(); r != nil { + text, _ := utils.Format_stacktrace_on_panic(r) + debugprintln(fmt.Sprintf("Failed to highlight: %s with panic: %s", path, text)) + } + close(output) + pm.WakeupMainThread() + }() highlighted, err := pm.highlighter.HighlightFile(path, &s) if err != nil { debugprintln(fmt.Sprintf("Failed to highlight: %s with error: %s", path, err)) } output <- highlighed_data{text: highlighted, err: err, light: s.light} - close(output) - pm.WakeupMainThread() }() }