| author | |
| committer | |
| log | c0f040a1b3c5f865374e3027102048483ec0c0b2 |
| tree | 11072d5d01b9f0b18bcedd268177c126bf432d31 |
| parent | c796d5ef4aa87f2db690d0090873c0c94c3aae04 |
| signature |
11 files changed, 45 insertions(+), 73 deletions(-)
Cargo.toml-3| ... | ... | @@ -19,6 +19,3 @@ serde_json = "1.0" |
| 19 | 19 | serde_yml = "0.0.12" |
| 20 | 20 | wasm-bindgen = { version = "0.2" } |
| 21 | 21 | serde-wasm-bindgen = "0.6.5" |
| 22 | ||
| 23 | [rust-analyzer.cargo] | |
| 24 | features = ["wasm"] |
src/component_transforms.rs+10-10| ... | ... | @@ -14,27 +14,27 @@ const SUFFIX: &str = "__markodown__"; |
| 14 | 14 | |
| 15 | 15 | /// Heading component name |
| 16 | 16 | fn heading_component() -> String { |
| 17 | format!("HeadingComponent{}", SUFFIX) | |
| 17 | format!("HeadingComponent{SUFFIX}") | |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | 20 | /// Code block component name |
| 21 | 21 | fn code_block_component() -> String { |
| 22 | format!("CodeBlockComponent{}", SUFFIX) | |
| 22 | format!("CodeBlockComponent{SUFFIX}") | |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | /// Link component name |
| 26 | 26 | fn link_component() -> String { |
| 27 | format!("LinkComponent{}", SUFFIX) | |
| 27 | format!("LinkComponent{SUFFIX}") | |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | 30 | /// Image component name |
| 31 | 31 | fn image_component() -> String { |
| 32 | format!("ImageComponent{}", SUFFIX) | |
| 32 | format!("ImageComponent{SUFFIX}") | |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | /// Blockquote component name |
| 36 | 36 | fn blockquote_component() -> String { |
| 37 | format!("BlockquoteComponent{}", SUFFIX) | |
| 37 | format!("BlockquoteComponent{SUFFIX}") | |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | 40 | /// Generate import statements for all configured component imports |
| ... | ... | @@ -199,7 +199,7 @@ fn transform_heading(node: &mut Node) { |
| 199 | 199 | |
| 200 | 200 | // Create a new MarkoBlockComplete to replace this node |
| 201 | 201 | let mut open = OpenOwned::from_tag_name(&heading_component()); |
| 202 | open.insert_attr(&format!("level={}", level)); | |
| 202 | open.insert_attr(&format!("level={level}")); | |
| 203 | 203 | |
| 204 | 204 | // We need to take ownership of children |
| 205 | 205 | let children = std::mem::take(&mut node.children); |
| ... | ... | @@ -222,7 +222,7 @@ fn transform_heading(node: &mut Node) { |
| 222 | 222 | let level = heading.level; |
| 223 | 223 | |
| 224 | 224 | let mut open = OpenOwned::from_tag_name(&heading_component()); |
| 225 | open.insert_attr(&format!("level={}", level)); | |
| 225 | open.insert_attr(&format!("level={level}")); | |
| 226 | 226 | |
| 227 | 227 | let children = std::mem::take(&mut node.children); |
| 228 | 228 | |
| ... | ... | @@ -241,7 +241,7 @@ fn transform_heading(node: &mut Node) { |
| 241 | 241 | if let Some(marko_open) = node.cast_mut::<MarkoOpen>() { |
| 242 | 242 | if let Some(level) = parse_heading_level(marko_open.open.as_ref().tag_name()) { |
| 243 | 243 | marko_open.open.replace_tag_name(&heading_component()); |
| 244 | marko_open.open.insert_attr(&format!("level={}", level)); | |
| 244 | marko_open.open.insert_attr(&format!("level={level}")); | |
| 245 | 245 | } |
| 246 | 246 | return; |
| 247 | 247 | } |
| ... | ... | @@ -250,7 +250,7 @@ fn transform_heading(node: &mut Node) { |
| 250 | 250 | if let Some(marko_open) = node.cast_mut::<MarkoOpenWithText>() { |
| 251 | 251 | if let Some(level) = parse_heading_level(marko_open.open.as_ref().tag_name()) { |
| 252 | 252 | marko_open.open.replace_tag_name(&heading_component()); |
| 253 | marko_open.open.insert_attr(&format!("level={}", level)); | |
| 253 | marko_open.open.insert_attr(&format!("level={level}")); | |
| 254 | 254 | } |
| 255 | 255 | return; |
| 256 | 256 | } |
| ... | ... | @@ -259,7 +259,7 @@ fn transform_heading(node: &mut Node) { |
| 259 | 259 | if let Some(marko_block) = node.cast_mut::<MarkoBlockComplete>() { |
| 260 | 260 | if let Some(level) = parse_heading_level(marko_block.open.as_ref().tag_name()) { |
| 261 | 261 | marko_block.open.replace_tag_name(&heading_component()); |
| 262 | marko_block.open.insert_attr(&format!("level={}", level)); | |
| 262 | marko_block.open.insert_attr(&format!("level={level}")); | |
| 263 | 263 | // Also update close tag to generic </> |
| 264 | 264 | marko_block.close_tag = "</>".to_string(); |
| 265 | 265 | } |
src/lib.rs+5-13| ... | ... | @@ -156,18 +156,11 @@ pub fn transform( |
| 156 | 156 | |
| 157 | 157 | if let Some(self_path) = self_import { |
| 158 | 158 | text = format!( |
| 159 | "import Layout__markodown__ from \"{}\";import * as self__markodown__ from \"{}\";\n{}<Layout__markodown__ module=self__markodown__ outline={outline_array}>\n{}</>", | |
| 160 | layout_path, | |
| 161 | self_path, | |
| 162 | hoisted, | |
| 163 | text | |
| 159 | "import Layout__markodown__ from \"{layout_path}\";import * as self__markodown__ from \"{self_path}\";\n{hoisted}<Layout__markodown__ module=self__markodown__ outline={outline_array}>\n{text}</>" | |
| 164 | 160 | ); |
| 165 | 161 | } else { |
| 166 | 162 | text = format!( |
| 167 | "import Layout__markodown__ from \"{}\";\n{}<Layout__markodown__ module=null outline={outline_array}>\n{}</>", | |
| 168 | layout_path, | |
| 169 | hoisted, | |
| 170 | text | |
| 163 | "import Layout__markodown__ from \"{layout_path}\";\n{hoisted}<Layout__markodown__ module=null outline={outline_array}>\n{text}</>" | |
| 171 | 164 | ); |
| 172 | 165 | } |
| 173 | 166 | } else { |
| ... | ... | @@ -245,7 +238,7 @@ fn has_marko_features(node: &markdown_it::Node) -> bool { |
| 245 | 238 | || node.cast::<plugin::tags::MarkoOpenWithText>().is_some() |
| 246 | 239 | || node.cast::<plugin::tags::MarkoInlineTag>().is_some() |
| 247 | 240 | || node.cast::<plugin::tags::MarkoBlockComplete>().is_some() |
| 248 | || node.children.iter().any(|child| has_marko_features(child)) | |
| 241 | || node.children.iter().any(has_marko_features) | |
| 249 | 242 | } |
| 250 | 243 | |
| 251 | 244 | /// Validate that Marko open/close tags are properly matched |
| ... | ... | @@ -299,8 +292,7 @@ fn validate_marko_tags( |
| 299 | 292 | // Mismatched: expected </top_name>, got </name> |
| 300 | 293 | errors.push( |
| 301 | 294 | OxcDiagnostic::error(format!( |
| 302 | "Mismatched closing tag: expected </{}>, found </{}>", | |
| 303 | top_name, name | |
| 295 | "Mismatched closing tag: expected </{top_name}>, found </{name}>" | |
| 304 | 296 | )) |
| 305 | 297 | .with_labels(vec![ |
| 306 | 298 | top_span.label("opened here"), |
| ... | ... | @@ -337,7 +329,7 @@ fn validate_marko_tags( |
| 337 | 329 | |
| 338 | 330 | // Check for unclosed tags |
| 339 | 331 | for (name, span) in stack { |
| 340 | errors.push(OxcDiagnostic::error(format!("Unclosed tag <{}>", name)).with_label(span)); | |
| 332 | errors.push(OxcDiagnostic::error(format!("Unclosed tag <{name}>")).with_label(span)); | |
| 341 | 333 | } |
| 342 | 334 | } |
| 343 | 335 |
src/marko.rs+9-18| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | use oxc_diagnostics::OxcDiagnostic; |
| 2 | 2 | use oxc_span::Span; |
| 3 | use serde_yml::libyml::tag; | |
| 4 | 3 | |
| 5 | 4 | use crate::{ |
| 6 | 5 | err, |
| ... | ... | @@ -201,12 +200,10 @@ pub fn parse_open(src: &str) -> Result<Open, OxcDiagnostic> { |
| 201 | 200 | } |
| 202 | 201 | } |
| 203 | 202 | } |
| 203 | } else if l.expect("/>").is_ok() { | |
| 204 | self_closing = true; | |
| 204 | 205 | } else { |
| 205 | if l.expect("/>").is_ok() { | |
| 206 | self_closing = true; | |
| 207 | } else { | |
| 208 | l.expect(">")?; | |
| 209 | } | |
| 206 | l.expect(">")?; | |
| 210 | 207 | } |
| 211 | 208 | |
| 212 | 209 | Ok(Open::new( |
| ... | ... | @@ -245,13 +242,10 @@ fn parse_tag_name<'a>(l: &mut LexState<'a>) -> Result<&'a str, OxcDiagnostic> { |
| 245 | 242 | return Ok(""); |
| 246 | 243 | } |
| 247 | 244 | let start = l.offset; |
| 248 | loop { | |
| 249 | match l.peek_byte() { | |
| 250 | Some(b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'-' | b'$' | b'@' | b'0'..=b'9') => { | |
| 251 | l.offset += 1 | |
| 252 | } | |
| 253 | _ => break, | |
| 254 | } | |
| 245 | while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'-' | b'$' | b'@' | b'0'..=b'9') = | |
| 246 | l.peek_byte() | |
| 247 | { | |
| 248 | l.offset += 1 | |
| 255 | 249 | } |
| 256 | 250 | let tag_name = &l.src[start as usize..l.offset as usize]; |
| 257 | 251 | if !tag_name.is_empty() { |
| ... | ... | @@ -263,11 +257,8 @@ fn parse_tag_name<'a>(l: &mut LexState<'a>) -> Result<&'a str, OxcDiagnostic> { |
| 263 | 257 | |
| 264 | 258 | fn parse_attr_name<'a>(l: &mut LexState<'a>) -> Result<&'a str, OxcDiagnostic> { |
| 265 | 259 | let start = l.offset; |
| 266 | loop { | |
| 267 | match l.peek_byte() { | |
| 268 | Some(b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'-' | b':') => l.offset += 1, | |
| 269 | _ => break, | |
| 270 | } | |
| 260 | while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'-' | b':') = l.peek_byte() { | |
| 261 | l.offset += 1; | |
| 271 | 262 | } |
| 272 | 263 | let tag_name = &l.src[start as usize..l.offset as usize]; |
| 273 | 264 | if !tag_name.is_empty() { |
src/marko_ast.rs+5-11| ... | ... | @@ -53,7 +53,7 @@ impl<'a> Open<'a> { |
| 53 | 53 | let mut l = LexState::new(self.src); |
| 54 | 54 | l.advance(3); |
| 55 | 55 | parse_expr(&mut l).expect("validated text should pass"); |
| 56 | Span::new(3, l.offset() as u32) | |
| 56 | Span::new(3, l.offset()) | |
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | |
| ... | ... | @@ -180,7 +180,7 @@ impl OpenOwned { |
| 180 | 180 | /// For example: `<Heading#foo class="bar">` with `level=3` → `<Heading#foo level=3 class="bar">` |
| 181 | 181 | pub fn insert_attr(&mut self, attr: &str) { |
| 182 | 182 | let insert_pos = self.shorthand_end as usize; |
| 183 | let insert_str = format!(" {}", attr); | |
| 183 | let insert_str = format!(" {attr}"); | |
| 184 | 184 | let delta = insert_str.len(); |
| 185 | 185 | |
| 186 | 186 | // Build new source |
| ... | ... | @@ -208,7 +208,7 @@ impl OpenOwned { |
| 208 | 208 | /// Create a new OpenOwned from a tag name (for synthesizing tags). |
| 209 | 209 | /// Creates a simple `<tagname>` with no attributes. |
| 210 | 210 | pub fn from_tag_name(tag_name: &str) -> Self { |
| 211 | let src = format!("<{}>", tag_name); | |
| 211 | let src = format!("<{tag_name}>"); | |
| 212 | 212 | let tag_name_span = Span::new(1, 1 + tag_name.len() as u32); |
| 213 | 213 | OpenOwned { |
| 214 | 214 | src, |
| ... | ... | @@ -293,14 +293,8 @@ impl<'a> LexState<'a> { |
| 293 | 293 | } |
| 294 | 294 | |
| 295 | 295 | pub fn skip_whitespace(&mut self) { |
| 296 | loop { | |
| 297 | match self.peek_byte() { | |
| 298 | Some(byte) => match byte { | |
| 299 | b' ' | b'\n' | b'\t' | b'\r' => self.offset += 1, | |
| 300 | _ => break, | |
| 301 | }, | |
| 302 | None => break, | |
| 303 | } | |
| 296 | while let Some(b' ' | b'\n' | b'\t' | b'\r') = self.peek_byte() { | |
| 297 | self.offset += 1; | |
| 304 | 298 | } |
| 305 | 299 | } |
| 306 | 300 | } |
src/outline.rs+7-7| ... | ... | @@ -76,7 +76,7 @@ pub fn generate_slug(text: &str, existing_ids: &mut HashSet<String>) -> String { |
| 76 | 76 | |
| 77 | 77 | let mut counter = 1; |
| 78 | 78 | loop { |
| 79 | let new_slug = format!("{}-{}", slug, counter); | |
| 79 | let new_slug = format!("{slug}-{counter}"); | |
| 80 | 80 | if !existing_ids.contains(&new_slug) { |
| 81 | 81 | existing_ids.insert(new_slug.clone()); |
| 82 | 82 | return new_slug; |
| ... | ... | @@ -140,7 +140,7 @@ fn collect_recursive( |
| 140 | 140 | |
| 141 | 141 | // Generate component name |
| 142 | 142 | *heading_counter += 1; |
| 143 | let component_name = format!("Heading_{}__markodown__", heading_counter); | |
| 143 | let component_name = format!("Heading_{heading_counter}__markodown__"); | |
| 144 | 144 | |
| 145 | 145 | // Render children before replacing them |
| 146 | 146 | let rendered_content = render_children(node); |
| ... | ... | @@ -176,7 +176,7 @@ fn collect_recursive( |
| 176 | 176 | let id = generate_slug(&text, existing_ids); |
| 177 | 177 | |
| 178 | 178 | *heading_counter += 1; |
| 179 | let component_name = format!("Heading_{}__markodown__", heading_counter); | |
| 179 | let component_name = format!("Heading_{heading_counter}__markodown__"); | |
| 180 | 180 | |
| 181 | 181 | let rendered_content = render_children(node); |
| 182 | 182 | |
| ... | ... | @@ -208,7 +208,7 @@ fn collect_recursive( |
| 208 | 208 | let id = generate_slug(&text, existing_ids); |
| 209 | 209 | |
| 210 | 210 | *heading_counter += 1; |
| 211 | let component_name = format!("Heading_{}__markodown__", heading_counter); | |
| 211 | let component_name = format!("Heading_{heading_counter}__markodown__"); | |
| 212 | 212 | |
| 213 | 213 | let rendered_content = render_children(node); |
| 214 | 214 | |
| ... | ... | @@ -288,7 +288,7 @@ fn collect_recursive( |
| 288 | 288 | }; |
| 289 | 289 | |
| 290 | 290 | *heading_counter += 1; |
| 291 | let component_name = format!("Heading_{}__markodown__", heading_counter); | |
| 291 | let component_name = format!("Heading_{heading_counter}__markodown__"); | |
| 292 | 292 | |
| 293 | 293 | let rendered_content = render_children(node); |
| 294 | 294 | |
| ... | ... | @@ -367,7 +367,7 @@ fn collect_recursive( |
| 367 | 367 | }; |
| 368 | 368 | |
| 369 | 369 | *heading_counter += 1; |
| 370 | let component_name = format!("Heading_{}__markodown__", heading_counter); | |
| 370 | let component_name = format!("Heading_{heading_counter}__markodown__"); | |
| 371 | 371 | |
| 372 | 372 | let rendered_content = render_children(node); |
| 373 | 373 | |
| ... | ... | @@ -446,7 +446,7 @@ fn collect_recursive( |
| 446 | 446 | }; |
| 447 | 447 | |
| 448 | 448 | *heading_counter += 1; |
| 449 | let component_name = format!("Heading_{}__markodown__", heading_counter); | |
| 449 | let component_name = format!("Heading_{heading_counter}__markodown__"); | |
| 450 | 450 | |
| 451 | 451 | let rendered_content = render_children(node); |
| 452 | 452 |
src/plugin/frontmatter.rs+2-2| ... | ... | @@ -147,8 +147,8 @@ pub fn extract_preamble_and_frontmatter( |
| 147 | 147 | |
| 148 | 148 | // Calculate bytes consumed (including the trailing newline after closing ---) |
| 149 | 149 | let mut bytes_consumed = 0; |
| 150 | for i in 0..end_line { | |
| 151 | bytes_consumed += lines[i].len() + 1; // +1 for newline | |
| 150 | for line in lines.iter().take(end_line) { | |
| 151 | bytes_consumed += line.len() + 1; // +1 for newline | |
| 152 | 152 | } |
| 153 | 153 | |
| 154 | 154 | // Consume one trailing blank line for separation if present |
src/plugin/mod.rs+1-1| ... | ... | @@ -34,7 +34,7 @@ pub(crate) struct ErrorBlock { |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | impl NodeValue for ErrorBlock { |
| 37 | fn render(&self, _: &Node, r: &mut dyn Renderer) { | |
| 37 | fn render(&self, _: &Node, _: &mut dyn Renderer) { | |
| 38 | 38 | panic!("cannot render ErrorBlock"); |
| 39 | 39 | } |
| 40 | 40 | } |
src/plugin/tags.rs+1-3| ... | ... | @@ -165,9 +165,7 @@ fn find_same_line_close<'a>(content: &'a str, tag_name: &str) -> Option<(&'a str |
| 165 | 165 | } |
| 166 | 166 | pos = lt_pos + close.length as usize; |
| 167 | 167 | } else { |
| 168 | if close.tag_name.is_none() { | |
| 169 | stack.pop(); | |
| 170 | } else if stack.last() == close.tag_name.as_ref() { | |
| 168 | if close.tag_name.is_none() || stack.last() == close.tag_name.as_ref() { | |
| 171 | 169 | stack.pop(); |
| 172 | 170 | } |
| 173 | 171 | pos = lt_pos + close.length as usize; |
src/typescript.rs+4-4| ... | ... | @@ -3,7 +3,7 @@ use crate::{adjust_err, err, marko_ast::LexState}; |
| 3 | 3 | |
| 4 | 4 | use oxc_allocator::Allocator; |
| 5 | 5 | use oxc_ast::ast::{Expression, Statement}; |
| 6 | use oxc_diagnostics::{LabeledSpan, OxcDiagnostic}; | |
| 6 | use oxc_diagnostics::OxcDiagnostic; | |
| 7 | 7 | use oxc_span::{GetSpan, SourceType}; |
| 8 | 8 | |
| 9 | 9 | pub fn scan_first_statement_forbid_trailing(source: &str) -> Result<u32, OxcDiagnostic> { |
| ... | ... | @@ -13,7 +13,7 @@ pub fn scan_first_statement_forbid_trailing(source: &str) -> Result<u32, OxcDiag |
| 13 | 13 | let len = span.end - span.start; |
| 14 | 14 | |
| 15 | 15 | if let Some(trailing) = source[span.end as usize..].lines().next() { |
| 16 | if trailing.trim().len() > 0 { | |
| 16 | if !trailing.trim().is_empty() { | |
| 17 | 17 | return Err(err( |
| 18 | 18 | "Trailing content not allowed here", |
| 19 | 19 | span.end + (trailing.len() - trailing.trim_start().len()) as u32, |
| ... | ... | @@ -42,7 +42,7 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( |
| 42 | 42 | .with_typescript(true); |
| 43 | 43 | |
| 44 | 44 | let mut result = oxc_parser::Parser::new(allocator, source, source_type).parse(); |
| 45 | if !result.errors.is_empty() && result.program.body.len() == 0 { | |
| 45 | if !result.errors.is_empty() && result.program.body.is_empty() { | |
| 46 | 46 | let first_err = result |
| 47 | 47 | .errors |
| 48 | 48 | .into_iter() |
| ... | ... | @@ -69,7 +69,7 @@ fn parse_stmt_extra<'alloc, 'src: 'alloc>( |
| 69 | 69 | return Err(adjust_err(first_err, offset)); |
| 70 | 70 | } |
| 71 | 71 | result = oxc_parser::Parser::new(allocator, candidate, source_type).parse(); |
| 72 | if (!result.errors.is_empty() || result.panicked) && result.program.body.len() == 0 { | |
| 72 | if (!result.errors.is_empty() || result.panicked) && result.program.body.is_empty() { | |
| 73 | 73 | let before_trim = &candidate[..candidate.len() - 1]; |
| 74 | 74 | let after_trim = before_trim.trim_end(); |
| 75 | 75 | if after_trim.len() < before_trim.len() { |
src/wasm.rs+1-1| ... | ... | @@ -49,7 +49,7 @@ fn offset_to_line_col(src: &str, offset: usize) -> (u32, u32) { |
| 49 | 49 | /// - Takes the first label and absorbs its position into the root error. |
| 50 | 50 | /// - If there are multiple labels or labels with text, forward them. |
| 51 | 51 | fn convert_diagnostic(src: &str, diag: &OxcDiagnostic) -> WasmDiagnostic { |
| 52 | let labels = diag.labels.as_ref().map(|l| l.as_slice()).unwrap_or(&[]); | |
| 52 | let labels = diag.labels.as_deref().unwrap_or(&[]); | |
| 53 | 53 | |
| 54 | 54 | // Determine root line/column from first label, or default to 1:1 |
| 55 | 55 | let (line, column) = if let Some(first) = labels.first() { |