use std::cmp; use crate::InlineCompletion;use gpui::{ point, prelude::*, quad, size, AnyElement, App, Bounds, Corners, Edges, HighlightStyle, Hsla, StyledText, TextLayout, TextStyle,};use language::OffsetRangeExt;use settings::Settings;use theme::ThemeSettings;use ui::prelude::*; pub struct CompletionDiffElement { element: AnyElement, text_layout: TextLayout, cursor_offset: usize,} impl CompletionDiffElement { pub fn new(completion: &InlineCompletion, cx: &App) -> Self { let mut diff = completion .snapshot .text_for_range(completion.excerpt_range.clone()) .collect::<String>(); let mut cursor_offset_in_diff = None; let mut delta = 0; let mut diff_highlights = Vec::new(); for (old_range, new_text) in completion.edits.iter() { let old_range = old_range.to_offset(&completion.snapshot); if cursor_offset_in_diff.is_none() && completion.cursor_offset <= old_range.end { cursor_offset_in_diff = Some(completion.cursor_offset - completion.excerpt_range.start + delta); } let old_start_in_diff = old_range.start - completion.excerpt_range.start + delta; let old_end_in_diff = old_range.end - completion.excerpt_range.start + delta; if old_start_in_diff < old_end_in_diff { diff_highlights.push(( old_start_in_diff..old_end_in_diff, HighlightStyle { background_color: Some(cx.theme().status().deleted_background), strikethrough: Some(gpui::StrikethroughStyle { thickness: px(1.), color: Some(cx.theme().colors().text_muted), }), ..Default::default() }, )); } if !new_text.is_empty() { diff.insert_str(old_end_in_diff, new_text); diff_highlights.push(( old_end_in_diff..old_end_in_diff + new_text.len(), HighlightStyle { background_color: Some(cx.theme().status().created_background), ..Default::default() }, )); delta += new_text.len(); } } let cursor_offset_in_diff = cursor_offset_in_diff .unwrap_or_else(|| completion.cursor_offset - completion.excerpt_range.start + delta); let settings = ThemeSettings::get_global(cx).clone(); let text_style = TextStyle { color: cx.theme().colors().editor_foreground, font_size: settings.buffer_font_size().into(), font_family: settings.buffer_font.family, font_features: settings.buffer_font.features, font_fallbacks: settings.buffer_font.fallbacks, line_height: relative(settings.buffer_line_height.value()), font_weight: settings.buffer_font.weight, font_style: settings.buffer_font.style, ..Default::default() }; let element = StyledText::new(diff).with_highlights(&text_style, diff_highlights); let text_layout = element.layout().clone(); CompletionDiffElement { element: element.into_any_element(), text_layout, cursor_offset: cursor_offset_in_diff, } }} impl IntoElement for CompletionDiffElement { type Element = Self; fn into_element(self) -> Self { self }} impl Element for CompletionDiffElement { type RequestLayoutState = (); type PrepaintState = (); fn id(&self) -> Option<ElementId> { None } fn request_layout( &mut self, _id: Option<&gpui::GlobalElementId>, window: &mut Window, cx: &mut App, ) -> (gpui::LayoutId, Self::RequestLayoutState) { (self.element.request_layout(window, cx), ()) } fn prepaint( &mut self, _id: Option<&gpui::GlobalElementId>, _bounds: gpui::Bounds<Pixels>, _request_layout: &mut Self::RequestLayoutState, window: &mut Window, cx: &mut App, ) -> Self::PrepaintState { self.element.prepaint(window, cx); } fn paint( &mut self, _id: Option<&gpui::GlobalElementId>, _bounds: gpui::Bounds<Pixels>, _request_layout: &mut Self::RequestLayoutState, _prepaint: &mut Self::PrepaintState, window: &mut Window, cx: &mut App, ) { if let Some(position) = self.text_layout.position_for_index(self.cursor_offset) { let bounds = self.text_layout.bounds(); let line_height = self.text_layout.line_height(); let line_width = self .text_layout .line_layout_for_index(self.cursor_offset) .map_or(bounds.size.width, |layout| layout.width()); window.paint_quad(quad( Bounds::new( point(bounds.origin.x, position.y), size(cmp::max(bounds.size.width, line_width), line_height), ), Corners::default(), cx.theme().colors().editor_active_line_background, Edges::default(), Hsla::transparent_black(), )); self.element.paint(window, cx); window.paint_quad(quad( Bounds::new(position, size(px(2.), line_height)), Corners::default(), cx.theme().players().local().cursor, Edges::default(), Hsla::transparent_black(), )); } }}
use std::cmp;
use crate::InlineCompletion;
use gpui::{
point, prelude::*, quad, size, AnyElement, App, Bounds, Corners, Edges, HighlightStyle, Hsla,
StyledText, TextLayout, TextStyle,
};
use language::OffsetRangeExt;
use settings::Settings;
use theme::ThemeSettings;
use ui::prelude::*;
pub struct CompletionDiffElement {
element: AnyElement,
text_layout: TextLayout,
cursor_offset: usize,
}
impl CompletionDiffElement {
pub fn new(completion: &InlineCompletion, cx: &App) -> Self {
let mut diff = completion
.snapshot
.text_for_range(completion.excerpt_range.clone())
.collect::<String>();
let mut cursor_offset_in_diff = None;
let mut delta = 0;
let mut diff_highlights = Vec::new();
for (old_range, new_text) in completion.edits.iter() {
let old_range = old_range.to_offset(&completion.snapshot);
if cursor_offset_in_diff.is_none() && completion.cursor_offset <= old_range.end {
cursor_offset_in_diff =
Some(completion.cursor_offset - completion.excerpt_range.start + delta);
}
let old_start_in_diff = old_range.start - completion.excerpt_range.start + delta;
let old_end_in_diff = old_range.end - completion.excerpt_range.start + delta;
if old_start_in_diff < old_end_in_diff {
diff_highlights.push((
old_start_in_diff..old_end_in_diff,
HighlightStyle {
background_color: Some(cx.theme().status().deleted_background),
strikethrough: Some(gpui::StrikethroughStyle {
thickness: px(1.),
color: Some(cx.theme().colors().text_muted),
}),
..Default::default()
},
));
}
if !new_text.is_empty() {
diff.insert_str(old_end_in_diff, new_text);
diff_highlights.push((
old_end_in_diff..old_end_in_diff + new_text.len(),
HighlightStyle {
background_color: Some(cx.theme().status().created_background),
..Default::default()
},
));
delta += new_text.len();
}
}
let cursor_offset_in_diff = cursor_offset_in_diff
.unwrap_or_else(|| completion.cursor_offset - completion.excerpt_range.start + delta);
let settings = ThemeSettings::get_global(cx).clone();
let text_style = TextStyle {
color: cx.theme().colors().editor_foreground,
font_size: settings.buffer_font_size().into(),
font_family: settings.buffer_font.family,
font_features: settings.buffer_font.features,
font_fallbacks: settings.buffer_font.fallbacks,
line_height: relative(settings.buffer_line_height.value()),
font_weight: settings.buffer_font.weight,
font_style: settings.buffer_font.style,
..Default::default()
};
let element = StyledText::new(diff).with_highlights(&text_style, diff_highlights);
let text_layout = element.layout().clone();
CompletionDiffElement {
element: element.into_any_element(),
text_layout,
cursor_offset: cursor_offset_in_diff,
}
}
}
impl IntoElement for CompletionDiffElement {
type Element = Self;
fn into_element(self) -> Self {
self
}
}
impl Element for CompletionDiffElement {
type RequestLayoutState = ();
type PrepaintState = ();
fn id(&self) -> Option<ElementId> {
None
}
fn request_layout(
&mut self,
_id: Option<&gpui::GlobalElementId>,
window: &mut Window,
cx: &mut App,
) -> (gpui::LayoutId, Self::RequestLayoutState) {
(self.element.request_layout(window, cx), ())
}
fn prepaint(
&mut self,
_id: Option<&gpui::GlobalElementId>,
_bounds: gpui::Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> Self::PrepaintState {
self.element.prepaint(window, cx);
}
fn paint(
&mut self,
_id: Option<&gpui::GlobalElementId>,
_bounds: gpui::Bounds<Pixels>,
_request_layout: &mut Self::RequestLayoutState,
_prepaint: &mut Self::PrepaintState,
window: &mut Window,
cx: &mut App,
) {
if let Some(position) = self.text_layout.position_for_index(self.cursor_offset) {
let bounds = self.text_layout.bounds();
let line_height = self.text_layout.line_height();
let line_width = self
.text_layout
.line_layout_for_index(self.cursor_offset)
.map_or(bounds.size.width, |layout| layout.width());
window.paint_quad(quad(
Bounds::new(
point(bounds.origin.x, position.y),
size(cmp::max(bounds.size.width, line_width), line_height),
),
Corners::default(),
cx.theme().colors().editor_active_line_background,
Edges::default(),
Hsla::transparent_black(),
));
self.element.paint(window, cx);
window.paint_quad(quad(
Bounds::new(position, size(px(2.), line_height)),
Corners::default(),
cx.theme().players().local().cursor,
Edges::default(),
Hsla::transparent_black(),
));
}
}
}