From 3b3b1c079aa441c02c9b5d893df63e68c7376487 Mon Sep 17 00:00:00 2001 From: Lucas Pickering Date: Sun, 27 Oct 2024 11:59:39 -0400 Subject: [PATCH] Optimize display of large text bodies It turns out calculating the width of a text body is expensive for large bodies, because we have to count every grapheme. This change caches the text dimensions so we only have to calculate it when the text changes. Further progress on #356 --- CHANGELOG.md | 3 + .../tui/src/view/common/template_preview.rs | 20 ++-- crates/tui/src/view/common/text_window.rs | 112 +++++++++++------- .../tui/src/view/component/queryable_body.rs | 67 +++++------ crates/tui/src/view/component/request_view.rs | 11 +- crates/tui/src/view/state.rs | 29 +++++ 6 files changed, 155 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97eed30a..8c37ed7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed - Fixed `ignore_certificate_hosts` and `large_body_size` fields not being loaded from config +- Improve performance of large response bodies [#356](https://github.com/LucasPickering/slumber/issues/356) + - This includes disabling prettyification and syntax highlighting on bodies over 1 MB (this size is configurable, via the `large_body_size` [config field](https://slumber.lucaspickering.me/book/api/configuration/index.html)) + - Loading a large response body should no longer cause the UI to freeze or low framerate ## [2.2.0] - 2024-10-22 diff --git a/crates/tui/src/view/common/template_preview.rs b/crates/tui/src/view/common/template_preview.rs index de016b6d..76dcd111 100644 --- a/crates/tui/src/view/common/template_preview.rs +++ b/crates/tui/src/view/common/template_preview.rs @@ -1,7 +1,7 @@ use crate::{ context::TuiContext, message::Message, - view::{draw::Generate, util::highlight, ViewContext}, + view::{draw::Generate, state::Identified, util::highlight, ViewContext}, }; use ratatui::{ buffer::Buffer, @@ -35,7 +35,7 @@ pub struct TemplatePreview { /// because it also gets an initial value. There should be effectively zero /// contention on the mutex because of the single write, and reads being /// single-threaded. - text: Arc>>, + text: Arc>>>, } impl TemplatePreview { @@ -46,7 +46,7 @@ impl TemplatePreview { /// unrendered and rendered content. pub fn new(template: Template, content_type: Option) -> Self { // Calculate raw text - let text = highlight::highlight_if( + let text: Identified = highlight::highlight_if( content_type, // We have to clone the template to detach the lifetime. We're // choosing to pay one upfront cost here so we don't have to @@ -54,7 +54,8 @@ impl TemplatePreview { // the template and have this text reference it, but that would be // self-referential template.display().into_owned().into(), - ); + ) + .into(); let text = Arc::new(Mutex::new(text)); // Trigger a task to render the preview and write the answer back into @@ -78,17 +79,17 @@ impl TemplatePreview { /// mutex fn calculate_rendered_text( chunks: Vec, - destination: &Mutex>, + destination: &Mutex>>, content_type: Option, ) { let text = TextStitcher::stitch_chunks(&chunks); let text = highlight::highlight_if(content_type, text); *destination .lock() - .expect("Template preview text lock is poisoned") = text; + .expect("Template preview text lock is poisoned") = text.into(); } - pub fn text(&self) -> impl '_ + Deref> { + pub fn text(&self) -> impl '_ + Deref>> { self.text .lock() .expect("Template preview text lock is poisoned") @@ -103,7 +104,8 @@ impl From