From 0d35d8db74ab0b3c4df60a198b0aa378f5e0fe09 Mon Sep 17 00:00:00 2001 From: Orual Date: Tue, 2 Dec 2025 22:29:56 -0500 Subject: [PATCH] fixed the stupid code block bug --- .../src/components/editor/render.rs | 31 ++++++++- .../src/components/editor/writer.rs | 68 ++++++++++++++++--- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/crates/weaver-app/src/components/editor/render.rs b/crates/weaver-app/src/components/editor/render.rs index 56bb842..fb97d3c 100644 --- a/crates/weaver-app/src/components/editor/render.rs +++ b/crates/weaver-app/src/components/editor/render.rs @@ -159,7 +159,10 @@ pub fn render_paragraphs_incremental( // Determine if we can use fast path (skip boundary discovery) // Need cache and non-boundary-affecting edit info (for edit position) let current_len = text.len_unicode(); - let use_fast_path = cache.is_some() && edit.is_some() && !is_boundary_affecting(edit.unwrap()); + + let use_fast_path = cache.is_some() + && edit.is_some() + && !is_boundary_affecting(edit.unwrap()); tracing::debug!( target: "weaver::render", @@ -218,7 +221,29 @@ pub fn render_paragraphs_incremental( }) .collect::>() } else { - // Slow path: run boundary-only pass to discover paragraph boundaries + vec![] // Will be populated by slow path below + }; + + // Validate fast path results - if any ranges are invalid, use slow path + let paragraph_ranges = if !paragraph_ranges.is_empty() { + let all_valid = paragraph_ranges + .iter() + .all(|(_, char_range)| char_range.start <= char_range.end); + if all_valid { + paragraph_ranges + } else { + tracing::debug!( + target: "weaver::render", + "fast path produced invalid ranges, falling back to slow path" + ); + vec![] // Trigger slow path + } + } else { + paragraph_ranges + }; + + // Slow path: run boundary-only pass to discover paragraph boundaries + let paragraph_ranges = if paragraph_ranges.is_empty() { let parser = Parser::new_ext(&source, weaver_renderer::default_md_options()).into_offset_iter(); let mut scratch_output = String::new(); @@ -234,6 +259,8 @@ pub fn render_paragraphs_incremental( Ok(result) => result.paragraph_ranges, Err(_) => return (Vec::new(), RenderCache::default(), vec![]), } + } else { + paragraph_ranges }; // Log discovered paragraphs diff --git a/crates/weaver-app/src/components/editor/writer.rs b/crates/weaver-app/src/components/editor/writer.rs index 178898d..c01bb65 100644 --- a/crates/weaver-app/src/components/editor/writer.rs +++ b/crates/weaver-app/src/components/editor/writer.rs @@ -338,6 +338,8 @@ pub struct EditorWriter<'a, I: Iterator, Range)>, W: St code_buffer: Option<(Option, String)>, // (lang, content) code_buffer_byte_range: Option>, // byte range of buffered code content code_buffer_char_range: Option>, // char range of buffered code content + code_block_char_start: Option, // char offset where code block started + code_block_opening_span_idx: Option, // index of opening fence syntax span pending_blockquote_range: Option>, // range for emitting > inside next paragraph // Table rendering mode @@ -455,6 +457,8 @@ impl< code_buffer: None, code_buffer_byte_range: None, code_buffer_char_range: None, + code_block_char_start: None, + code_block_opening_span_idx: None, pending_blockquote_range: None, render_tables_as_markdown: true, // Default to markdown rendering table_start_offset: None, @@ -503,6 +507,8 @@ impl< code_buffer: None, code_buffer_byte_range: None, code_buffer_char_range: None, + code_block_char_start: None, + code_block_opening_span_idx: None, pending_blockquote_range: None, render_tables_as_markdown: true, table_start_offset: None, @@ -545,6 +551,8 @@ impl< code_buffer: self.code_buffer, code_buffer_byte_range: self.code_buffer_byte_range, code_buffer_char_range: self.code_buffer_char_range, + code_block_char_start: self.code_block_char_start, + code_block_opening_span_idx: self.code_block_opening_span_idx, pending_blockquote_range: self.pending_blockquote_range, render_tables_as_markdown: self.render_tables_as_markdown, table_start_offset: self.table_start_offset, @@ -590,6 +598,8 @@ impl< code_buffer: self.code_buffer, code_buffer_byte_range: self.code_buffer_byte_range, code_buffer_char_range: self.code_buffer_char_range, + code_block_char_start: self.code_block_char_start, + code_block_opening_span_idx: self.code_block_opening_span_idx, pending_blockquote_range: self.pending_blockquote_range, render_tables_as_markdown: self.render_tables_as_markdown, table_start_offset: self.table_start_offset, @@ -1973,11 +1983,15 @@ impl< escape_html(&mut self.writer, syntax)?; self.write("\n")?; + // Track opening span index for formatted_range update later + self.code_block_opening_span_idx = Some(self.syntax_spans.len()); + self.code_block_char_start = Some(char_start); + self.syntax_spans.push(SyntaxSpanInfo { syn_id, char_range: char_start..char_end, syntax_type: SyntaxType::Block, - formatted_range: None, + formatted_range: None, // Will be set in TagEnd::CodeBlock }); self.last_char_offset += syntax_char_len; @@ -2570,6 +2584,9 @@ impl< self.record_mapping(code_byte_range, code_char_range); } + // Get node_id for data-node-id attribute (needed for cursor positioning) + let node_id = self.current_node_id.clone(); + if let Some(ref lang_str) = lang { // Use a temporary String buffer for syntect let mut temp_output = String::new(); @@ -2580,11 +2597,25 @@ impl< &mut temp_output, ) { Ok(_) => { - self.write(&temp_output)?; + // Inject data-node-id into the
 tag for cursor positioning
+                                if let Some(ref nid) = node_id {
+                                    let injected = temp_output.replacen(
+                                        "
",
+                                        &format!("
", nid),
+                                        1,
+                                    );
+                                    self.write(&injected)?;
+                                } else {
+                                    self.write(&temp_output)?;
+                                }
                             }
                             Err(_) => {
                                 // Fallback to plain code block
-                                self.write("
")?;
                                 escape_html_body_text(&mut self.writer, &buffer)?;
@@ -2592,7 +2623,11 @@ impl<
                             }
                         }
                     } else {
-                        self.write("
")?;
+                        if let Some(ref nid) = node_id {
+                            write!(&mut self.writer, "
", nid)?;
+                        } else {
+                            self.write("
")?;
+                        }
                         escape_html_body_text(&mut self.writer, &buffer)?;
                         self.write("
\n")?; } @@ -2604,6 +2639,10 @@ impl< } // Emit closing ``` (emit_gap_before is skipped while buffering) + // Track the opening span index and char start before we potentially clear them + let opening_span_idx = self.code_block_opening_span_idx.take(); + let code_block_start = self.code_block_char_start.take(); + if range.start < range.end { let raw_text = &self.source[range.clone()]; if let Some(fence_line) = raw_text.lines().last() { @@ -2623,15 +2662,28 @@ impl< escape_html(&mut self.writer, fence)?; self.write("")?; + self.last_char_offset += fence_char_len; + self.last_byte_offset += fence.len(); + + // Compute formatted_range for entire code block (opening fence to closing fence) + let formatted_range = code_block_start + .map(|start| start..self.last_char_offset); + + // Update opening fence span with formatted_range + if let (Some(idx), Some(fr)) = (opening_span_idx, formatted_range.as_ref()) + { + if let Some(span) = self.syntax_spans.get_mut(idx) { + span.formatted_range = Some(fr.clone()); + } + } + + // Push closing fence span with formatted_range self.syntax_spans.push(SyntaxSpanInfo { syn_id, char_range: char_start..char_end, syntax_type: SyntaxType::Block, - formatted_range: None, + formatted_range, }); - - self.last_char_offset += fence_char_len; - self.last_byte_offset += fence.len(); } } } -- knotmirror 0.0.1