Remove language::markdown (#25136)

The language::markdown crate had been superceded by markdown::Mardown.

After #25117, the only two remaining use-cases were rendering git commit
messages (which are arguably not really markdown) and the signature help
(which is definitely not markdown).

Updated the former to use the new markdown component, and the latter to
do syntax highlighting manually.

Release Notes:

- Allow selecting the commit message in git commits
This commit is contained in:
Conrad Irwin
2025-02-19 08:55:36 -07:00
committed by GitHub
parent 686978d7c5
commit 119bd896b0
25 changed files with 348 additions and 947 deletions
+1 -2
View File
@@ -603,7 +603,6 @@ impl CompletionsMenu {
hover_markdown_style(window, cx),
languages,
language,
window,
cx,
)
.copy_code_block_buttons(false)
@@ -611,7 +610,7 @@ impl CompletionsMenu {
})
});
markdown.update(cx, |markdown, cx| {
markdown.reset(parsed.clone(), window, cx);
markdown.reset(parsed.clone(), cx);
});
div().child(markdown.clone())
}
+38 -32
View File
@@ -2,10 +2,10 @@ use futures::Future;
use git::blame::BlameEntry;
use git::PullRequest;
use gpui::{
App, Asset, ClipboardItem, Element, ParentElement, Render, ScrollHandle,
StatefulInteractiveElement, WeakEntity,
App, Asset, ClipboardItem, Element, Entity, MouseButton, ParentElement, Render, ScrollHandle,
StatefulInteractiveElement,
};
use language::ParsedMarkdown;
use markdown::Markdown;
use settings::Settings;
use std::hash::Hash;
use theme::ThemeSettings;
@@ -13,10 +13,9 @@ use time::{OffsetDateTime, UtcOffset};
use time_format::format_local_timestamp;
use ui::{prelude::*, tooltip_container, Avatar, Divider, IconButtonShape};
use url::Url;
use workspace::Workspace;
use crate::git::blame::GitRemote;
use crate::EditorStyle;
use crate::hover_popover::hover_markdown_style;
#[derive(Clone, Debug)]
pub struct CommitDetails {
@@ -30,7 +29,6 @@ pub struct CommitDetails {
#[derive(Clone, Debug, Default)]
pub struct ParsedCommitMessage {
pub message: SharedString,
pub parsed_message: ParsedMarkdown,
pub permalink: Option<Url>,
pub pull_request: Option<PullRequest>,
pub remote: Option<GitRemote>,
@@ -115,48 +113,65 @@ impl Asset for CommitAvatarAsset {
pub struct CommitTooltip {
commit: CommitDetails,
editor_style: EditorStyle,
workspace: Option<WeakEntity<Workspace>>,
scroll_handle: ScrollHandle,
markdown: Entity<Markdown>,
}
impl CommitTooltip {
pub fn blame_entry(
blame: BlameEntry,
blame: &BlameEntry,
details: Option<ParsedCommitMessage>,
style: EditorStyle,
workspace: Option<WeakEntity<Workspace>>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let commit_time = blame
.committer_time
.and_then(|t| OffsetDateTime::from_unix_timestamp(t).ok())
.unwrap_or(OffsetDateTime::now_utc());
Self::new(
CommitDetails {
sha: blame.sha.to_string().into(),
commit_time,
committer_name: blame
.committer_name
.clone()
.unwrap_or("<no name>".to_string())
.into(),
committer_email: blame.committer_email.unwrap_or("".to_string()).into(),
committer_email: blame
.committer_email
.clone()
.unwrap_or("".to_string())
.into(),
message: details,
},
style,
workspace,
window,
cx,
)
}
pub fn new(
commit: CommitDetails,
editor_style: EditorStyle,
workspace: Option<WeakEntity<Workspace>>,
) -> Self {
pub fn new(commit: CommitDetails, window: &mut Window, cx: &mut Context<Self>) -> Self {
let mut style = hover_markdown_style(window, cx);
if let Some(code_block) = &style.code_block.text {
style.base_text_style.refine(code_block);
}
let markdown = cx.new(|cx| {
Markdown::new(
commit
.message
.as_ref()
.map(|message| message.message.clone())
.unwrap_or_default(),
style,
None,
None,
cx,
)
});
Self {
editor_style,
commit,
workspace,
scroll_handle: ScrollHandle::new(),
markdown,
}
}
}
@@ -186,16 +201,7 @@ impl Render for CommitTooltip {
.commit
.message
.as_ref()
.map(|details| {
crate::render_parsed_markdown(
"blame-message",
&details.parsed_message,
&self.editor_style,
self.workspace.clone(),
cx,
)
.into_any()
})
.map(|_| self.markdown.clone().into_any_element())
.unwrap_or("<no commit message>".into_any());
let pull_request = self
@@ -210,6 +216,7 @@ impl Render for CommitTooltip {
tooltip_container(window, cx, move |this, _, cx| {
this.occlude()
.on_mouse_move(|_, _, cx| cx.stop_propagation())
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
.child(
v_flex()
.w(gpui::rems(30.))
@@ -235,7 +242,6 @@ impl Render for CommitTooltip {
.child(
div()
.id("inline-blame-commit-message")
.occlude()
.child(message)
.max_h(message_max_height)
.overflow_y_scroll()
+13 -72
View File
@@ -81,11 +81,11 @@ use git::blame::GitBlame;
use gpui::{
div, impl_actions, point, prelude::*, pulsating_between, px, relative, size, Action, Animation,
AnimationExt, AnyElement, App, AsyncWindowContext, AvailableSpace, Background, Bounds,
ClipboardEntry, ClipboardItem, Context, DispatchPhase, ElementId, Entity, EntityInputHandler,
ClipboardEntry, ClipboardItem, Context, DispatchPhase, Entity, EntityInputHandler,
EventEmitter, FocusHandle, FocusOutEvent, Focusable, FontId, FontWeight, Global,
HighlightStyle, Hsla, InteractiveText, KeyContext, Modifiers, MouseButton, MouseDownEvent,
PaintQuad, ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription,
Task, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
HighlightStyle, Hsla, KeyContext, Modifiers, MouseButton, MouseDownEvent, PaintQuad,
ParentElement, Pixels, Render, SharedString, Size, Styled, StyledText, Subscription, Task,
TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle, UniformListScrollHandle,
WeakEntity, WeakFocusHandle, Window,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
@@ -98,7 +98,7 @@ pub use items::MAX_TAB_TITLE_LEN;
use itertools::Itertools;
use language::{
language_settings::{self, all_language_settings, language_settings, InlayHintSettings},
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
CursorShape, Diagnostic, DiskState, EditPredictionsMode, EditPreview, HighlightedText,
IndentKind, IndentSize, Language, OffsetRangeExt, Point, Selection, SelectionGoal, TextObject,
TransactionId, TreeSitterOptions,
@@ -214,72 +214,6 @@ const COLUMNAR_SELECTION_MODIFIERS: Modifiers = Modifiers {
function: false,
};
pub fn render_parsed_markdown(
element_id: impl Into<ElementId>,
parsed: &language::ParsedMarkdown,
editor_style: &EditorStyle,
workspace: Option<WeakEntity<Workspace>>,
cx: &mut App,
) -> InteractiveText {
let code_span_background_color = cx
.theme()
.colors()
.editor_document_highlight_read_background;
let highlights = gpui::combine_highlights(
parsed.highlights.iter().filter_map(|(range, highlight)| {
let highlight = highlight.to_highlight_style(&editor_style.syntax)?;
Some((range.clone(), highlight))
}),
parsed
.regions
.iter()
.zip(&parsed.region_ranges)
.filter_map(|(region, range)| {
if region.code {
Some((
range.clone(),
HighlightStyle {
background_color: Some(code_span_background_color),
..Default::default()
},
))
} else {
None
}
}),
);
let mut links = Vec::new();
let mut link_ranges = Vec::new();
for (range, region) in parsed.region_ranges.iter().zip(&parsed.regions) {
if let Some(link) = region.link.clone() {
links.push(link);
link_ranges.push(range.clone());
}
}
InteractiveText::new(
element_id,
StyledText::new(parsed.text.clone()).with_highlights(&editor_style.text, highlights),
)
.on_click(
link_ranges,
move |clicked_range_ix, window, cx| match &links[clicked_range_ix] {
markdown::Link::Web { url } => cx.open_url(url),
markdown::Link::Path { path } => {
if let Some(workspace) = &workspace {
_ = workspace.update(cx, |workspace, cx| {
workspace
.open_abs_path(path.clone(), false, window, cx)
.detach();
});
}
}
},
)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum InlayId {
InlineCompletion(usize),
@@ -745,6 +679,7 @@ pub struct Editor {
show_git_blame_gutter: bool,
show_git_blame_inline: bool,
show_git_blame_inline_delay_task: Option<Task<()>>,
git_blame_inline_tooltip: Option<WeakEntity<crate::commit_tooltip::CommitTooltip>>,
distinguish_unstaged_diff_hunks: bool,
git_blame_inline_enabled: bool,
serialize_dirty_buffers: bool,
@@ -1454,6 +1389,7 @@ impl Editor {
distinguish_unstaged_diff_hunks: false,
show_selection_menu: None,
show_git_blame_inline_delay_task: None,
git_blame_inline_tooltip: None,
git_blame_inline_enabled: ProjectSettings::get_global(cx).git.inline_blame_enabled(),
serialize_dirty_buffers: ProjectSettings::get_global(cx)
.session
@@ -13434,7 +13370,12 @@ impl Editor {
pub fn render_git_blame_inline(&self, window: &Window, cx: &App) -> bool {
self.show_git_blame_inline
&& self.focus_handle.is_focused(window)
&& (self.focus_handle.is_focused(window)
|| self
.git_blame_inline_tooltip
.as_ref()
.and_then(|t| t.upgrade())
.is_some())
&& !self.newest_selection_head_on_empty_line(cx)
&& self.has_blame_entries(cx)
}
+18 -26
View File
@@ -21,17 +21,14 @@ use language::{
BracketPairConfig,
Capability::ReadWrite,
FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageMatcher, LanguageName,
Override, ParsedMarkdown, Point,
Override, Point,
};
use language_settings::{Formatter, FormatterList, IndentGuideSettings};
use multi_buffer::IndentGuide;
use parking_lot::Mutex;
use pretty_assertions::{assert_eq, assert_ne};
use project::project_settings::{LspSettings, ProjectSettings};
use project::FakeFs;
use project::{
lsp_command::SIGNATURE_HELP_HIGHLIGHT_CURRENT,
project_settings::{LspSettings, ProjectSettings},
};
use serde_json::{self, json};
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
use std::{
@@ -8118,12 +8115,10 @@ async fn test_handle_input_for_show_signature_help_auto_signature_help_true(
cx.editor(|editor, _, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
assert_eq!(
signature_help_state.unwrap().label,
"param1: u8, param2: u8"
);
});
}
@@ -8291,11 +8286,10 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui:
cx.update_editor(|editor, _, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
assert_eq!(
signature_help_state.unwrap().label,
"param1: u8, param2: u8"
);
editor.signature_help_state = SignatureHelpState::default();
});
@@ -8333,11 +8327,10 @@ async fn test_handle_input_with_different_show_signature_settings(cx: &mut gpui:
cx.editor(|editor, _, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
assert_eq!(
signature_help_state.unwrap().label,
"param1: u8, param2: u8"
);
});
}
@@ -8395,11 +8388,10 @@ async fn test_signature_help(cx: &mut gpui::TestAppContext) {
cx.editor(|editor, _, _| {
let signature_help_state = editor.signature_help_state.popover().cloned();
assert!(signature_help_state.is_some());
let ParsedMarkdown {
text, highlights, ..
} = signature_help_state.unwrap().parsed_content;
assert_eq!(text, "param1: u8, param2: u8");
assert_eq!(highlights, vec![(0..10, SIGNATURE_HELP_HIGHLIGHT_CURRENT)]);
assert_eq!(
signature_help_state.unwrap().label,
"param1: u8, param2: u8"
);
});
// When exiting outside from inside the brackets, `signature_help` is closed.
+24 -41
View File
@@ -38,8 +38,7 @@ use gpui::{
GlobalElementId, Hitbox, Hsla, InteractiveElement, IntoElement, Keystroke, Length,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
ParentElement, Pixels, ScrollDelta, ScrollWheelEvent, ShapedLine, SharedString, Size,
StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement,
WeakEntity, Window,
StatefulInteractiveElement, Style, Styled, Subscription, TextRun, TextStyleRefinement, Window,
};
use itertools::Itertools;
use language::{
@@ -76,7 +75,7 @@ use ui::{
};
use unicode_segmentation::UnicodeSegmentation;
use util::{RangeExt, ResultExt};
use workspace::{item::Item, notifications::NotifyTaskExt, Workspace};
use workspace::{item::Item, notifications::NotifyTaskExt};
const INLINE_BLAME_PADDING_EM_WIDTHS: f32 = 7.;
const MIN_SCROLL_THUMB_SIZE: f32 = 25.;
@@ -1630,13 +1629,6 @@ impl EditorElement {
return None;
}
let workspace = self
.editor
.read(cx)
.workspace
.as_ref()
.map(|(w, _)| w.clone());
let editor = self.editor.read(cx);
let blame = editor.blame.clone()?;
let padding = {
@@ -1665,7 +1657,7 @@ impl EditorElement {
.flatten()?;
let mut element =
render_inline_blame_entry(&blame, blame_entry, &self.style, workspace, cx);
render_inline_blame_entry(self.editor.clone(), &blame, blame_entry, &self.style, cx);
let start_y = content_origin.y
+ line_height * (display_row.as_f32() - scroll_pixel_position.y / line_height);
@@ -4251,12 +4243,7 @@ impl EditorElement {
let maybe_element = self.editor.update(cx, |editor, cx| {
if let Some(popover) = editor.signature_help_state.popover_mut() {
let element = popover.render(
&self.style,
max_size,
editor.workspace.as_ref().map(|(w, _)| w.clone()),
cx,
);
let element = popover.render(max_size, cx);
Some(element)
} else {
None
@@ -5903,10 +5890,10 @@ fn prepaint_gutter_button(
}
fn render_inline_blame_entry(
editor: Entity<Editor>,
blame: &gpui::Entity<GitBlame>,
blame_entry: BlameEntry,
style: &EditorStyle,
workspace: Option<WeakEntity<Workspace>>,
cx: &mut App,
) -> AnyElement {
let relative_timestamp = blame_entry_relative_timestamp(&blame_entry);
@@ -5922,11 +5909,8 @@ fn render_inline_blame_entry(
}
_ => format!("{}, {}", author, relative_timestamp),
};
let details = blame.read(cx).details_for_entry(&blame_entry);
let tooltip =
cx.new(|_| CommitTooltip::blame_entry(blame_entry, details, style.clone(), workspace));
let blame = blame.clone();
let blame_entry = blame_entry.clone();
h_flex()
.id("inline-blame")
@@ -5937,7 +5921,15 @@ fn render_inline_blame_entry(
.child(Icon::new(IconName::FileGit).color(Color::Hint))
.child(text)
.gap_2()
.hoverable_tooltip(move |_, _| tooltip.clone().into())
.hoverable_tooltip(move |window, cx| {
let details = blame.read(cx).details_for_entry(&blame_entry);
let tooltip =
cx.new(|cx| CommitTooltip::blame_entry(&blame_entry, details, window, cx));
editor.update(cx, |editor, _| {
editor.git_blame_inline_tooltip = Some(tooltip.downgrade())
});
tooltip.into()
})
.into_any()
}
@@ -5971,20 +5963,8 @@ fn render_blame_entry(
let author_name = blame_entry.author.as_deref().unwrap_or("<no name>");
let name = util::truncate_and_trailoff(author_name, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED);
let details = blame.read(cx).details_for_entry(&blame_entry);
let workspace = editor.read(cx).workspace.as_ref().map(|(w, _)| w.clone());
let tooltip = cx.new(|_| {
CommitTooltip::blame_entry(
blame_entry.clone(),
details.clone(),
style.clone(),
workspace,
)
});
h_flex()
.w_full()
.justify_between()
@@ -6018,16 +5998,20 @@ fn render_blame_entry(
})
.hover(|style| style.bg(cx.theme().colors().element_hover))
.when_some(
details.and_then(|details| details.permalink),
details
.as_ref()
.and_then(|details| details.permalink.clone()),
|this, url| {
let url = url.clone();
this.cursor_pointer().on_click(move |_, _, cx| {
cx.stop_propagation();
cx.open_url(url.as_str())
})
},
)
.hoverable_tooltip(move |_, _| tooltip.clone().into())
.hoverable_tooltip(move |window, cx| {
cx.new(|cx| CommitTooltip::blame_entry(&blame_entry, details.clone(), window, cx))
.into()
})
.into_any()
}
@@ -7068,12 +7052,11 @@ impl Element for EditorElement {
blame.blame_for_rows(&[row_infos], cx).next()
})
.flatten()?;
let workspace = editor.workspace.as_ref().map(|(w, _)| w.to_owned());
let mut element = render_inline_blame_entry(
self.editor.clone(),
blame,
blame_entry,
&style,
workspace,
cx,
);
let inline_blame_padding = INLINE_BLAME_PADDING_EM_WIDTHS * em_advance;
+4 -24
View File
@@ -6,7 +6,7 @@ use git::{
};
use gpui::{App, AppContext as _, Context, Entity, Subscription, Task};
use http_client::HttpClient;
use language::{markdown, Bias, Buffer, BufferSnapshot, Edit, LanguageRegistry, ParsedMarkdown};
use language::{Bias, Buffer, BufferSnapshot, Edit};
use multi_buffer::RowInfo;
use project::{Project, ProjectItem};
use smallvec::SmallVec;
@@ -229,6 +229,9 @@ impl GitBlame {
}
pub fn focus(&mut self, cx: &mut Context<Self>) {
if self.focused {
return;
}
self.focused = true;
if self.changed_while_blurred {
self.changed_while_blurred = false;
@@ -355,7 +358,6 @@ impl GitBlame {
let buffer_edits = self.buffer.update(cx, |buffer, _| buffer.subscribe());
let snapshot = self.buffer.read(cx).snapshot();
let blame = self.project.read(cx).blame_buffer(&self.buffer, None, cx);
let languages = self.project.read(cx).languages().clone();
let provider_registry = GitHostingProviderRegistry::default_global(cx);
self.task = cx.spawn(|this, mut cx| async move {
@@ -379,7 +381,6 @@ impl GitBlame {
remote_url,
&permalinks,
provider_registry,
&languages,
)
.await;
@@ -475,7 +476,6 @@ async fn parse_commit_messages(
remote_url: Option<String>,
deprecated_permalinks: &HashMap<Oid, Url>,
provider_registry: Arc<GitHostingProviderRegistry>,
languages: &Arc<LanguageRegistry>,
) -> HashMap<Oid, ParsedCommitMessage> {
let mut commit_details = HashMap::default();
@@ -484,8 +484,6 @@ async fn parse_commit_messages(
.and_then(|remote_url| parse_git_remote_url(provider_registry, remote_url));
for (oid, message) in messages {
let parsed_message = parse_markdown(&message, languages).await;
let permalink = if let Some((provider, git_remote)) = parsed_remote_url.as_ref() {
Some(provider.build_commit_permalink(
git_remote,
@@ -517,7 +515,6 @@ async fn parse_commit_messages(
oid,
ParsedCommitMessage {
message: message.into(),
parsed_message,
permalink,
remote,
pull_request,
@@ -528,23 +525,6 @@ async fn parse_commit_messages(
commit_details
}
async fn parse_markdown(text: &str, language_registry: &Arc<LanguageRegistry>) -> ParsedMarkdown {
let mut parsed_message = ParsedMarkdown::default();
markdown::parse_markdown_block(
text,
Some(language_registry),
None,
&mut parsed_message.text,
&mut parsed_message.highlights,
&mut parsed_message.region_ranges,
&mut parsed_message.regions,
)
.await;
parsed_message
}
#[cfg(test)]
mod tests {
use super::*;
+2 -10
View File
@@ -358,15 +358,8 @@ fn show_hover(
},
..Default::default()
};
Markdown::new_text(
SharedString::new(text),
markdown_style.clone(),
None,
None,
window,
cx,
)
.open_url(open_markdown_url)
Markdown::new_text(SharedString::new(text), markdown_style.clone(), cx)
.open_url(open_markdown_url)
})
.ok();
@@ -573,7 +566,6 @@ async fn parse_blocks(
hover_markdown_style(window, cx),
Some(language_registry.clone()),
fallback_language_name,
window,
cx,
)
.copy_code_block_buttons(false)
+145 -61
View File
@@ -1,17 +1,19 @@
mod popover;
mod state;
use crate::actions::ShowSignatureHelp;
use crate::{Editor, EditorSettings, ToggleAutoSignatureHelp};
use gpui::{App, Context, Window};
use language::markdown::parse_markdown;
use gpui::{
combine_highlights, App, Context, HighlightStyle, MouseButton, Size, StyledText, Task,
TextStyle, Window,
};
use language::BufferSnapshot;
use multi_buffer::{Anchor, ToOffset};
use settings::Settings;
use std::ops::Range;
pub use popover::SignatureHelpPopover;
pub use state::SignatureHelpState;
use text::Rope;
use theme::ThemeSettings;
use ui::{
div, relative, ActiveTheme, AnyElement, InteractiveElement, IntoElement, ParentElement, Pixels,
SharedString, StatefulInteractiveElement, Styled, StyledExt,
};
// Language-specific settings may define quotes as "brackets", so filter them out separately.
const QUOTE_PAIRS: [(&str, &str); 3] = [("'", "'"), ("\"", "\""), ("`", "`")];
@@ -168,67 +170,149 @@ impl Editor {
else {
return;
};
let Some(lsp_store) = self.project.as_ref().map(|p| p.read(cx).lsp_store()) else {
return;
};
let task = lsp_store.update(cx, |lsp_store, cx| {
lsp_store.signature_help(&buffer, buffer_position, cx)
});
let language = self.language_at(position, cx);
self.signature_help_state
.set_task(cx.spawn_in(window, move |editor, mut cx| async move {
let signature_help = editor
.update(&mut cx, |editor, cx| {
let language = editor.language_at(position, cx);
let project = editor.project.clone()?;
let (markdown, language_registry) = {
project.update(cx, |project, cx| {
let language_registry = project.languages().clone();
(
project.signature_help(&buffer, buffer_position, cx),
language_registry,
)
})
};
Some((markdown, language_registry, language))
})
.ok()
.flatten();
let signature_help_popover = if let Some((
signature_help_task,
language_registry,
language,
)) = signature_help
{
// TODO allow multiple signature helps inside the same popover
if let Some(mut signature_help) = signature_help_task.await.into_iter().next() {
let mut parsed_content = parse_markdown(
signature_help.markdown.as_str(),
Some(&language_registry),
language,
)
.await;
parsed_content
.highlights
.append(&mut signature_help.highlights);
Some(SignatureHelpPopover { parsed_content })
} else {
None
}
} else {
None
};
let signature_help = task.await;
editor
.update(&mut cx, |editor, cx| {
let previous_popover = editor.signature_help_state.popover();
if previous_popover != signature_help_popover.as_ref() {
if let Some(signature_help_popover) = signature_help_popover {
editor
.signature_help_state
.set_popover(signature_help_popover);
} else {
editor
.signature_help_state
.hide(SignatureHelpHiddenBy::AutoClose);
}
cx.notify();
let Some(mut signature_help) = signature_help.into_iter().next() else {
editor
.signature_help_state
.hide(SignatureHelpHiddenBy::AutoClose);
return;
};
if let Some(language) = language {
let text = Rope::from(signature_help.label.clone());
let highlights = language
.highlight_text(&text, 0..signature_help.label.len())
.into_iter()
.flat_map(|(range, highlight_id)| {
Some((range, highlight_id.style(&cx.theme().syntax())?))
});
signature_help.highlights =
combine_highlights(signature_help.highlights, highlights).collect()
}
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.buffer_font.family.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_size: settings.buffer_font_size.into(),
font_weight: settings.buffer_font.weight,
line_height: relative(settings.buffer_line_height.value()),
..Default::default()
};
let signature_help_popover = SignatureHelpPopover {
label: signature_help.label.into(),
highlights: signature_help.highlights,
style: text_style,
};
editor
.signature_help_state
.set_popover(signature_help_popover);
cx.notify();
})
.ok();
}));
}
}
#[derive(Default, Debug)]
pub struct SignatureHelpState {
task: Option<Task<()>>,
popover: Option<SignatureHelpPopover>,
hidden_by: Option<SignatureHelpHiddenBy>,
backspace_pressed: bool,
}
impl SignatureHelpState {
pub fn set_task(&mut self, task: Task<()>) {
self.task = Some(task);
self.hidden_by = None;
}
pub fn kill_task(&mut self) {
self.task = None;
}
#[cfg(test)]
pub fn popover(&self) -> Option<&SignatureHelpPopover> {
self.popover.as_ref()
}
pub fn popover_mut(&mut self) -> Option<&mut SignatureHelpPopover> {
self.popover.as_mut()
}
pub fn backspace_pressed(&self) -> bool {
self.backspace_pressed
}
pub fn set_backspace_pressed(&mut self, backspace_pressed: bool) {
self.backspace_pressed = backspace_pressed;
}
pub fn set_popover(&mut self, popover: SignatureHelpPopover) {
self.popover = Some(popover);
self.hidden_by = None;
}
pub fn hide(&mut self, hidden_by: SignatureHelpHiddenBy) {
if self.hidden_by.is_none() {
self.popover = None;
self.hidden_by = Some(hidden_by);
}
}
pub fn hidden_by_selection(&self) -> bool {
self.hidden_by == Some(SignatureHelpHiddenBy::Selection)
}
pub fn is_shown(&self) -> bool {
self.popover.is_some()
}
}
#[cfg(test)]
impl SignatureHelpState {
pub fn task(&self) -> Option<&Task<()>> {
self.task.as_ref()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SignatureHelpPopover {
pub label: SharedString,
pub style: TextStyle,
pub highlights: Vec<(Range<usize>, HighlightStyle)>,
}
impl SignatureHelpPopover {
pub fn render(&mut self, max_size: Size<Pixels>, cx: &mut Context<Editor>) -> AnyElement {
div()
.id("signature_help_popover")
.elevation_2(cx)
.overflow_y_scroll()
.max_w(max_size.width)
.max_h(max_size.height)
.on_mouse_move(|_, _, cx| cx.stop_propagation())
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
.child(
div().px_4().pb_1().child(
StyledText::new(self.label.clone())
.with_highlights(&self.style, self.highlights.iter().cloned()),
),
)
.into_any_element()
}
}
@@ -1,48 +0,0 @@
use crate::{Editor, EditorStyle};
use gpui::{
div, AnyElement, Context, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
Size, StatefulInteractiveElement, Styled, WeakEntity,
};
use language::ParsedMarkdown;
use ui::StyledExt;
use workspace::Workspace;
#[derive(Clone, Debug)]
pub struct SignatureHelpPopover {
pub parsed_content: ParsedMarkdown,
}
impl PartialEq for SignatureHelpPopover {
fn eq(&self, other: &Self) -> bool {
let str_equality = self.parsed_content.text.as_str() == other.parsed_content.text.as_str();
let highlight_equality = self.parsed_content.highlights == other.parsed_content.highlights;
str_equality && highlight_equality
}
}
impl SignatureHelpPopover {
pub fn render(
&mut self,
style: &EditorStyle,
max_size: Size<Pixels>,
workspace: Option<WeakEntity<Workspace>>,
cx: &mut Context<Editor>,
) -> AnyElement {
div()
.id("signature_help_popover")
.elevation_2(cx)
.overflow_y_scroll()
.max_w(max_size.width)
.max_h(max_size.height)
.on_mouse_move(|_, _, cx| cx.stop_propagation())
.on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
.child(div().p_2().child(crate::render_parsed_markdown(
"signature_help_popover_content",
&self.parsed_content,
style,
workspace,
cx,
)))
.into_any_element()
}
}
-65
View File
@@ -1,65 +0,0 @@
use crate::signature_help::popover::SignatureHelpPopover;
use crate::signature_help::SignatureHelpHiddenBy;
use gpui::Task;
#[derive(Default, Debug)]
pub struct SignatureHelpState {
task: Option<Task<()>>,
popover: Option<SignatureHelpPopover>,
hidden_by: Option<SignatureHelpHiddenBy>,
backspace_pressed: bool,
}
impl SignatureHelpState {
pub fn set_task(&mut self, task: Task<()>) {
self.task = Some(task);
self.hidden_by = None;
}
pub fn kill_task(&mut self) {
self.task = None;
}
pub fn popover(&self) -> Option<&SignatureHelpPopover> {
self.popover.as_ref()
}
pub fn popover_mut(&mut self) -> Option<&mut SignatureHelpPopover> {
self.popover.as_mut()
}
pub fn backspace_pressed(&self) -> bool {
self.backspace_pressed
}
pub fn set_backspace_pressed(&mut self, backspace_pressed: bool) {
self.backspace_pressed = backspace_pressed;
}
pub fn set_popover(&mut self, popover: SignatureHelpPopover) {
self.popover = Some(popover);
self.hidden_by = None;
}
pub fn hide(&mut self, hidden_by: SignatureHelpHiddenBy) {
if self.hidden_by.is_none() {
self.popover = None;
self.hidden_by = Some(hidden_by);
}
}
pub fn hidden_by_selection(&self) -> bool {
self.hidden_by == Some(SignatureHelpHiddenBy::Selection)
}
pub fn is_shown(&self) -> bool {
self.popover.is_some()
}
}
#[cfg(test)]
impl SignatureHelpState {
pub fn task(&self) -> Option<&Task<()>> {
self.task.as_ref()
}
}