Skip to content

Commit 615124c

Browse files
committed
[tui] Editor component - remove content comparison from cache implemenation
1 parent bfca7af commit 615124c

File tree

3 files changed

+93
-72
lines changed

3 files changed

+93
-72
lines changed

CHANGELOG.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,13 @@
288288
<a id="markdown-next-release" name="next-release"></a>
289289

290290
- Fixed:
291-
- Fix the custom MD parser so that it correctly parses plain text. Also disable the
292-
syntect highlighter for the editor by default and just use the custom MD parser. For
293-
files that are not Markdown, we will probably need to enable syntect in the future
294-
since it is not covered by the custom MD parser & highlighter combo.
291+
- Fix the custom MD parser so that it correctly parses plain text.
292+
293+
- Changed:
294+
- In the editor component - disable the syntect highlighter for the editor by default
295+
and just use the custom MD parser. For files that are not Markdown, we will probably
296+
need to enable syntect in the future since it is not covered by the custom MD parser &
297+
highlighter combo.
295298

296299
- Added:
297300
- Add undo, redo support for the editor component.
@@ -303,6 +306,11 @@
303306
terminal emulator. Also added conversion function
304307
`convert_tui_color_into_r3bl_ansi_color()` to convert from `TuiColor` to
305308
`r3bl_ansi_term::Color`.
309+
- In editor component, add support for caching rendered output of content. When the
310+
content changes, or the viewport size or window size change, the cache is invalidated.
311+
This is useful for performance reasons. It also leverages the undo/redo system for
312+
cache invalidation (which makes it fast to invalidate the render ops cache w/out
313+
having to do a content comparison to detect changes).
306314

307315
- Changed:
308316
- Redux is no longer used in order to propagate state transitions from async middleware

tui/src/tui/editor/editor_buffer/editor_buffer_struct.rs

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ use crate::*;
170170
pub struct EditorBuffer {
171171
editor_content: EditorContent,
172172
pub(crate) history: EditorBufferHistory,
173-
pub render_cache: RenderCache,
173+
pub render_cache: HashMap<String, RenderOps>,
174174
}
175175

176176
#[derive(Clone, PartialEq, Serialize, Deserialize, GetSize, Default)]
@@ -188,12 +188,6 @@ pub struct EditorBufferHistory {
188188
current_index: isize,
189189
}
190190

191-
#[derive(Clone, PartialEq, Serialize, Deserialize, Default, GetSize)]
192-
pub struct RenderCache {
193-
cache_map: HashMap<String, RenderOps>,
194-
editor_content: EditorContent,
195-
}
196-
197191
impl Default for EditorBufferHistory {
198192
fn default() -> Self {
199193
Self {
@@ -211,6 +205,9 @@ pub mod history {
211205
}
212206

213207
pub fn push(editor_buffer: &mut EditorBuffer) {
208+
// Invalidate the content cache, since the content just changed.
209+
cache::clear(editor_buffer);
210+
214211
let content_copy = editor_buffer.editor_content.clone();
215212

216213
// Delete the history from the current version index to the end.
@@ -233,6 +230,9 @@ pub mod history {
233230
}
234231

235232
pub fn undo(editor_buffer: &mut EditorBuffer) {
233+
// Invalidate the content cache, since the content just changed.
234+
cache::clear(editor_buffer);
235+
236236
let retain_caret_position = editor_buffer.editor_content.caret_display_position;
237237
if let Some(content) = editor_buffer.history.previous_content() {
238238
editor_buffer.editor_content = content;
@@ -245,6 +245,9 @@ pub mod history {
245245
}
246246

247247
pub fn redo(editor_buffer: &mut EditorBuffer) {
248+
// Invalidate the content cache, since the content just changed.
249+
cache::clear(editor_buffer);
250+
248251
if let Some(content) = editor_buffer.history.next_content() {
249252
editor_buffer.editor_content = content;
250253
}
@@ -546,47 +549,49 @@ mod constructor {
546549
pub mod cache {
547550
use super::*;
548551

549-
impl EditorBuffer {
550-
/// Render the content of the editor buffer to the screen from the cache if the content
551-
/// has not been modified.
552-
///
553-
/// The cache miss occurs if
554-
/// - Scroll Offset changes
555-
/// - Window size changes
556-
/// - Content of the editor changes
557-
pub fn render_content_from_cache(
558-
&mut self,
559-
key: String,
560-
render_args: &RenderArgs<'_>,
561-
render_ops: &mut RenderOps,
562-
) {
563-
if let Some(cached_output) = self.render_cache.cache_map.get(&key) {
564-
if &self.render_cache.editor_content.lines == &self.editor_content.lines {
565-
// Cache hit
566-
*render_ops = cached_output.clone();
567-
} else {
568-
// Content has been modified.
569-
self.handle_cache_miss(key, render_args, render_ops);
570-
}
571-
} else {
572-
// Scroll Offset or Window size has been modified.
573-
self.handle_cache_miss(key, render_args, render_ops);
574-
}
575-
}
552+
pub fn clear(editor_buffer: &mut EditorBuffer) { editor_buffer.render_cache.clear(); }
576553

577-
/// When the cache miss occurs, the entire buffer needs to be re-rendered.
578-
/// Cache Map is cleared and the new render ops are stored in the cache.
579-
fn handle_cache_miss(
580-
&mut self,
581-
key: String,
582-
render_args: &RenderArgs<'_>,
583-
render_ops: &mut RenderOps,
584-
) {
585-
self.render_cache.cache_map.clear();
586-
EditorEngineApi::render_content(render_args, render_ops);
587-
self.render_cache.editor_content = self.editor_content.clone();
588-
self.render_cache.cache_map.insert(key, render_ops.clone());
589-
}
554+
/// Cache key is combination of scroll_offset and window_size.
555+
fn generate_key(editor_buffer: &EditorBuffer, window_size: Size) -> String {
556+
format!("{}{}", editor_buffer.get_scroll_offset(), window_size,)
557+
}
558+
559+
/// Render the content of the editor buffer to the screen from the cache if the content
560+
/// has not been modified.
561+
///
562+
/// The cache miss occurs if
563+
/// - Scroll Offset changes
564+
/// - Window size changes
565+
/// - Content of the editor changes
566+
pub fn render_content(
567+
editor_buffer: &mut EditorBuffer,
568+
editor_engine: &mut EditorEngine,
569+
window_size: Size,
570+
has_focus: &mut HasFocus,
571+
render_ops: &mut RenderOps,
572+
) {
573+
let key = generate_key(editor_buffer, window_size);
574+
if let Some(cached_output) = editor_buffer.render_cache.get(&key) {
575+
// Cache hit
576+
*render_ops = cached_output.clone();
577+
return;
578+
}
579+
580+
// Cache miss, due to either:
581+
// - Content has been modified.
582+
// - Scroll Offset or Window size has been modified.
583+
editor_buffer.render_cache.clear();
584+
let render_args = RenderArgs {
585+
editor_engine,
586+
editor_buffer,
587+
has_focus,
588+
};
589+
590+
// Re-render content, generate & write to render_ops.
591+
EditorEngineApi::render_content(&render_args, render_ops);
592+
593+
// Snapshot the render_ops in the cache.
594+
editor_buffer.render_cache.insert(key, render_ops.clone());
590595
}
591596
}
592597
pub enum CaretKind {

tui/src/tui/editor/editor_engine/editor_engine_api.rs

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -121,31 +121,39 @@ impl EditorEngineApi {
121121
throws_with_return!({
122122
editor_engine.current_box = current_box.into();
123123

124-
// Create reusable args for render functions.
125-
let render_args = RenderArgs {
126-
editor_buffer: &editor_buffer.clone(),
127-
editor_engine,
128-
has_focus,
129-
};
130-
131124
if editor_buffer.is_empty() {
132-
EditorEngineApi::render_empty_state(&render_args)
125+
EditorEngineApi::render_empty_state(RenderArgs {
126+
editor_buffer,
127+
editor_engine,
128+
has_focus,
129+
})
133130
} else {
134131
let mut render_ops = render_ops!();
135-
// Cache key is combination of scroll_offset and window_size.
136-
let cache_key = format!(
137-
"{}{}",
138-
editor_buffer.get_scroll_offset().to_string(),
139-
window_size.to_string()
132+
133+
cache::render_content(
134+
editor_buffer,
135+
editor_engine,
136+
window_size,
137+
has_focus,
138+
&mut render_ops,
140139
);
141140

142-
editor_buffer.render_content_from_cache(
143-
cache_key,
144-
&render_args,
141+
EditorEngineApi::render_selection(
142+
RenderArgs {
143+
editor_buffer,
144+
editor_engine,
145+
has_focus,
146+
},
147+
&mut render_ops,
148+
);
149+
EditorEngineApi::render_caret(
150+
RenderArgs {
151+
editor_buffer,
152+
editor_engine,
153+
has_focus,
154+
},
145155
&mut render_ops,
146156
);
147-
EditorEngineApi::render_selection(&render_args, &mut render_ops);
148-
EditorEngineApi::render_caret(&render_args, &mut render_ops);
149157

150158
let mut render_pipeline = render_pipeline!();
151159
render_pipeline.push(ZOrder::Normal, render_ops);
@@ -205,7 +213,7 @@ impl EditorEngineApi {
205213
}
206214

207215
// BOOKM: Render selection
208-
fn render_selection(render_args: &RenderArgs<'_>, render_ops: &mut RenderOps) {
216+
fn render_selection(render_args: RenderArgs<'_>, render_ops: &mut RenderOps) {
209217
let RenderArgs {
210218
editor_buffer,
211219
editor_engine,
@@ -292,7 +300,7 @@ impl EditorEngineApi {
292300
}
293301
}
294302

295-
fn render_caret(render_args: &RenderArgs<'_>, render_ops: &mut RenderOps) {
303+
fn render_caret(render_args: RenderArgs<'_>, render_ops: &mut RenderOps) {
296304
let RenderArgs {
297305
editor_buffer,
298306
editor_engine,
@@ -327,7 +335,7 @@ impl EditorEngineApi {
327335
}
328336
}
329337

330-
pub fn render_empty_state(render_args: &RenderArgs<'_>) -> RenderPipeline {
338+
pub fn render_empty_state(render_args: RenderArgs<'_>) -> RenderPipeline {
331339
let RenderArgs {
332340
has_focus,
333341
editor_engine,

0 commit comments

Comments
 (0)