mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Allow file links in markdown & filter links a bit aggressively
This commit is contained in:
parent
a09ee3a41b
commit
4688a94a54
@ -122,6 +122,7 @@ pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
pub fn render_parsed_markdown<Tag: 'static>(
|
||||
parsed: &language::ParsedMarkdown,
|
||||
editor_style: &EditorStyle,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Text {
|
||||
enum RenderedMarkdown {}
|
||||
@ -147,15 +148,22 @@ pub fn render_parsed_markdown<Tag: 'static>(
|
||||
region_id += 1;
|
||||
let region = parsed.regions[ix].clone();
|
||||
|
||||
if let Some(url) = region.link_url {
|
||||
if let Some(link) = region.link {
|
||||
cx.scene().push_cursor_region(CursorRegion {
|
||||
bounds,
|
||||
style: CursorStyle::PointingHand,
|
||||
});
|
||||
cx.scene().push_mouse_region(
|
||||
MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds)
|
||||
.on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| {
|
||||
cx.platform().open_url(&url)
|
||||
.on_down::<Editor, _>(MouseButton::Left, move |_, _, cx| match &link {
|
||||
markdown::Link::Web { url } => cx.platform().open_url(url),
|
||||
markdown::Link::Path { path } => {
|
||||
if let Some(workspace) = &workspace {
|
||||
_ = workspace.update(cx, |workspace, cx| {
|
||||
workspace.open_abs_path(path.clone(), false, cx).detach();
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -916,10 +924,11 @@ impl ContextMenu {
|
||||
&self,
|
||||
cursor_position: DisplayPoint,
|
||||
style: EditorStyle,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> (DisplayPoint, AnyElement<Editor>) {
|
||||
match self {
|
||||
ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
|
||||
ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)),
|
||||
ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx),
|
||||
}
|
||||
}
|
||||
@ -1105,7 +1114,12 @@ impl CompletionsMenu {
|
||||
!self.matches.is_empty()
|
||||
}
|
||||
|
||||
fn render(&self, style: EditorStyle, cx: &mut ViewContext<Editor>) -> AnyElement<Editor> {
|
||||
fn render(
|
||||
&self,
|
||||
style: EditorStyle,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> AnyElement<Editor> {
|
||||
enum CompletionTag {}
|
||||
|
||||
let widest_completion_ix = self
|
||||
@ -1278,7 +1292,7 @@ impl CompletionsMenu {
|
||||
Flex::column()
|
||||
.scrollable::<MultiLineDocumentation>(0, None, cx)
|
||||
.with_child(render_parsed_markdown::<MultiLineDocumentation>(
|
||||
parsed, &style, cx,
|
||||
parsed, &style, workspace, cx,
|
||||
))
|
||||
.contained()
|
||||
.with_style(style.autocomplete.alongside_docs_container)
|
||||
@ -3140,6 +3154,7 @@ impl Editor {
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
|
||||
let offset = position.to_offset(buffer);
|
||||
let (word_range, kind) = buffer.surrounding_word(offset);
|
||||
@ -4215,9 +4230,14 @@ impl Editor {
|
||||
style: EditorStyle,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Option<(DisplayPoint, AnyElement<Editor>)> {
|
||||
self.context_menu
|
||||
.as_ref()
|
||||
.map(|menu| menu.render(cursor_position, style, cx))
|
||||
self.context_menu.as_ref().map(|menu| {
|
||||
menu.render(
|
||||
cursor_position,
|
||||
style,
|
||||
self.workspace.as_ref().map(|(w, _)| w.clone()),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
|
||||
|
@ -2439,9 +2439,13 @@ impl Element<Editor> for EditorElement {
|
||||
}
|
||||
|
||||
let visible_rows = start_row..start_row + line_layouts.len() as u32;
|
||||
let mut hover = editor
|
||||
.hover_state
|
||||
.render(&snapshot, &style, visible_rows, cx);
|
||||
let mut hover = editor.hover_state.render(
|
||||
&snapshot,
|
||||
&style,
|
||||
visible_rows,
|
||||
editor.workspace.as_ref().map(|(w, _)| w.clone()),
|
||||
cx,
|
||||
);
|
||||
let mode = editor.mode;
|
||||
|
||||
let mut fold_indicators = editor.render_fold_indicators(
|
||||
|
@ -9,7 +9,7 @@ use gpui::{
|
||||
actions,
|
||||
elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AnyElement, AppContext, Element, ModelHandle, Task, ViewContext,
|
||||
AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, WeakViewHandle,
|
||||
};
|
||||
use language::{
|
||||
markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
|
||||
@ -17,6 +17,7 @@ use language::{
|
||||
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
|
||||
use std::{ops::Range, sync::Arc, time::Duration};
|
||||
use util::TryFutureExt;
|
||||
use workspace::Workspace;
|
||||
|
||||
pub const HOVER_DELAY_MILLIS: u64 = 350;
|
||||
pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200;
|
||||
@ -422,6 +423,7 @@ impl HoverState {
|
||||
snapshot: &EditorSnapshot,
|
||||
style: &EditorStyle,
|
||||
visible_rows: Range<u32>,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Option<(DisplayPoint, Vec<AnyElement<Editor>>)> {
|
||||
// If there is a diagnostic, position the popovers based on that.
|
||||
@ -451,7 +453,7 @@ impl HoverState {
|
||||
elements.push(diagnostic_popover.render(style, cx));
|
||||
}
|
||||
if let Some(info_popover) = self.info_popover.as_mut() {
|
||||
elements.push(info_popover.render(style, cx));
|
||||
elements.push(info_popover.render(style, workspace, cx));
|
||||
}
|
||||
|
||||
Some((point, elements))
|
||||
@ -470,6 +472,7 @@ impl InfoPopover {
|
||||
pub fn render(
|
||||
&mut self,
|
||||
style: &EditorStyle,
|
||||
workspace: Option<WeakViewHandle<Workspace>>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> AnyElement<Editor> {
|
||||
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
|
||||
@ -478,6 +481,7 @@ impl InfoPopover {
|
||||
.with_child(crate::render_parsed_markdown::<HoverBlock>(
|
||||
&self.parsed_content,
|
||||
style,
|
||||
workspace,
|
||||
cx,
|
||||
))
|
||||
.contained()
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use std::{ops::Range, path::PathBuf};
|
||||
|
||||
use crate::{HighlightId, Language, LanguageRegistry};
|
||||
use gpui::fonts::{self, HighlightStyle, Weight};
|
||||
@ -58,7 +58,28 @@ pub struct MarkdownHighlightStyle {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParsedRegion {
|
||||
pub code: bool,
|
||||
pub link_url: Option<String>,
|
||||
pub link: Option<Link>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Link {
|
||||
Web { url: String },
|
||||
Path { path: PathBuf },
|
||||
}
|
||||
|
||||
impl Link {
|
||||
fn identify(text: String) -> Option<Link> {
|
||||
if text.starts_with("http") {
|
||||
return Some(Link::Web { url: text });
|
||||
}
|
||||
|
||||
let path = PathBuf::from(text);
|
||||
if path.is_absolute() {
|
||||
return Some(Link::Path { path });
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn parse_markdown(
|
||||
@ -115,17 +136,20 @@ pub async fn parse_markdown_block(
|
||||
text.push_str(t.as_ref());
|
||||
|
||||
let mut style = MarkdownHighlightStyle::default();
|
||||
|
||||
if bold_depth > 0 {
|
||||
style.weight = Weight::BOLD;
|
||||
}
|
||||
|
||||
if italic_depth > 0 {
|
||||
style.italic = true;
|
||||
}
|
||||
if let Some(link_url) = link_url.clone() {
|
||||
|
||||
if let Some(link) = link_url.clone().and_then(|u| Link::identify(u)) {
|
||||
region_ranges.push(prev_len..text.len());
|
||||
regions.push(ParsedRegion {
|
||||
link_url: Some(link_url),
|
||||
code: false,
|
||||
link: Some(link),
|
||||
});
|
||||
style.underline = true;
|
||||
}
|
||||
@ -151,7 +175,9 @@ pub async fn parse_markdown_block(
|
||||
Event::Code(t) => {
|
||||
text.push_str(t.as_ref());
|
||||
region_ranges.push(prev_len..text.len());
|
||||
if link_url.is_some() {
|
||||
|
||||
let link = link_url.clone().and_then(|u| Link::identify(u));
|
||||
if link.is_some() {
|
||||
highlights.push((
|
||||
prev_len..text.len(),
|
||||
MarkdownHighlight::Style(MarkdownHighlightStyle {
|
||||
@ -160,10 +186,7 @@ pub async fn parse_markdown_block(
|
||||
}),
|
||||
));
|
||||
}
|
||||
regions.push(ParsedRegion {
|
||||
code: true,
|
||||
link_url: link_url.clone(),
|
||||
});
|
||||
regions.push(ParsedRegion { code: true, link });
|
||||
}
|
||||
|
||||
Event::Start(tag) => match tag {
|
||||
|
@ -150,11 +150,14 @@ impl TerminalView {
|
||||
cx.notify();
|
||||
cx.emit(Event::Wakeup);
|
||||
}
|
||||
|
||||
Event::Bell => {
|
||||
this.has_bell = true;
|
||||
cx.emit(Event::Wakeup);
|
||||
}
|
||||
|
||||
Event::BlinkChanged => this.blinking_on = !this.blinking_on,
|
||||
|
||||
Event::TitleChanged => {
|
||||
if let Some(foreground_info) = &this.terminal().read(cx).foreground_process_info {
|
||||
let cwd = foreground_info.cwd.clone();
|
||||
@ -171,6 +174,7 @@ impl TerminalView {
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
Event::NewNavigationTarget(maybe_navigation_target) => {
|
||||
this.can_navigate_to_selected_word = match maybe_navigation_target {
|
||||
Some(MaybeNavigationTarget::Url(_)) => true,
|
||||
@ -180,8 +184,10 @@ impl TerminalView {
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
Event::Open(maybe_navigation_target) => match maybe_navigation_target {
|
||||
MaybeNavigationTarget::Url(url) => cx.platform().open_url(url),
|
||||
|
||||
MaybeNavigationTarget::PathLike(maybe_path) => {
|
||||
if !this.can_navigate_to_selected_word {
|
||||
return;
|
||||
@ -246,6 +252,7 @@ impl TerminalView {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => cx.emit(event.clone()),
|
||||
})
|
||||
.detach();
|
||||
|
Loading…
Reference in New Issue
Block a user