MarkdownDocument: a Markdown file in one CodeEditor, shown visual, source, split or preview
CI / site (push) Successful in 34s
CI / platforms (windows-latest) (push) Successful in 5m7s
CI / crate (push) Failing after 26m45s
CI / platforms (ubuntu-24.04) (push) Canceled after 0s

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:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-12 00:44:17 -04:00
1 parent 9036c92442
commit 2b6f5513aa
19 files changed
+1267 -86

No files matched your search

+20 -57
View File
@@ -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),
]
);
}
}