MarkdownDocument: a Markdown file in one CodeEditor, shown visual, source, split or preview
Visual draws the top-level blocks in a gpui list; a pressed block opens as its source and its typing is the text at once, written back as one undo step. CodeEditor takes the owner's line colors, resets with a fresh history and keeps at most 64 MiB of undo text. MarkdownMode gains Preview. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
9036c92442
commit
2b6f5513aa
19 files changed
+1267
-86
No files matched your search
+20
-57
@@ -8,6 +8,7 @@ use gpui::{
|
||||
use markdown::mdast::AlignKind;
|
||||
|
||||
use super::{
|
||||
contents::anchor,
|
||||
diagram::Diagram,
|
||||
picture::Picture,
|
||||
tree::{Inline, Node, Style, parse},
|
||||
@@ -20,7 +21,7 @@ use crate::{
|
||||
typography::{Latex, failed},
|
||||
};
|
||||
|
||||
type OnLink = Rc<dyn Fn(&str, &mut Window, &mut App)>;
|
||||
pub(crate) type OnLink = Rc<dyn Fn(&str, &mut Window, &mut App)>;
|
||||
type DrawCode = Rc<dyn Fn(ElementId, Option<SharedString>, SharedString) -> AnyElement>;
|
||||
|
||||
/// Markdown as a document: headings, prose with its styles and links, lists and tasks, quotes, code colored as code, tables, rules, math and footnotes. A press on a link opens it, or hands it to the host.
|
||||
@@ -401,37 +402,24 @@ fn open_link(on_link: &Option<OnLink>, url: &str, window: &mut Window, cx: &mut
|
||||
}
|
||||
}
|
||||
|
||||
/// The anchor of the document's `ordinal`th heading.
|
||||
fn anchor(ordinal: usize) -> SharedString {
|
||||
format!("heading-{ordinal}").into()
|
||||
}
|
||||
|
||||
/// The headings of `markdown` in order, as a table of contents takes them: anchor, text and depth from 0.
|
||||
pub fn outline(markdown: &str) -> Vec<(SharedString, SharedString, usize)> {
|
||||
fn walk(nodes: &[Node], out: &mut Vec<(SharedString, SharedString, usize)>) {
|
||||
for node in nodes {
|
||||
match node {
|
||||
Node::Heading(level, inline) => {
|
||||
let ordinal = out.len();
|
||||
out.push((
|
||||
anchor(ordinal),
|
||||
inline.text.clone().into(),
|
||||
*level as usize - 1,
|
||||
));
|
||||
}
|
||||
Node::Quote(inner) | Node::Footnote { body: inner, .. } => walk(inner, out),
|
||||
Node::List { items, .. } => {
|
||||
for (_, body) in items {
|
||||
walk(body, out);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut out = Vec::new();
|
||||
walk(&parse(markdown), &mut out);
|
||||
out
|
||||
/// Parsed `nodes` drawn under `id`; pictures read from `base`.
|
||||
pub(crate) fn draw_nodes(
|
||||
id: &ElementId,
|
||||
nodes: &[Node],
|
||||
base: &Option<Arc<Path>>,
|
||||
on_link: &Option<OnLink>,
|
||||
cx: &App,
|
||||
) -> Vec<AnyElement> {
|
||||
let draw = Draw {
|
||||
id,
|
||||
on_link,
|
||||
next: std::cell::Cell::new(0),
|
||||
sections: &None,
|
||||
headings: std::cell::Cell::new(0),
|
||||
code: &None,
|
||||
base,
|
||||
};
|
||||
draw.nodes(nodes, cx)
|
||||
}
|
||||
|
||||
impl RenderOnce for MarkdownRenderer {
|
||||
@@ -456,28 +444,3 @@ impl RenderOnce for MarkdownRenderer {
|
||||
.children(draw.nodes(&nodes, cx))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::outline;
|
||||
|
||||
#[test]
|
||||
fn the_outline_numbers_headings_in_order_with_their_depth() {
|
||||
let found =
|
||||
outline("# Lift\n\ntext\n\n## In light\n\n> ### Quoted\n\n- #### Listed\n\n## In dark");
|
||||
let shape: Vec<(&str, &str, usize)> = found
|
||||
.iter()
|
||||
.map(|(anchor, text, depth)| (anchor.as_ref(), text.as_ref(), *depth))
|
||||
.collect();
|
||||
assert_eq!(
|
||||
shape,
|
||||
[
|
||||
("heading-0", "Lift", 0),
|
||||
("heading-1", "In light", 1),
|
||||
("heading-2", "Quoted", 2),
|
||||
("heading-3", "Listed", 3),
|
||||
("heading-4", "In dark", 1),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user