mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 05:15:00 +00:00
Don't need editor style to parse markdown
This commit is contained in:
parent
a881b1f5fb
commit
8dca4c3f9a
4 changed files with 133 additions and 205 deletions
|
@ -60,6 +60,7 @@ use itertools::Itertools;
|
||||||
pub use language::{char_kind, CharKind};
|
pub use language::{char_kind, CharKind};
|
||||||
use language::{
|
use language::{
|
||||||
language_settings::{self, all_language_settings, InlayHintSettings},
|
language_settings::{self, all_language_settings, InlayHintSettings},
|
||||||
|
markdown::MarkdownHighlight,
|
||||||
point_from_lsp, prepare_completion_documentation, AutoindentMode, BracketPair, Buffer,
|
point_from_lsp, prepare_completion_documentation, AutoindentMode, BracketPair, Buffer,
|
||||||
CodeAction, CodeLabel, Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation,
|
CodeAction, CodeLabel, Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation,
|
||||||
File, IndentKind, IndentSize, Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point,
|
File, IndentKind, IndentSize, Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point,
|
||||||
|
@ -120,21 +121,57 @@ pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis
|
||||||
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
pub fn render_parsed_markdown(
|
pub fn render_parsed_markdown(
|
||||||
md: &language::ParsedMarkdown,
|
parsed: &language::ParsedMarkdown,
|
||||||
style: &EditorStyle,
|
editor_style: &EditorStyle,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Text {
|
) -> Text {
|
||||||
enum RenderedMarkdown {}
|
enum RenderedMarkdown {}
|
||||||
|
|
||||||
let md = md.clone();
|
let parsed = parsed.clone();
|
||||||
let code_span_background_color = style.document_highlight_read_background;
|
|
||||||
let view_id = cx.view_id();
|
let view_id = cx.view_id();
|
||||||
|
let code_span_background_color = editor_style.document_highlight_read_background;
|
||||||
|
|
||||||
let mut region_id = 0;
|
let mut region_id = 0;
|
||||||
Text::new(md.text, style.text.clone())
|
|
||||||
.with_highlights(md.highlights)
|
Text::new(parsed.text, editor_style.text.clone())
|
||||||
.with_custom_runs(md.region_ranges, move |ix, bounds, scene, _| {
|
.with_highlights(
|
||||||
|
parsed
|
||||||
|
.highlights
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(range, highlight)| {
|
||||||
|
let highlight = match highlight {
|
||||||
|
MarkdownHighlight::Style(style) => {
|
||||||
|
let mut highlight = HighlightStyle::default();
|
||||||
|
|
||||||
|
if style.italic {
|
||||||
|
highlight.italic = Some(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if style.underline {
|
||||||
|
highlight.underline = Some(fonts::Underline {
|
||||||
|
thickness: 1.0.into(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if style.weight != fonts::Weight::default() {
|
||||||
|
highlight.weight = Some(style.weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkdownHighlight::Code(id) => id.style(&editor_style.syntax)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((range.clone(), highlight))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.with_custom_runs(parsed.region_ranges, move |ix, bounds, scene, _| {
|
||||||
region_id += 1;
|
region_id += 1;
|
||||||
let region = md.regions[ix].clone();
|
let region = parsed.regions[ix].clone();
|
||||||
|
|
||||||
if let Some(url) = region.link_url {
|
if let Some(url) = region.link_url {
|
||||||
scene.push_cursor_region(CursorRegion {
|
scene.push_cursor_region(CursorRegion {
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -147,6 +184,7 @@ pub fn render_parsed_markdown(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if region.code {
|
if region.code {
|
||||||
scene.push_quad(gpui::Quad {
|
scene.push_quad(gpui::Quad {
|
||||||
bounds,
|
bounds,
|
||||||
|
@ -831,12 +869,11 @@ impl ContextMenu {
|
||||||
fn select_first(
|
fn select_first(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.visible() {
|
if self.visible() {
|
||||||
match self {
|
match self {
|
||||||
ContextMenu::Completions(menu) => menu.select_first(project, style, cx),
|
ContextMenu::Completions(menu) => menu.select_first(project, cx),
|
||||||
ContextMenu::CodeActions(menu) => menu.select_first(cx),
|
ContextMenu::CodeActions(menu) => menu.select_first(cx),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -848,12 +885,11 @@ impl ContextMenu {
|
||||||
fn select_prev(
|
fn select_prev(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.visible() {
|
if self.visible() {
|
||||||
match self {
|
match self {
|
||||||
ContextMenu::Completions(menu) => menu.select_prev(project, style, cx),
|
ContextMenu::Completions(menu) => menu.select_prev(project, cx),
|
||||||
ContextMenu::CodeActions(menu) => menu.select_prev(cx),
|
ContextMenu::CodeActions(menu) => menu.select_prev(cx),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -865,12 +901,11 @@ impl ContextMenu {
|
||||||
fn select_next(
|
fn select_next(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.visible() {
|
if self.visible() {
|
||||||
match self {
|
match self {
|
||||||
ContextMenu::Completions(menu) => menu.select_next(project, style, cx),
|
ContextMenu::Completions(menu) => menu.select_next(project, cx),
|
||||||
ContextMenu::CodeActions(menu) => menu.select_next(cx),
|
ContextMenu::CodeActions(menu) => menu.select_next(cx),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -882,12 +917,11 @@ impl ContextMenu {
|
||||||
fn select_last(
|
fn select_last(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if self.visible() {
|
if self.visible() {
|
||||||
match self {
|
match self {
|
||||||
ContextMenu::Completions(menu) => menu.select_last(project, style, cx),
|
ContextMenu::Completions(menu) => menu.select_last(project, cx),
|
||||||
ContextMenu::CodeActions(menu) => menu.select_last(cx),
|
ContextMenu::CodeActions(menu) => menu.select_last(cx),
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
@ -932,59 +966,54 @@ impl CompletionsMenu {
|
||||||
fn select_first(
|
fn select_first(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
self.selected_item = 0;
|
self.selected_item = 0;
|
||||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||||
self.attempt_resolve_selected_completion(project, style, cx);
|
self.attempt_resolve_selected_completion(project, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_prev(
|
fn select_prev(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
if self.selected_item > 0 {
|
if self.selected_item > 0 {
|
||||||
self.selected_item -= 1;
|
self.selected_item -= 1;
|
||||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||||
}
|
}
|
||||||
self.attempt_resolve_selected_completion(project, style, cx);
|
self.attempt_resolve_selected_completion(project, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_next(
|
fn select_next(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
if self.selected_item + 1 < self.matches.len() {
|
if self.selected_item + 1 < self.matches.len() {
|
||||||
self.selected_item += 1;
|
self.selected_item += 1;
|
||||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||||
}
|
}
|
||||||
self.attempt_resolve_selected_completion(project, style, cx);
|
self.attempt_resolve_selected_completion(project, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_last(
|
fn select_last(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
self.selected_item = self.matches.len() - 1;
|
self.selected_item = self.matches.len() - 1;
|
||||||
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
self.list.scroll_to(ScrollTarget::Show(self.selected_item));
|
||||||
self.attempt_resolve_selected_completion(project, style, cx);
|
self.attempt_resolve_selected_completion(project, cx);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attempt_resolve_selected_completion(
|
fn attempt_resolve_selected_completion(
|
||||||
&mut self,
|
&mut self,
|
||||||
project: Option<&ModelHandle<Project>>,
|
project: Option<&ModelHandle<Project>>,
|
||||||
style: theme::Editor,
|
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) {
|
) {
|
||||||
let index = self.matches[self.selected_item].candidate_id;
|
let index = self.matches[self.selected_item].candidate_id;
|
||||||
|
@ -1029,7 +1058,6 @@ impl CompletionsMenu {
|
||||||
&lsp_documentation,
|
&lsp_documentation,
|
||||||
&language_registry,
|
&language_registry,
|
||||||
None, // TODO: Try to reasonably work out which language the completion is for
|
None, // TODO: Try to reasonably work out which language the completion is for
|
||||||
&style,
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -1097,10 +1125,8 @@ impl CompletionsMenu {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("It is impossible have LSP servers without a project");
|
.expect("It is impossible have LSP servers without a project");
|
||||||
|
|
||||||
let lsp_docs = lsp_docs.clone();
|
|
||||||
let lsp_docs = lsp_docs.clone();
|
let lsp_docs = lsp_docs.clone();
|
||||||
let language_registry = project.read(cx).languages().clone();
|
let language_registry = project.read(cx).languages().clone();
|
||||||
let style = style.theme.clone();
|
|
||||||
let completions = completions.clone();
|
let completions = completions.clone();
|
||||||
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
|
@ -1108,7 +1134,6 @@ impl CompletionsMenu {
|
||||||
&lsp_docs,
|
&lsp_docs,
|
||||||
&language_registry,
|
&language_registry,
|
||||||
None,
|
None,
|
||||||
&style,
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -3365,11 +3390,7 @@ impl Editor {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
_ = this.update(&mut cx, |editor, cx| {
|
_ = this.update(&mut cx, |editor, cx| {
|
||||||
menu.attempt_resolve_selected_completion(
|
menu.attempt_resolve_selected_completion(editor.project.as_ref(), cx);
|
||||||
editor.project.as_ref(),
|
|
||||||
editor.style(cx).theme,
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
Some(menu)
|
Some(menu)
|
||||||
}
|
}
|
||||||
|
@ -5545,16 +5566,13 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.context_menu.is_some() {
|
if self
|
||||||
let style = self.style(cx).theme;
|
.context_menu
|
||||||
if self
|
.as_mut()
|
||||||
.context_menu
|
.map(|menu| menu.select_last(self.project.as_ref(), cx))
|
||||||
.as_mut()
|
.unwrap_or(false)
|
||||||
.map(|menu| menu.select_last(self.project.as_ref(), style, cx))
|
{
|
||||||
.unwrap_or(false)
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if matches!(self.mode, EditorMode::SingleLine) {
|
if matches!(self.mode, EditorMode::SingleLine) {
|
||||||
|
@ -5594,30 +5612,26 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
|
pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
|
||||||
let style = self.style(cx).theme;
|
|
||||||
if let Some(context_menu) = self.context_menu.as_mut() {
|
if let Some(context_menu) = self.context_menu.as_mut() {
|
||||||
context_menu.select_first(self.project.as_ref(), style, cx);
|
context_menu.select_first(self.project.as_ref(), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
|
pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
|
||||||
let style = self.style(cx).theme;
|
|
||||||
if let Some(context_menu) = self.context_menu.as_mut() {
|
if let Some(context_menu) = self.context_menu.as_mut() {
|
||||||
context_menu.select_prev(self.project.as_ref(), style, cx);
|
context_menu.select_prev(self.project.as_ref(), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
|
pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
|
||||||
let style = self.style(cx).theme;
|
|
||||||
if let Some(context_menu) = self.context_menu.as_mut() {
|
if let Some(context_menu) = self.context_menu.as_mut() {
|
||||||
context_menu.select_next(self.project.as_ref(), style, cx);
|
context_menu.select_next(self.project.as_ref(), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
|
pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
|
||||||
let style = self.style(cx).theme;
|
|
||||||
if let Some(context_menu) = self.context_menu.as_mut() {
|
if let Some(context_menu) = self.context_menu.as_mut() {
|
||||||
context_menu.select_last(self.project.as_ref(), style, cx);
|
context_menu.select_last(self.project.as_ref(), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,11 @@ use futures::FutureExt;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions,
|
actions,
|
||||||
elements::{Empty, Flex, MouseEventHandler, Padding, ParentElement, Text},
|
elements::{Empty, Flex, MouseEventHandler, Padding, ParentElement, Text},
|
||||||
fonts::HighlightStyle,
|
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
AnyElement, AppContext, CursorRegion, Element, ModelHandle, MouseRegion, Task, ViewContext,
|
AnyElement, AppContext, Element, ModelHandle, Task, ViewContext,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
markdown::{self, ParsedRegion},
|
markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
|
||||||
Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry,
|
|
||||||
};
|
};
|
||||||
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
|
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
|
||||||
use std::{ops::Range, sync::Arc, time::Duration};
|
use std::{ops::Range, sync::Arc, time::Duration};
|
||||||
|
@ -363,13 +361,11 @@ fn show_hover(
|
||||||
editor.hover_state.info_task = Some(task);
|
editor.hover_state.info_task = Some(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn render_blocks(
|
async fn parse_blocks(
|
||||||
theme_id: usize,
|
|
||||||
blocks: &[HoverBlock],
|
blocks: &[HoverBlock],
|
||||||
language_registry: &Arc<LanguageRegistry>,
|
language_registry: &Arc<LanguageRegistry>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
style: &theme::Editor,
|
) -> markdown::ParsedMarkdown {
|
||||||
) -> ParsedInfo {
|
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
let mut highlights = Vec::new();
|
let mut highlights = Vec::new();
|
||||||
let mut region_ranges = Vec::new();
|
let mut region_ranges = Vec::new();
|
||||||
|
@ -387,7 +383,6 @@ async fn render_blocks(
|
||||||
&block.text,
|
&block.text,
|
||||||
language_registry,
|
language_registry,
|
||||||
language.clone(),
|
language.clone(),
|
||||||
style,
|
|
||||||
&mut text,
|
&mut text,
|
||||||
&mut highlights,
|
&mut highlights,
|
||||||
&mut region_ranges,
|
&mut region_ranges,
|
||||||
|
@ -402,13 +397,7 @@ async fn render_blocks(
|
||||||
.now_or_never()
|
.now_or_never()
|
||||||
.and_then(Result::ok)
|
.and_then(Result::ok)
|
||||||
{
|
{
|
||||||
markdown::highlight_code(
|
markdown::highlight_code(&mut text, &mut highlights, &block.text, &language);
|
||||||
&mut text,
|
|
||||||
&mut highlights,
|
|
||||||
&block.text,
|
|
||||||
&language,
|
|
||||||
style,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
text.push_str(&block.text);
|
text.push_str(&block.text);
|
||||||
}
|
}
|
||||||
|
@ -416,8 +405,7 @@ async fn render_blocks(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ParsedInfo {
|
ParsedMarkdown {
|
||||||
theme_id,
|
|
||||||
text: text.trim().to_string(),
|
text: text.trim().to_string(),
|
||||||
highlights,
|
highlights,
|
||||||
region_ranges,
|
region_ranges,
|
||||||
|
@ -485,16 +473,7 @@ pub struct InfoPopover {
|
||||||
symbol_range: DocumentRange,
|
symbol_range: DocumentRange,
|
||||||
pub blocks: Vec<HoverBlock>,
|
pub blocks: Vec<HoverBlock>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
parsed_content: Option<ParsedInfo>,
|
parsed_content: Option<ParsedMarkdown>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct ParsedInfo {
|
|
||||||
theme_id: usize,
|
|
||||||
text: String,
|
|
||||||
highlights: Vec<(Range<usize>, HighlightStyle)>,
|
|
||||||
region_ranges: Vec<Range<usize>>,
|
|
||||||
regions: Vec<ParsedRegion>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InfoPopover {
|
impl InfoPopover {
|
||||||
|
@ -503,58 +482,14 @@ impl InfoPopover {
|
||||||
style: &EditorStyle,
|
style: &EditorStyle,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> AnyElement<Editor> {
|
) -> AnyElement<Editor> {
|
||||||
if let Some(parsed) = &self.parsed_content {
|
|
||||||
if parsed.theme_id != style.theme_id {
|
|
||||||
self.parsed_content = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rendered = if let Some(parsed) = &self.parsed_content {
|
let rendered = if let Some(parsed) = &self.parsed_content {
|
||||||
let view_id = cx.view_id();
|
crate::render_parsed_markdown(parsed, style, cx).into_any()
|
||||||
let regions = parsed.regions.clone();
|
|
||||||
let code_span_background_color = style.document_highlight_read_background;
|
|
||||||
|
|
||||||
let mut region_id = 0;
|
|
||||||
|
|
||||||
Text::new(parsed.text.clone(), style.text.clone())
|
|
||||||
.with_highlights(parsed.highlights.clone())
|
|
||||||
.with_custom_runs(parsed.region_ranges.clone(), move |ix, bounds, scene, _| {
|
|
||||||
region_id += 1;
|
|
||||||
let region = regions[ix].clone();
|
|
||||||
|
|
||||||
if let Some(url) = region.link_url {
|
|
||||||
scene.push_cursor_region(CursorRegion {
|
|
||||||
bounds,
|
|
||||||
style: CursorStyle::PointingHand,
|
|
||||||
});
|
|
||||||
scene.push_mouse_region(
|
|
||||||
MouseRegion::new::<Self>(view_id, region_id, bounds)
|
|
||||||
.on_click::<Editor, _>(MouseButton::Left, move |_, _, cx| {
|
|
||||||
cx.platform().open_url(&url)
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if region.code {
|
|
||||||
scene.push_quad(gpui::Quad {
|
|
||||||
bounds,
|
|
||||||
background: Some(code_span_background_color),
|
|
||||||
border: Default::default(),
|
|
||||||
corner_radii: (2.0).into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.with_soft_wrap(true)
|
|
||||||
.into_any()
|
|
||||||
} else {
|
} else {
|
||||||
let theme_id = style.theme_id;
|
|
||||||
let language_registry = self.project.read(cx).languages().clone();
|
let language_registry = self.project.read(cx).languages().clone();
|
||||||
let blocks = self.blocks.clone();
|
let blocks = self.blocks.clone();
|
||||||
let language = self.language.clone();
|
let language = self.language.clone();
|
||||||
let style = style.theme.clone();
|
|
||||||
cx.spawn(|this, mut cx| async move {
|
cx.spawn(|this, mut cx| async move {
|
||||||
let blocks =
|
let blocks = parse_blocks(&blocks, &language_registry, language).await;
|
||||||
render_blocks(theme_id, &blocks, &language_registry, language, &style).await;
|
|
||||||
_ = this.update(&mut cx, |_, cx| cx.notify());
|
_ = this.update(&mut cx, |_, cx| cx.notify());
|
||||||
blocks
|
blocks
|
||||||
})
|
})
|
||||||
|
@ -563,23 +498,6 @@ impl InfoPopover {
|
||||||
Empty::new().into_any()
|
Empty::new().into_any()
|
||||||
};
|
};
|
||||||
|
|
||||||
// let rendered_content = self.parsed_content.get_or_insert_with(|| {
|
|
||||||
// let language_registry = self.project.read(cx).languages().clone();
|
|
||||||
// cx.spawn(|this, mut cx| async move {
|
|
||||||
// let blocks = render_blocks(
|
|
||||||
// style.theme_id,
|
|
||||||
// &self.blocks,
|
|
||||||
// &language_registry,
|
|
||||||
// self.language.clone(),
|
|
||||||
// style,
|
|
||||||
// )
|
|
||||||
// .await;
|
|
||||||
// this.update(&mut cx, |_, cx| cx.notify());
|
|
||||||
// blocks
|
|
||||||
// })
|
|
||||||
// .shared()
|
|
||||||
// });
|
|
||||||
|
|
||||||
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
|
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.scrollable::<HoverBlock>(1, None, cx)
|
.scrollable::<HoverBlock>(1, None, cx)
|
||||||
|
@ -678,9 +596,12 @@ mod tests {
|
||||||
test::editor_lsp_test_context::EditorLspTestContext,
|
test::editor_lsp_test_context::EditorLspTestContext,
|
||||||
};
|
};
|
||||||
use collections::BTreeSet;
|
use collections::BTreeSet;
|
||||||
use gpui::fonts::{Underline, Weight};
|
use gpui::fonts::Weight;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
|
use language::{
|
||||||
|
language_settings::InlayHintSettings, markdown::MarkdownHighlightStyle, Diagnostic,
|
||||||
|
DiagnosticSet,
|
||||||
|
};
|
||||||
use lsp::LanguageServerId;
|
use lsp::LanguageServerId;
|
||||||
use project::{HoverBlock, HoverBlockKind};
|
use project::{HoverBlock, HoverBlockKind};
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
|
@ -893,7 +814,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
cx.condition(|editor, _| editor.hover_state.visible()).await;
|
cx.condition(|editor, _| editor.hover_state.visible()).await;
|
||||||
cx.editor(|editor, cx| {
|
cx.editor(|editor, _| {
|
||||||
let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
|
let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
blocks,
|
blocks,
|
||||||
|
@ -903,9 +824,7 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
let style = editor.style(cx);
|
let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
|
||||||
let rendered =
|
|
||||||
smol::block_on(render_blocks(0, &blocks, &Default::default(), None, &style));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
rendered.text,
|
rendered.text,
|
||||||
code_str.trim(),
|
code_str.trim(),
|
||||||
|
@ -984,16 +903,17 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_render_blocks(cx: &mut gpui::TestAppContext) {
|
fn test_render_blocks(cx: &mut gpui::TestAppContext) {
|
||||||
|
use markdown::MarkdownHighlight;
|
||||||
|
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
|
|
||||||
cx.add_window(|cx| {
|
cx.add_window(|cx| {
|
||||||
let editor = Editor::single_line(None, cx);
|
let editor = Editor::single_line(None, cx);
|
||||||
let style = editor.style(cx);
|
|
||||||
|
|
||||||
struct Row {
|
struct Row {
|
||||||
blocks: Vec<HoverBlock>,
|
blocks: Vec<HoverBlock>,
|
||||||
expected_marked_text: String,
|
expected_marked_text: String,
|
||||||
expected_styles: Vec<HighlightStyle>,
|
expected_styles: Vec<MarkdownHighlight>,
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = &[
|
let rows = &[
|
||||||
|
@ -1004,10 +924,10 @@ mod tests {
|
||||||
kind: HoverBlockKind::Markdown,
|
kind: HoverBlockKind::Markdown,
|
||||||
}],
|
}],
|
||||||
expected_marked_text: "one «two» three".to_string(),
|
expected_marked_text: "one «two» three".to_string(),
|
||||||
expected_styles: vec![HighlightStyle {
|
expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
|
||||||
weight: Some(Weight::BOLD),
|
weight: Weight::BOLD,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}],
|
})],
|
||||||
},
|
},
|
||||||
// Links
|
// Links
|
||||||
Row {
|
Row {
|
||||||
|
@ -1016,13 +936,10 @@ mod tests {
|
||||||
kind: HoverBlockKind::Markdown,
|
kind: HoverBlockKind::Markdown,
|
||||||
}],
|
}],
|
||||||
expected_marked_text: "one «two» three".to_string(),
|
expected_marked_text: "one «two» three".to_string(),
|
||||||
expected_styles: vec![HighlightStyle {
|
expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
|
||||||
underline: Some(Underline {
|
underline: true,
|
||||||
thickness: 1.0.into(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}],
|
})],
|
||||||
},
|
},
|
||||||
// Lists
|
// Lists
|
||||||
Row {
|
Row {
|
||||||
|
@ -1047,13 +964,10 @@ mod tests {
|
||||||
- «c»
|
- «c»
|
||||||
- d"
|
- d"
|
||||||
.unindent(),
|
.unindent(),
|
||||||
expected_styles: vec![HighlightStyle {
|
expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
|
||||||
underline: Some(Underline {
|
underline: true,
|
||||||
thickness: 1.0.into(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}],
|
})],
|
||||||
},
|
},
|
||||||
// Multi-paragraph list items
|
// Multi-paragraph list items
|
||||||
Row {
|
Row {
|
||||||
|
@ -1081,13 +995,10 @@ mod tests {
|
||||||
- ten
|
- ten
|
||||||
- six"
|
- six"
|
||||||
.unindent(),
|
.unindent(),
|
||||||
expected_styles: vec![HighlightStyle {
|
expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
|
||||||
underline: Some(Underline {
|
underline: true,
|
||||||
thickness: 1.0.into(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}],
|
})],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1097,8 +1008,7 @@ mod tests {
|
||||||
expected_styles,
|
expected_styles,
|
||||||
} in &rows[0..]
|
} in &rows[0..]
|
||||||
{
|
{
|
||||||
let rendered =
|
let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
|
||||||
smol::block_on(render_blocks(0, &blocks, &Default::default(), None, &style));
|
|
||||||
|
|
||||||
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
|
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
|
||||||
let expected_highlights = ranges
|
let expected_highlights = ranges
|
||||||
|
|
|
@ -149,7 +149,6 @@ pub async fn prepare_completion_documentation(
|
||||||
documentation: &lsp::Documentation,
|
documentation: &lsp::Documentation,
|
||||||
language_registry: &Arc<LanguageRegistry>,
|
language_registry: &Arc<LanguageRegistry>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
style: &theme::Editor,
|
|
||||||
) -> Option<Documentation> {
|
) -> Option<Documentation> {
|
||||||
match documentation {
|
match documentation {
|
||||||
lsp::Documentation::String(text) => {
|
lsp::Documentation::String(text) => {
|
||||||
|
@ -170,7 +169,7 @@ pub async fn prepare_completion_documentation(
|
||||||
}
|
}
|
||||||
|
|
||||||
lsp::MarkupKind::Markdown => {
|
lsp::MarkupKind::Markdown => {
|
||||||
let parsed = parse_markdown(value, language_registry, language, style).await;
|
let parsed = parse_markdown(value, language_registry, language).await;
|
||||||
Some(Documentation::MultiLineMarkdown(parsed))
|
Some(Documentation::MultiLineMarkdown(parsed))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{Language, LanguageRegistry};
|
use crate::{HighlightId, Language, LanguageRegistry};
|
||||||
use gpui::fonts::{HighlightStyle, Underline, Weight};
|
use gpui::fonts::Weight;
|
||||||
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
|
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParsedMarkdown {
|
pub struct ParsedMarkdown {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub highlights: Vec<(Range<usize>, HighlightStyle)>,
|
pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
|
||||||
pub region_ranges: Vec<Range<usize>>,
|
pub region_ranges: Vec<Range<usize>>,
|
||||||
pub regions: Vec<ParsedRegion>,
|
pub regions: Vec<ParsedRegion>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum MarkdownHighlight {
|
||||||
|
Style(MarkdownHighlightStyle),
|
||||||
|
Code(HighlightId),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
|
pub struct MarkdownHighlightStyle {
|
||||||
|
pub italic: bool,
|
||||||
|
pub underline: bool,
|
||||||
|
pub weight: Weight,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParsedRegion {
|
pub struct ParsedRegion {
|
||||||
pub code: bool,
|
pub code: bool,
|
||||||
|
@ -23,7 +36,6 @@ pub async fn parse_markdown(
|
||||||
markdown: &str,
|
markdown: &str,
|
||||||
language_registry: &Arc<LanguageRegistry>,
|
language_registry: &Arc<LanguageRegistry>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
style: &theme::Editor,
|
|
||||||
) -> ParsedMarkdown {
|
) -> ParsedMarkdown {
|
||||||
let mut text = String::new();
|
let mut text = String::new();
|
||||||
let mut highlights = Vec::new();
|
let mut highlights = Vec::new();
|
||||||
|
@ -34,7 +46,6 @@ pub async fn parse_markdown(
|
||||||
markdown,
|
markdown,
|
||||||
language_registry,
|
language_registry,
|
||||||
language,
|
language,
|
||||||
style,
|
|
||||||
&mut text,
|
&mut text,
|
||||||
&mut highlights,
|
&mut highlights,
|
||||||
&mut region_ranges,
|
&mut region_ranges,
|
||||||
|
@ -54,9 +65,8 @@ pub async fn parse_markdown_block(
|
||||||
markdown: &str,
|
markdown: &str,
|
||||||
language_registry: &Arc<LanguageRegistry>,
|
language_registry: &Arc<LanguageRegistry>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
style: &theme::Editor,
|
|
||||||
text: &mut String,
|
text: &mut String,
|
||||||
highlights: &mut Vec<(Range<usize>, HighlightStyle)>,
|
highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
|
||||||
region_ranges: &mut Vec<Range<usize>>,
|
region_ranges: &mut Vec<Range<usize>>,
|
||||||
regions: &mut Vec<ParsedRegion>,
|
regions: &mut Vec<ParsedRegion>,
|
||||||
) {
|
) {
|
||||||
|
@ -71,16 +81,16 @@ pub async fn parse_markdown_block(
|
||||||
match event {
|
match event {
|
||||||
Event::Text(t) => {
|
Event::Text(t) => {
|
||||||
if let Some(language) = ¤t_language {
|
if let Some(language) = ¤t_language {
|
||||||
highlight_code(text, highlights, t.as_ref(), language, style);
|
highlight_code(text, highlights, t.as_ref(), language);
|
||||||
} else {
|
} else {
|
||||||
text.push_str(t.as_ref());
|
text.push_str(t.as_ref());
|
||||||
|
|
||||||
let mut style = HighlightStyle::default();
|
let mut style = MarkdownHighlightStyle::default();
|
||||||
if bold_depth > 0 {
|
if bold_depth > 0 {
|
||||||
style.weight = Some(Weight::BOLD);
|
style.weight = Weight::BOLD;
|
||||||
}
|
}
|
||||||
if italic_depth > 0 {
|
if italic_depth > 0 {
|
||||||
style.italic = Some(true);
|
style.italic = true;
|
||||||
}
|
}
|
||||||
if let Some(link_url) = link_url.clone() {
|
if let Some(link_url) = link_url.clone() {
|
||||||
region_ranges.push(prev_len..text.len());
|
region_ranges.push(prev_len..text.len());
|
||||||
|
@ -88,22 +98,22 @@ pub async fn parse_markdown_block(
|
||||||
link_url: Some(link_url),
|
link_url: Some(link_url),
|
||||||
code: false,
|
code: false,
|
||||||
});
|
});
|
||||||
style.underline = Some(Underline {
|
style.underline = true;
|
||||||
thickness: 1.0.into(),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if style != HighlightStyle::default() {
|
if style != MarkdownHighlightStyle::default() {
|
||||||
let mut new_highlight = true;
|
let mut new_highlight = true;
|
||||||
if let Some((last_range, last_style)) = highlights.last_mut() {
|
if let Some((last_range, MarkdownHighlight::Style(last_style))) =
|
||||||
|
highlights.last_mut()
|
||||||
|
{
|
||||||
if last_range.end == prev_len && last_style == &style {
|
if last_range.end == prev_len && last_style == &style {
|
||||||
last_range.end = text.len();
|
last_range.end = text.len();
|
||||||
new_highlight = false;
|
new_highlight = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if new_highlight {
|
if new_highlight {
|
||||||
highlights.push((prev_len..text.len(), style));
|
let range = prev_len..text.len();
|
||||||
|
highlights.push((range, MarkdownHighlight::Style(style)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,13 +125,10 @@ pub async fn parse_markdown_block(
|
||||||
if link_url.is_some() {
|
if link_url.is_some() {
|
||||||
highlights.push((
|
highlights.push((
|
||||||
prev_len..text.len(),
|
prev_len..text.len(),
|
||||||
HighlightStyle {
|
MarkdownHighlight::Style(MarkdownHighlightStyle {
|
||||||
underline: Some(Underline {
|
underline: true,
|
||||||
thickness: 1.0.into(),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
regions.push(ParsedRegion {
|
regions.push(ParsedRegion {
|
||||||
|
@ -204,17 +211,15 @@ pub async fn parse_markdown_block(
|
||||||
|
|
||||||
pub fn highlight_code(
|
pub fn highlight_code(
|
||||||
text: &mut String,
|
text: &mut String,
|
||||||
highlights: &mut Vec<(Range<usize>, HighlightStyle)>,
|
highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
|
||||||
content: &str,
|
content: &str,
|
||||||
language: &Arc<Language>,
|
language: &Arc<Language>,
|
||||||
style: &theme::Editor,
|
|
||||||
) {
|
) {
|
||||||
let prev_len = text.len();
|
let prev_len = text.len();
|
||||||
text.push_str(content);
|
text.push_str(content);
|
||||||
for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
|
for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
|
||||||
if let Some(style) = highlight_id.style(&style.syntax) {
|
let highlight = MarkdownHighlight::Code(highlight_id);
|
||||||
highlights.push((prev_len + range.start..prev_len + range.end, style));
|
highlights.push((prev_len + range.start..prev_len + range.end, highlight));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue