Strip planning line and drawers from section body

section_body() now filters out:
- Leading planning line (SCHEDULED/DEADLINE/CLOSED)
- Property drawers (:PROPERTIES: ... :END:)
- Any other drawers (:LOGBOOK:, etc.)
- Leading/trailing blank lines

These are displayed separately in the UI.
This commit is contained in:
Levi Neely 2026-10-01 10:09:39 +02:00
parent ff48f9c7ee
commit f5f2bffce4
1 changed files with 53 additions and 5 deletions

View File

@ -461,11 +461,11 @@ impl OrkState {
}
/// Get section body content (text between headline and first child).
/// Strips planning line and properties drawer.
pub fn section_body(&self, doc_name: &str, id: &str) -> Option<String> {
self.get_doc(doc_name).and_then(|d| {
self.find_section(&d.doc, id).map(|s| {
// Body is section content minus children
// For now, extract from raw content using spans
let start = s.headline.span.end.offset;
let end = if let Some(first_child) = s.children.first() {
first_child.headline.span.start.offset
@ -473,11 +473,59 @@ impl OrkState {
s.span.end.offset
};
if start < end && end <= d.content.len() {
d.content[start..end].to_string()
} else {
String::new()
if start >= end || end > d.content.len() {
return String::new();
}
let raw_body = &d.content[start..end];
// Strip leading planning line (SCHEDULED/DEADLINE/CLOSED)
// and properties drawer (:PROPERTIES: ... :END:)
let lines: Vec<&str> = raw_body.lines().collect();
let mut result_lines = Vec::new();
let mut in_drawer = false;
let mut first_line = true;
for line in lines {
let trimmed = line.trim();
// Skip planning line at the very start
if first_line && (trimmed.starts_with("SCHEDULED:") ||
trimmed.starts_with("DEADLINE:") ||
trimmed.starts_with("CLOSED:")) {
first_line = false;
continue;
}
first_line = false;
// Handle drawer
if trimmed.starts_with(":") && trimmed.ends_with(":") && trimmed.len() > 2 {
let drawer_name = &trimmed[1..trimmed.len()-1];
if drawer_name.chars().all(|c| c.is_ascii_uppercase() || c == '_') {
in_drawer = true;
continue;
}
}
if in_drawer {
if trimmed == ":END:" {
in_drawer = false;
}
continue;
}
result_lines.push(line);
}
// Trim leading/trailing empty lines
while result_lines.first().map(|l| l.trim().is_empty()).unwrap_or(false) {
result_lines.remove(0);
}
while result_lines.last().map(|l| l.trim().is_empty()).unwrap_or(false) {
result_lines.pop();
}
result_lines.join("\n")
})
})
}