-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add initial markdown preview to Zed (#6958)
Adds a "markdown: open preview" action to open a markdown preview. https://github.com/zed-industries/zed/assets/18583882/6fd7f009-53f7-4f98-84ea-7dd3f0dd11bf This PR extends the work done in `crates/rich_text` to render markdown to also support: - Variable heading sizes - Markdown tables - Code blocks - Block quotes ## Release Notes - Added `Markdown: Open preview` action to partially close ([#6789](#6789)). ## Known issues that will not be included in this PR - Images. - Nested block quotes. - Footnote Reference. - Headers highlighting. - Inline code highlighting (this will need to be implemented in `rich_text`) - Checkboxes (`- [ ]` and `- [x]`) - Syntax highlighting in code blocks. - Markdown table text alignment. - Inner markdown URL clicks
- Loading branch information
1 parent
3b88291
commit 8bafc61
Showing
14 changed files
with
547 additions
and
8 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
[package] | ||
name = "markdown_preview" | ||
version = "0.1.0" | ||
edition = "2021" | ||
publish = false | ||
license = "GPL-3.0-or-later" | ||
|
||
[lib] | ||
path = "src/markdown_preview.rs" | ||
|
||
[features] | ||
test-support = [] | ||
|
||
[dependencies] | ||
editor = { path = "../editor" } | ||
gpui = { path = "../gpui" } | ||
language = { path = "../language" } | ||
menu = { path = "../menu" } | ||
project = { path = "../project" } | ||
theme = { path = "../theme" } | ||
ui = { path = "../ui" } | ||
util = { path = "../util" } | ||
workspace = { path = "../workspace" } | ||
rich_text = { path = "../rich_text" } | ||
|
||
anyhow.workspace = true | ||
lazy_static.workspace = true | ||
log.workspace = true | ||
pulldown-cmark.workspace = true | ||
|
||
[dev-dependencies] | ||
editor = { path = "../editor", features = ["test-support"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../../LICENSE-GPL |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
use gpui::{actions, AppContext}; | ||
use workspace::Workspace; | ||
|
||
pub mod markdown_preview_view; | ||
pub mod markdown_renderer; | ||
|
||
actions!(markdown, [OpenPreview]); | ||
|
||
pub fn init(cx: &mut AppContext) { | ||
cx.observe_new_views(|workspace: &mut Workspace, cx| { | ||
markdown_preview_view::MarkdownPreviewView::register(workspace, cx); | ||
}) | ||
.detach(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
use editor::{Editor, EditorEvent}; | ||
use gpui::{ | ||
canvas, AnyElement, AppContext, AvailableSpace, EventEmitter, FocusHandle, FocusableView, | ||
InteractiveElement, IntoElement, ParentElement, Render, Styled, View, ViewContext, | ||
}; | ||
use language::LanguageRegistry; | ||
use std::sync::Arc; | ||
use ui::prelude::*; | ||
use workspace::item::Item; | ||
use workspace::Workspace; | ||
|
||
use crate::{markdown_renderer::render_markdown, OpenPreview}; | ||
|
||
pub struct MarkdownPreviewView { | ||
focus_handle: FocusHandle, | ||
languages: Arc<LanguageRegistry>, | ||
contents: String, | ||
} | ||
|
||
impl MarkdownPreviewView { | ||
pub fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) { | ||
let languages = workspace.app_state().languages.clone(); | ||
|
||
workspace.register_action(move |workspace, _: &OpenPreview, cx| { | ||
if workspace.has_active_modal(cx) { | ||
cx.propagate(); | ||
return; | ||
} | ||
let languages = languages.clone(); | ||
if let Some(editor) = workspace.active_item_as::<Editor>(cx) { | ||
let view: View<MarkdownPreviewView> = | ||
cx.new_view(|cx| MarkdownPreviewView::new(editor, languages, cx)); | ||
workspace.split_item(workspace::SplitDirection::Right, Box::new(view.clone()), cx); | ||
cx.notify(); | ||
} | ||
}); | ||
} | ||
|
||
pub fn new( | ||
active_editor: View<Editor>, | ||
languages: Arc<LanguageRegistry>, | ||
cx: &mut ViewContext<Self>, | ||
) -> Self { | ||
let focus_handle = cx.focus_handle(); | ||
|
||
cx.subscribe(&active_editor, |this, editor, event: &EditorEvent, cx| { | ||
if *event == EditorEvent::Edited { | ||
let editor = editor.read(cx); | ||
let contents = editor.buffer().read(cx).snapshot(cx).text(); | ||
this.contents = contents; | ||
cx.notify(); | ||
} | ||
}) | ||
.detach(); | ||
|
||
let editor = active_editor.read(cx); | ||
let contents = editor.buffer().read(cx).snapshot(cx).text(); | ||
|
||
Self { | ||
focus_handle, | ||
languages, | ||
contents, | ||
} | ||
} | ||
} | ||
|
||
impl FocusableView for MarkdownPreviewView { | ||
fn focus_handle(&self, _: &AppContext) -> gpui::FocusHandle { | ||
self.focus_handle.clone() | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub enum PreviewEvent {} | ||
|
||
impl EventEmitter<PreviewEvent> for MarkdownPreviewView {} | ||
|
||
impl Item for MarkdownPreviewView { | ||
type Event = PreviewEvent; | ||
|
||
fn tab_content( | ||
&self, | ||
_detail: Option<usize>, | ||
selected: bool, | ||
_cx: &WindowContext, | ||
) -> AnyElement { | ||
h_flex() | ||
.gap_2() | ||
.child(Icon::new(IconName::FileDoc).color(if selected { | ||
Color::Default | ||
} else { | ||
Color::Muted | ||
})) | ||
.child(Label::new("Markdown preview").color(if selected { | ||
Color::Default | ||
} else { | ||
Color::Muted | ||
})) | ||
.into_any() | ||
} | ||
|
||
fn telemetry_event_text(&self) -> Option<&'static str> { | ||
Some("markdown preview") | ||
} | ||
|
||
fn to_item_events(_event: &Self::Event, _f: impl FnMut(workspace::item::ItemEvent)) {} | ||
} | ||
|
||
impl Render for MarkdownPreviewView { | ||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { | ||
let rendered_markdown = v_flex() | ||
.items_start() | ||
.justify_start() | ||
.key_context("MarkdownPreview") | ||
.track_focus(&self.focus_handle) | ||
.id("MarkdownPreview") | ||
.overflow_scroll() | ||
.size_full() | ||
.bg(cx.theme().colors().editor_background) | ||
.p_4() | ||
.children(render_markdown(&self.contents, &self.languages, cx)); | ||
|
||
div().flex_1().child( | ||
canvas(move |bounds, cx| { | ||
rendered_markdown.into_any().draw( | ||
bounds.origin, | ||
bounds.size.map(AvailableSpace::Definite), | ||
cx, | ||
) | ||
}) | ||
.size_full(), | ||
) | ||
} | ||
} |
Oops, something went wrong.