feat: handle <thead> in html export
This commit is contained in:
parent
1362624083
commit
db7fb70724
5 changed files with 300 additions and 14 deletions
|
|
@ -1,6 +1,57 @@
|
|||
use super::OrgTableRow;
|
||||
use rowan::ast::AstNode;
|
||||
|
||||
use super::{OrgTable, OrgTableRow};
|
||||
use crate::syntax::SyntaxKind;
|
||||
|
||||
impl OrgTable {
|
||||
/// Returns `true` if this table has a header
|
||||
///
|
||||
/// A table has a header when it contains at least two row groups.
|
||||
///
|
||||
/// ```rust
|
||||
/// use orgize::{Org, ast::OrgTable};
|
||||
///
|
||||
/// let org = Org::parse(r#"
|
||||
/// | a | b |
|
||||
/// |---+---|
|
||||
/// | c | d |"#);
|
||||
/// let table = org.first_node::<OrgTable>().unwrap();
|
||||
/// assert!(table.has_header());
|
||||
///
|
||||
/// let org = Org::parse(r#"
|
||||
/// | a | b |
|
||||
/// | 0 | 1 |
|
||||
/// |---+---|
|
||||
/// | a | w |"#);
|
||||
/// let table = org.first_node::<OrgTable>().unwrap();
|
||||
/// assert!(table.has_header());
|
||||
///
|
||||
/// let org = Org::parse(r#"
|
||||
/// | a | b |
|
||||
/// | c | d |"#);
|
||||
/// let table = org.first_node::<OrgTable>().unwrap();
|
||||
/// assert!(!table.has_header());
|
||||
///
|
||||
/// let org = Org::parse(r#"
|
||||
/// |---+---|
|
||||
/// | a | b |
|
||||
/// | c | d |
|
||||
/// |---+---|"#);
|
||||
/// let table = org.first_node::<OrgTable>().unwrap();
|
||||
/// assert!(!table.has_header());
|
||||
/// ```
|
||||
pub fn has_header(&self) -> bool {
|
||||
self.syntax
|
||||
.children()
|
||||
.filter_map(OrgTableRow::cast)
|
||||
.skip_while(|row| row.is_rule())
|
||||
.skip_while(|row| row.is_standard())
|
||||
.skip_while(|row| row.is_rule())
|
||||
.next()
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl OrgTableRow {
|
||||
/// Returns `true` if this row is a rule
|
||||
///
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@
|
|||
/// let level = title.headline().and_then(|h| h.level()).unwrap_or(1);
|
||||
/// let level = min(level, 6);
|
||||
/// let raw = title.syntax().to_string();
|
||||
/// self.0.output += &format!("<h{level}><a id=\"{0}\" href=\"#{0}\">", slugify!(&raw));
|
||||
/// self.0.push_str(format!("<h{level}><a id=\"{0}\" href=\"#{0}\">", slugify!(&raw)));
|
||||
/// }
|
||||
/// WalkEvent::Leave(title) => {
|
||||
/// let level = title.headline().and_then(|h| h.level()).unwrap_or(1);
|
||||
/// let level = min(level, 6);
|
||||
/// self.0.output += &format!("</a></h{level}>");
|
||||
/// self.0.push_str(format!("</a></h{level}>"));
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -47,11 +47,27 @@ impl<S: AsRef<str>> fmt::Display for HtmlEscape<S> {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct HtmlExport {
|
||||
pub output: String,
|
||||
output: String,
|
||||
|
||||
in_descriptive_list: Vec<bool>,
|
||||
|
||||
table_row: TableRow,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
enum TableRow {
|
||||
#[default]
|
||||
HeaderRule,
|
||||
Header,
|
||||
BodyRule,
|
||||
Body,
|
||||
}
|
||||
|
||||
impl HtmlExport {
|
||||
pub fn push_str(&mut self, s: impl AsRef<str>) {
|
||||
self.output += s.as_ref();
|
||||
}
|
||||
|
||||
pub fn finish(self) -> String {
|
||||
self.output
|
||||
}
|
||||
|
|
@ -306,10 +322,28 @@ impl Traverser for HtmlExport {
|
|||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
fn org_table(&mut self, event: WalkEvent<&OrgTable>, _ctx: &mut TraversalContext) {
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<table><tbody>",
|
||||
WalkEvent::Leave(_) => "</tbody></table>",
|
||||
};
|
||||
match event {
|
||||
WalkEvent::Enter(table) => {
|
||||
self.output += "<table>";
|
||||
self.table_row = if table.has_header() {
|
||||
TableRow::HeaderRule
|
||||
} else {
|
||||
TableRow::BodyRule
|
||||
}
|
||||
}
|
||||
WalkEvent::Leave(_) => {
|
||||
match self.table_row {
|
||||
TableRow::Body => {
|
||||
self.output += "</tbody>";
|
||||
}
|
||||
TableRow::Header => {
|
||||
self.output += "</thead>";
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.output += "</table>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, ctx))]
|
||||
|
|
@ -317,13 +351,39 @@ impl Traverser for HtmlExport {
|
|||
if match event {
|
||||
WalkEvent::Enter(n) | WalkEvent::Leave(n) => n.is_rule(),
|
||||
} {
|
||||
match self.table_row {
|
||||
TableRow::Body => {
|
||||
self.output += "</tbody>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
TableRow::Header => {
|
||||
self.output += "</thead>";
|
||||
self.table_row = TableRow::BodyRule;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return ctx.skip();
|
||||
}
|
||||
|
||||
self.output += match event {
|
||||
WalkEvent::Enter(_) => "<tr>",
|
||||
WalkEvent::Leave(_) => "</tr>",
|
||||
};
|
||||
match event {
|
||||
WalkEvent::Enter(_) => {
|
||||
match self.table_row {
|
||||
TableRow::HeaderRule => {
|
||||
self.table_row = TableRow::Header;
|
||||
self.output += "<thead>";
|
||||
}
|
||||
TableRow::BodyRule => {
|
||||
self.table_row = TableRow::Body;
|
||||
self.output += "<tbody>";
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.output += "<tr>";
|
||||
}
|
||||
WalkEvent::Leave(_) => {
|
||||
self.output += "</tr>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, _ctx))]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue