Into the woods

This commit is contained in:
Conrad Irwin 2023-11-02 18:52:08 -06:00
parent 7b712ac68f
commit 0aabb19a45
16 changed files with 2263 additions and 2258 deletions

View file

@ -35,7 +35,7 @@ git = { path = "../git" }
gpui = { package = "gpui2", path = "../gpui2" } gpui = { package = "gpui2", path = "../gpui2" }
language = { package = "language2", path = "../language2" } language = { package = "language2", path = "../language2" }
lsp = { package = "lsp2", path = "../lsp2" } lsp = { package = "lsp2", path = "../lsp2" }
multi_buffer = { path = "../multi_buffer" } multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2" }
project = { package = "project2", path = "../project2" } project = { package = "project2", path = "../project2" }
rpc = { package = "rpc2", path = "../rpc2" } rpc = { package = "rpc2", path = "../rpc2" }
rich_text = { path = "../rich_text" } rich_text = { path = "../rich_text" }
@ -80,7 +80,7 @@ util = { path = "../util", features = ["test-support"] }
project = { package = "project2", path = "../project2", features = ["test-support"] } project = { package = "project2", path = "../project2", features = ["test-support"] }
settings = { package = "settings2", path = "../settings2", features = ["test-support"] } settings = { package = "settings2", path = "../settings2", features = ["test-support"] }
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] } workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
multi_buffer = { path = "../multi_buffer", features = ["test-support"] } multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2", features = ["test-support"] }
ctor.workspace = true ctor.workspace = true
env_logger.workspace = true env_logger.workspace = true

View file

@ -1,5 +1,6 @@
use crate::EditorSettings; use crate::EditorSettings;
use gpui::{Entity, ModelContext}; use gpui::ModelContext;
use settings::Settings;
use settings::SettingsStore; use settings::SettingsStore;
use smol::Timer; use smol::Timer;
use std::time::Duration; use std::time::Duration;
@ -16,7 +17,7 @@ pub struct BlinkManager {
impl BlinkManager { impl BlinkManager {
pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self { pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
// Make sure we blink the cursors if the setting is re-enabled // Make sure we blink the cursors if the setting is re-enabled
cx.observe_global::<SettingsStore, _>(move |this, cx| { cx.observe_global::<SettingsStore>(move |this, cx| {
this.blink_cursors(this.blink_epoch, cx) this.blink_cursors(this.blink_epoch, cx)
}) })
.detach(); .detach();
@ -41,14 +42,9 @@ impl BlinkManager {
let epoch = self.next_blink_epoch(); let epoch = self.next_blink_epoch();
let interval = self.blink_interval; let interval = self.blink_interval;
cx.spawn(|this, mut cx| { cx.spawn(|this, mut cx| async move {
let this = this.downgrade(); Timer::after(interval).await;
async move { this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
Timer::after(interval).await;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
}
}
}) })
.detach(); .detach();
} }
@ -68,13 +64,10 @@ impl BlinkManager {
let epoch = self.next_blink_epoch(); let epoch = self.next_blink_epoch();
let interval = self.blink_interval; let interval = self.blink_interval;
cx.spawn(|this, mut cx| { cx.spawn(|this, mut cx| async move {
let this = this.downgrade(); Timer::after(interval).await;
async move { if let Some(this) = this.upgrade() {
Timer::after(interval).await; this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
}
} }
}) })
.detach(); .detach();

View file

@ -497,62 +497,63 @@ impl DisplaySnapshot {
) )
} }
pub fn highlighted_chunks<'a>( // pub fn highlighted_chunks<'a>(
&'a self, // &'a self,
display_rows: Range<u32>, // display_rows: Range<u32>,
language_aware: bool, // language_aware: bool,
style: &'a EditorStyle, // style: &'a EditorStyle,
) -> impl Iterator<Item = HighlightedChunk<'a>> { // ) -> impl Iterator<Item = HighlightedChunk<'a>> {
self.chunks( // self.chunks(
display_rows, // display_rows,
language_aware, // language_aware,
Some(style.theme.hint), // Some(style.theme.hint),
Some(style.theme.suggestion), // Some(style.theme.suggestion),
) // )
.map(|chunk| { // .map(|chunk| {
let mut highlight_style = chunk // let mut highlight_style = chunk
.syntax_highlight_id // .syntax_highlight_id
.and_then(|id| id.style(&style.syntax)); // .and_then(|id| id.style(&style.syntax));
if let Some(chunk_highlight) = chunk.highlight_style { // if let Some(chunk_highlight) = chunk.highlight_style {
if let Some(highlight_style) = highlight_style.as_mut() { // if let Some(highlight_style) = highlight_style.as_mut() {
highlight_style.highlight(chunk_highlight); // highlight_style.highlight(chunk_highlight);
} else { // } else {
highlight_style = Some(chunk_highlight); // highlight_style = Some(chunk_highlight);
} // }
} // }
let mut diagnostic_highlight = HighlightStyle::default(); // let mut diagnostic_highlight = HighlightStyle::default();
if chunk.is_unnecessary { // if chunk.is_unnecessary {
diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade); // diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
} // }
if let Some(severity) = chunk.diagnostic_severity { // if let Some(severity) = chunk.diagnostic_severity {
// Omit underlines for HINT/INFO diagnostics on 'unnecessary' code. // // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary { // if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
let diagnostic_style = super::diagnostic_style(severity, true, style); // todo!()
diagnostic_highlight.underline = Some(UnderlineStyle { // // let diagnostic_style = super::diagnostic_style(severity, true, style);
color: Some(diagnostic_style.message.text.color), // // diagnostic_highlight.underline = Some(UnderlineStyle {
thickness: 1.0.into(), // // color: Some(diagnostic_style.message.text.color),
wavy: true, // // thickness: 1.0.into(),
}); // // wavy: true,
} // // });
} // }
// }
if let Some(highlight_style) = highlight_style.as_mut() { // if let Some(highlight_style) = highlight_style.as_mut() {
highlight_style.highlight(diagnostic_highlight); // highlight_style.highlight(diagnostic_highlight);
} else { // } else {
highlight_style = Some(diagnostic_highlight); // highlight_style = Some(diagnostic_highlight);
} // }
HighlightedChunk { // HighlightedChunk {
chunk: chunk.text, // chunk: chunk.text,
style: highlight_style, // style: highlight_style,
is_tab: chunk.is_tab, // is_tab: chunk.is_tab,
} // }
}) // })
} // }
pub fn lay_out_line_for_row( pub fn lay_out_line_for_row(
&self, &self,
@ -606,7 +607,7 @@ impl DisplaySnapshot {
// }); // });
} }
text_layout_cache.layout_text(&line, editor_style.text.font_size, &styles) text_layout_cache.layout_text(&line, editor_style.text.font_size, &styles, None)
} }
pub fn x_for_point( pub fn x_for_point(

View file

@ -932,15 +932,15 @@ impl BlockDisposition {
} }
} }
impl<'a, 'b, 'c> Deref for BlockContext<'a, 'b, 'c> { impl<'a> Deref for BlockContext<'a, '_> {
type Target = ViewContext<'a, 'b, Editor>; type Target = ViewContext<'a, Editor>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.view_context self.view_context
} }
} }
impl DerefMut for BlockContext<'_, '_, '_> { impl DerefMut for BlockContext<'_, '_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.view_context self.view_context
} }

View file

@ -4,7 +4,7 @@ use super::{
Highlights, Highlights,
}; };
use crate::MultiBufferSnapshot; use crate::MultiBufferSnapshot;
use gpui::{AppContext, FontId, Model, ModelContext, Pixels, Task}; use gpui::{AppContext, FontId, LineWrapper, Model, ModelContext, Pixels, Task};
use language::{Chunk, Point}; use language::{Chunk, Point};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use smol::future::yield_now; use smol::future::yield_now;
@ -20,7 +20,7 @@ pub struct WrapMap {
pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>, pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
interpolated_edits: Patch<u32>, interpolated_edits: Patch<u32>,
edits_since_sync: Patch<u32>, edits_since_sync: Patch<u32>,
wrap_width: Option<f32>, wrap_width: Option<Pixels>,
background_task: Option<Task<()>>, background_task: Option<Task<()>>,
font: (FontId, Pixels), font: (FontId, Pixels),
} }
@ -130,7 +130,11 @@ impl WrapMap {
} }
} }
pub fn set_wrap_width(&mut self, wrap_width: Option<f32>, cx: &mut ModelContext<Self>) -> bool { pub fn set_wrap_width(
&mut self,
wrap_width: Option<Pixels>,
cx: &mut ModelContext<Self>,
) -> bool {
if wrap_width == self.wrap_width { if wrap_width == self.wrap_width {
return false; return false;
} }
@ -379,7 +383,7 @@ impl WrapSnapshot {
&mut self, &mut self,
new_tab_snapshot: TabSnapshot, new_tab_snapshot: TabSnapshot,
tab_edits: &[TabEdit], tab_edits: &[TabEdit],
wrap_width: f32, wrap_width: Pixels,
line_wrapper: &mut LineWrapper, line_wrapper: &mut LineWrapper,
) -> Patch<u32> { ) -> Patch<u32> {
#[derive(Debug)] #[derive(Debug)]

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
use gpui::Settings;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct EditorSettings { pub struct EditorSettings {
@ -55,7 +55,7 @@ impl Settings for EditorSettings {
fn load( fn load(
default_value: &Self::FileContent, default_value: &Self::FileContent,
user_values: &[&Self::FileContent], user_values: &[&Self::FileContent],
_: &gpui::AppContext, _: &mut gpui::AppContext,
) -> anyhow::Result<Self> { ) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values) Self::load_via_json_merge(default_value, user_values)
} }

View file

@ -3,9 +3,11 @@ use super::{
}; };
use crate::{ use crate::{
display_map::{BlockStyle, DisplaySnapshot}, display_map::{BlockStyle, DisplaySnapshot},
mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt, EditorStyle,
};
use gpui::{
px, relative, AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, TextRun, TextSystem,
}; };
use gpui::{AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, TextRun, TextSystem};
use language::{CursorShape, Selection}; use language::{CursorShape, Selection};
use std::{ops::Range, sync::Arc}; use std::{ops::Range, sync::Arc};
use sum_tree::Bias; use sum_tree::Bias;
@ -1997,7 +1999,10 @@ impl Element<Editor> for EditorElement {
element_state: &mut Self::ElementState, element_state: &mut Self::ElementState,
cx: &mut gpui::ViewContext<Editor>, cx: &mut gpui::ViewContext<Editor>,
) -> gpui::LayoutId { ) -> gpui::LayoutId {
cx.request_layout(Style::default().size_full(), None) let mut style = Style::default();
style.size.width = relative(1.).into();
style.size.height = relative(1.).into();
cx.request_layout(&style, None)
} }
fn paint( fn paint(
@ -2011,13 +2016,8 @@ impl Element<Editor> for EditorElement {
let layout_text = cx.text_system().layout_text( let layout_text = cx.text_system().layout_text(
"hello world", "hello world",
text_style.font_size, text_style.font_size * cx.rem_size(),
&[TextRun { &[text_style.to_run("hello world".len())],
len: "hello world".len(),
font: text_style.font,
color: text_style.color,
underline: text_style.underline,
}],
None, None,
); );
} }
@ -2697,19 +2697,19 @@ impl PositionMap {
position: gpui::Point<Pixels>, position: gpui::Point<Pixels>,
) -> PointForPosition { ) -> PointForPosition {
let scroll_position = self.snapshot.scroll_position(); let scroll_position = self.snapshot.scroll_position();
let position = position - text_bounds.origin(); let position = position - text_bounds.origin;
let y = position.y().max(0.0).min(self.size.y()); let y = position.y.max(px(0.)).min(self.size.width);
let x = position.x() + (scroll_position.x() * self.em_width); let x = position.x + (scroll_position.x * self.em_width);
let row = (y / self.line_height + scroll_position.y()) as u32; let row = (y / self.line_height + scroll_position.y).into();
let (column, x_overshoot_after_line_end) = if let Some(line) = self let (column, x_overshoot_after_line_end) = if let Some(line) = self
.line_layouts .line_layouts
.get(row as usize - scroll_position.y() as usize) .get(row as usize - scroll_position.y.into())
.map(|line_with_spaces| &line_with_spaces.line) .map(|line_with_spaces| &line_with_spaces.line)
{ {
if let Some(ix) = line.index_for_x(x) { if let Some(ix) = line.index_for_x(x) {
(ix as u32, 0.0) (ix as u32, 0.0)
} else { } else {
(line.len() as u32, 0f32.max(x - line.width())) (line.len() as u32, px(0.).max(x - line.width()))
} }
} else { } else {
(0, x) (0, x)

View file

@ -466,35 +466,35 @@ pub struct InfoPopover {
parsed_content: ParsedMarkdown, parsed_content: ParsedMarkdown,
} }
impl InfoPopover { // impl InfoPopover {
pub fn render( // pub fn render(
&mut self, // &mut self,
style: &EditorStyle, // style: &EditorStyle,
workspace: Option<WeakView<Workspace>>, // workspace: Option<WeakView<Workspace>>,
cx: &mut ViewContext<Editor>, // cx: &mut ViewContext<Editor>,
) -> AnyElement<Editor> { // ) -> AnyElement<Editor> {
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| { // MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
Flex::column() // Flex::column()
.scrollable::<HoverBlock>(0, None, cx) // .scrollable::<HoverBlock>(0, None, cx)
.with_child(crate::render_parsed_markdown::<HoverBlock>( // .with_child(crate::render_parsed_markdown::<HoverBlock>(
&self.parsed_content, // &self.parsed_content,
style, // style,
workspace, // workspace,
cx, // cx,
)) // ))
.contained() // .contained()
.with_style(style.hover_popover.container) // .with_style(style.hover_popover.container)
}) // })
.on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath. // .on_move(|_, _, _| {}) // Consume move events so they don't reach regions underneath.
.with_cursor_style(CursorStyle::Arrow) // .with_cursor_style(CursorStyle::Arrow)
.with_padding(Padding { // .with_padding(Padding {
bottom: HOVER_POPOVER_GAP, // bottom: HOVER_POPOVER_GAP,
top: HOVER_POPOVER_GAP, // top: HOVER_POPOVER_GAP,
..Default::default() // ..Default::default()
}) // })
.into_any() // .into_any()
} // }
} // }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DiagnosticPopover { pub struct DiagnosticPopover {

View file

@ -521,7 +521,7 @@ impl InlayHintCache {
buffer_id: u64, buffer_id: u64,
excerpt_id: ExcerptId, excerpt_id: ExcerptId,
id: InlayId, id: InlayId,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, Editor>,
) { ) {
if let Some(excerpt_hints) = self.hints.get(&excerpt_id) { if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
let mut guard = excerpt_hints.write(); let mut guard = excerpt_hints.write();
@ -582,7 +582,7 @@ fn spawn_new_update_tasks(
excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>, excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
invalidate: InvalidationStrategy, invalidate: InvalidationStrategy,
update_cache_version: usize, update_cache_version: usize,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, Editor>,
) { ) {
let visible_hints = Arc::new(editor.visible_inlay_hints(cx)); let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
@ -760,7 +760,7 @@ fn new_update_task(
visible_hints: Arc<Vec<Inlay>>, visible_hints: Arc<Vec<Inlay>>,
cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>, cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
lsp_request_limiter: Arc<Semaphore>, lsp_request_limiter: Arc<Semaphore>,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, Editor>,
) -> Task<()> { ) -> Task<()> {
cx.spawn(|editor, mut cx| async move { cx.spawn(|editor, mut cx| async move {
let closure_cx = cx.clone(); let closure_cx = cx.clone();
@ -836,134 +836,134 @@ fn new_update_task(
}) })
} }
async fn fetch_and_update_hints( // async fn fetch_and_update_hints(
editor: gpui::WeakView<Editor>, // editor: gpui::WeakView<Editor>,
multi_buffer_snapshot: MultiBufferSnapshot, // multi_buffer_snapshot: MultiBufferSnapshot,
buffer_snapshot: BufferSnapshot, // buffer_snapshot: BufferSnapshot,
visible_hints: Arc<Vec<Inlay>>, // visible_hints: Arc<Vec<Inlay>>,
cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>, // cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
query: ExcerptQuery, // query: ExcerptQuery,
invalidate: bool, // invalidate: bool,
fetch_range: Range<language::Anchor>, // fetch_range: Range<language::Anchor>,
lsp_request_limiter: Arc<Semaphore>, // lsp_request_limiter: Arc<Semaphore>,
mut cx: gpui::AsyncAppContext, // mut cx: gpui::AsyncAppContext,
) -> anyhow::Result<()> { // ) -> anyhow::Result<()> {
let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() { // let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
(None, false) // (None, false)
} else { // } else {
match lsp_request_limiter.try_acquire() { // match lsp_request_limiter.try_acquire() {
Some(guard) => (Some(guard), false), // Some(guard) => (Some(guard), false),
None => (Some(lsp_request_limiter.acquire().await), true), // None => (Some(lsp_request_limiter.acquire().await), true),
} // }
}; // };
let fetch_range_to_log = // let fetch_range_to_log =
fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot); // fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot);
let inlay_hints_fetch_task = editor // let inlay_hints_fetch_task = editor
.update(&mut cx, |editor, cx| { // .update(&mut cx, |editor, cx| {
if got_throttled { // if got_throttled {
let query_not_around_visible_range = match editor.excerpt_visible_offsets(None, cx).remove(&query.excerpt_id) { // let query_not_around_visible_range = match editor.excerpt_visible_offsets(None, cx).remove(&query.excerpt_id) {
Some((_, _, current_visible_range)) => { // Some((_, _, current_visible_range)) => {
let visible_offset_length = current_visible_range.len(); // let visible_offset_length = current_visible_range.len();
let double_visible_range = current_visible_range // let double_visible_range = current_visible_range
.start // .start
.saturating_sub(visible_offset_length) // .saturating_sub(visible_offset_length)
..current_visible_range // ..current_visible_range
.end // .end
.saturating_add(visible_offset_length) // .saturating_add(visible_offset_length)
.min(buffer_snapshot.len()); // .min(buffer_snapshot.len());
!double_visible_range // !double_visible_range
.contains(&fetch_range.start.to_offset(&buffer_snapshot)) // .contains(&fetch_range.start.to_offset(&buffer_snapshot))
&& !double_visible_range // && !double_visible_range
.contains(&fetch_range.end.to_offset(&buffer_snapshot)) // .contains(&fetch_range.end.to_offset(&buffer_snapshot))
}, // },
None => true, // None => true,
}; // };
if query_not_around_visible_range { // if query_not_around_visible_range {
log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping."); // log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
if let Some(task_ranges) = editor // if let Some(task_ranges) = editor
.inlay_hint_cache // .inlay_hint_cache
.update_tasks // .update_tasks
.get_mut(&query.excerpt_id) // .get_mut(&query.excerpt_id)
{ // {
task_ranges.invalidate_range(&buffer_snapshot, &fetch_range); // task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
} // }
return None; // return None;
} // }
} // }
editor // editor
.buffer() // .buffer()
.read(cx) // .read(cx)
.buffer(query.buffer_id) // .buffer(query.buffer_id)
.and_then(|buffer| { // .and_then(|buffer| {
let project = editor.project.as_ref()?; // let project = editor.project.as_ref()?;
Some(project.update(cx, |project, cx| { // Some(project.update(cx, |project, cx| {
project.inlay_hints(buffer, fetch_range.clone(), cx) // project.inlay_hints(buffer, fetch_range.clone(), cx)
})) // }))
}) // })
}) // })
.ok() // .ok()
.flatten(); // .flatten();
let new_hints = match inlay_hints_fetch_task { // let new_hints = match inlay_hints_fetch_task {
Some(fetch_task) => { // Some(fetch_task) => {
log::debug!( // log::debug!(
"Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}", // "Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}",
query_reason = query.reason, // query_reason = query.reason,
); // );
log::trace!( // log::trace!(
"Currently visible hints: {visible_hints:?}, cached hints present: {}", // "Currently visible hints: {visible_hints:?}, cached hints present: {}",
cached_excerpt_hints.is_some(), // cached_excerpt_hints.is_some(),
); // );
fetch_task.await.context("inlay hint fetch task")? // fetch_task.await.context("inlay hint fetch task")?
} // }
None => return Ok(()), // None => return Ok(()),
}; // };
drop(lsp_request_guard); // drop(lsp_request_guard);
log::debug!( // log::debug!(
"Fetched {} hints for range {fetch_range_to_log:?}", // "Fetched {} hints for range {fetch_range_to_log:?}",
new_hints.len() // new_hints.len()
); // );
log::trace!("Fetched hints: {new_hints:?}"); // log::trace!("Fetched hints: {new_hints:?}");
let background_task_buffer_snapshot = buffer_snapshot.clone(); // let background_task_buffer_snapshot = buffer_snapshot.clone();
let backround_fetch_range = fetch_range.clone(); // let backround_fetch_range = fetch_range.clone();
let new_update = cx // let new_update = cx
.background() // .background()
.spawn(async move { // .spawn(async move {
calculate_hint_updates( // calculate_hint_updates(
query.excerpt_id, // query.excerpt_id,
invalidate, // invalidate,
backround_fetch_range, // backround_fetch_range,
new_hints, // new_hints,
&background_task_buffer_snapshot, // &background_task_buffer_snapshot,
cached_excerpt_hints, // cached_excerpt_hints,
&visible_hints, // &visible_hints,
) // )
}) // })
.await; // .await;
if let Some(new_update) = new_update { // if let Some(new_update) = new_update {
log::debug!( // log::debug!(
"Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}", // "Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}",
new_update.remove_from_visible.len(), // new_update.remove_from_visible.len(),
new_update.remove_from_cache.len(), // new_update.remove_from_cache.len(),
new_update.add_to_cache.len() // new_update.add_to_cache.len()
); // );
log::trace!("New update: {new_update:?}"); // log::trace!("New update: {new_update:?}");
editor // editor
.update(&mut cx, |editor, cx| { // .update(&mut cx, |editor, cx| {
apply_hint_update( // apply_hint_update(
editor, // editor,
new_update, // new_update,
query, // query,
invalidate, // invalidate,
buffer_snapshot, // buffer_snapshot,
multi_buffer_snapshot, // multi_buffer_snapshot,
cx, // cx,
); // );
}) // })
.ok(); // .ok();
} // }
Ok(()) // Ok(())
} // }
fn calculate_hint_updates( fn calculate_hint_updates(
excerpt_id: ExcerptId, excerpt_id: ExcerptId,
@ -1071,7 +1071,7 @@ fn apply_hint_update(
invalidate: bool, invalidate: bool,
buffer_snapshot: BufferSnapshot, buffer_snapshot: BufferSnapshot,
multi_buffer_snapshot: MultiBufferSnapshot, multi_buffer_snapshot: MultiBufferSnapshot,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, Editor>,
) { ) {
let cached_excerpt_hints = editor let cached_excerpt_hints = editor
.inlay_hint_cache .inlay_hint_cache

View file

@ -7,8 +7,8 @@ use anyhow::{Context, Result};
use collections::HashSet; use collections::HashSet;
use futures::future::try_join_all; use futures::future::try_join_all;
use gpui::{ use gpui::{
point, AnyElement, AppContext, AsyncAppContext, Entity, Model, Pixels, Subscription, Task, point, AnyElement, AppContext, AsyncAppContext, Entity, Model, Pixels, SharedString,
View, ViewContext, WeakView, Subscription, Task, View, ViewContext, WeakView,
}; };
use language::{ use language::{
proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point, proto::serialize_anchor as serialize_text_anchor, Bias, Buffer, OffsetRangeExt, Point,
@ -304,133 +304,133 @@ impl FollowableItem for Editor {
} }
} }
async fn update_editor_from_message( // async fn update_editor_from_message(
this: WeakView<Editor>, // this: WeakView<Editor>,
project: Model<Project>, // project: Model<Project>,
message: proto::update_view::Editor, // message: proto::update_view::Editor,
cx: &mut AsyncAppContext, // cx: &mut AsyncAppContext,
) -> Result<()> { // ) -> Result<()> {
// Open all of the buffers of which excerpts were added to the editor. // // Open all of the buffers of which excerpts were added to the editor.
let inserted_excerpt_buffer_ids = message // let inserted_excerpt_buffer_ids = message
.inserted_excerpts // .inserted_excerpts
.iter() // .iter()
.filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id)) // .filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id))
.collect::<HashSet<_>>(); // .collect::<HashSet<_>>();
let inserted_excerpt_buffers = project.update(cx, |project, cx| { // let inserted_excerpt_buffers = project.update(cx, |project, cx| {
inserted_excerpt_buffer_ids // inserted_excerpt_buffer_ids
.into_iter() // .into_iter()
.map(|id| project.open_buffer_by_id(id, cx)) // .map(|id| project.open_buffer_by_id(id, cx))
.collect::<Vec<_>>() // .collect::<Vec<_>>()
}); // })?;
let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?; // let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;
// Update the editor's excerpts. // // Update the editor's excerpts.
this.update(cx, |editor, cx| { // this.update(cx, |editor, cx| {
editor.buffer.update(cx, |multibuffer, cx| { // editor.buffer.update(cx, |multibuffer, cx| {
let mut removed_excerpt_ids = message // let mut removed_excerpt_ids = message
.deleted_excerpts // .deleted_excerpts
.into_iter() // .into_iter()
.map(ExcerptId::from_proto) // .map(ExcerptId::from_proto)
.collect::<Vec<_>>(); // .collect::<Vec<_>>();
removed_excerpt_ids.sort_by({ // removed_excerpt_ids.sort_by({
let multibuffer = multibuffer.read(cx); // let multibuffer = multibuffer.read(cx);
move |a, b| a.cmp(&b, &multibuffer) // move |a, b| a.cmp(&b, &multibuffer)
}); // });
let mut insertions = message.inserted_excerpts.into_iter().peekable(); // let mut insertions = message.inserted_excerpts.into_iter().peekable();
while let Some(insertion) = insertions.next() { // while let Some(insertion) = insertions.next() {
let Some(excerpt) = insertion.excerpt else { // let Some(excerpt) = insertion.excerpt else {
continue; // continue;
}; // };
let Some(previous_excerpt_id) = insertion.previous_excerpt_id else { // let Some(previous_excerpt_id) = insertion.previous_excerpt_id else {
continue; // continue;
}; // };
let buffer_id = excerpt.buffer_id; // let buffer_id = excerpt.buffer_id;
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else { // let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
continue; // continue;
}; // };
let adjacent_excerpts = iter::from_fn(|| { // let adjacent_excerpts = iter::from_fn(|| {
let insertion = insertions.peek()?; // let insertion = insertions.peek()?;
if insertion.previous_excerpt_id.is_none() // if insertion.previous_excerpt_id.is_none()
&& insertion.excerpt.as_ref()?.buffer_id == buffer_id // && insertion.excerpt.as_ref()?.buffer_id == buffer_id
{ // {
insertions.next()?.excerpt // insertions.next()?.excerpt
} else { // } else {
None // None
} // }
}); // });
multibuffer.insert_excerpts_with_ids_after( // multibuffer.insert_excerpts_with_ids_after(
ExcerptId::from_proto(previous_excerpt_id), // ExcerptId::from_proto(previous_excerpt_id),
buffer, // buffer,
[excerpt] // [excerpt]
.into_iter() // .into_iter()
.chain(adjacent_excerpts) // .chain(adjacent_excerpts)
.filter_map(|excerpt| { // .filter_map(|excerpt| {
Some(( // Some((
ExcerptId::from_proto(excerpt.id), // ExcerptId::from_proto(excerpt.id),
deserialize_excerpt_range(excerpt)?, // deserialize_excerpt_range(excerpt)?,
)) // ))
}), // }),
cx, // cx,
); // );
} // }
multibuffer.remove_excerpts(removed_excerpt_ids, cx); // multibuffer.remove_excerpts(removed_excerpt_ids, cx);
}); // });
})?; // })?;
// Deserialize the editor state. // // Deserialize the editor state.
let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| { // let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
let buffer = editor.buffer.read(cx).read(cx); // let buffer = editor.buffer.read(cx).read(cx);
let selections = message // let selections = message
.selections // .selections
.into_iter() // .into_iter()
.filter_map(|selection| deserialize_selection(&buffer, selection)) // .filter_map(|selection| deserialize_selection(&buffer, selection))
.collect::<Vec<_>>(); // .collect::<Vec<_>>();
let pending_selection = message // let pending_selection = message
.pending_selection // .pending_selection
.and_then(|selection| deserialize_selection(&buffer, selection)); // .and_then(|selection| deserialize_selection(&buffer, selection));
let scroll_top_anchor = message // let scroll_top_anchor = message
.scroll_top_anchor // .scroll_top_anchor
.and_then(|anchor| deserialize_anchor(&buffer, anchor)); // .and_then(|anchor| deserialize_anchor(&buffer, anchor));
anyhow::Ok((selections, pending_selection, scroll_top_anchor)) // anyhow::Ok((selections, pending_selection, scroll_top_anchor))
})??; // })??;
// Wait until the buffer has received all of the operations referenced by // // Wait until the buffer has received all of the operations referenced by
// the editor's new state. // // the editor's new state.
this.update(cx, |editor, cx| { // this.update(cx, |editor, cx| {
editor.buffer.update(cx, |buffer, cx| { // editor.buffer.update(cx, |buffer, cx| {
buffer.wait_for_anchors( // buffer.wait_for_anchors(
selections // selections
.iter() // .iter()
.chain(pending_selection.as_ref()) // .chain(pending_selection.as_ref())
.flat_map(|selection| [selection.start, selection.end]) // .flat_map(|selection| [selection.start, selection.end])
.chain(scroll_top_anchor), // .chain(scroll_top_anchor),
cx, // cx,
) // )
}) // })
})? // })?
.await?; // .await?;
// Update the editor's state. // // Update the editor's state.
this.update(cx, |editor, cx| { // this.update(cx, |editor, cx| {
if !selections.is_empty() || pending_selection.is_some() { // if !selections.is_empty() || pending_selection.is_some() {
editor.set_selections_from_remote(selections, pending_selection, cx); // editor.set_selections_from_remote(selections, pending_selection, cx);
editor.request_autoscroll_remotely(Autoscroll::newest(), cx); // editor.request_autoscroll_remotely(Autoscroll::newest(), cx);
} else if let Some(scroll_top_anchor) = scroll_top_anchor { // } else if let Some(scroll_top_anchor) = scroll_top_anchor {
editor.set_scroll_anchor_remote( // editor.set_scroll_anchor_remote(
ScrollAnchor { // ScrollAnchor {
anchor: scroll_top_anchor, // anchor: scroll_top_anchor,
offset: point(message.scroll_x, message.scroll_y), // offset: point(message.scroll_x, message.scroll_y),
}, // },
cx, // cx,
); // );
} // }
})?; // })?;
Ok(()) // Ok(())
} // }
fn serialize_excerpt( fn serialize_excerpt(
buffer_id: u64, buffer_id: u64,
@ -544,7 +544,7 @@ impl Item for Editor {
} }
} }
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<Cow<str>> { fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
let file_path = self let file_path = self
.buffer() .buffer()
.read(cx) .read(cx)
@ -559,7 +559,7 @@ impl Item for Editor {
Some(file_path.into()) Some(file_path.into())
} }
fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<str>> { fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<SharedString> {
match path_for_buffer(&self.buffer, detail, true, cx)? { match path_for_buffer(&self.buffer, detail, true, cx)? {
Cow::Borrowed(path) => Some(path.to_string_lossy()), Cow::Borrowed(path) => Some(path.to_string_lossy()),
Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()), Cow::Owned(path) => Some(path.to_string_lossy().to_string().into()),

View file

@ -168,7 +168,7 @@ pub fn update_inlay_link_and_hover_points(
editor: &mut Editor, editor: &mut Editor,
cmd_held: bool, cmd_held: bool,
shift_held: bool, shift_held: bool,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, Editor>,
) { ) {
let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 { let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left)) Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))

View file

@ -9,7 +9,7 @@ use crate::{
Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot, Anchor, DisplayPoint, Editor, EditorMode, Event, InlayHintRefreshReason, MultiBufferSnapshot,
ToPoint, ToPoint,
}; };
use gpui::{point, AppContext, Pixels, Task, ViewContext}; use gpui::{point, px, AppContext, Pixels, Styled, Task, ViewContext};
use language::{Bias, Point}; use language::{Bias, Point};
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
@ -44,7 +44,7 @@ impl ScrollAnchor {
} }
} }
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Point<Pixels> { pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<Pixels> {
let mut scroll_position = self.offset; let mut scroll_position = self.offset;
if self.anchor != Anchor::min() { if self.anchor != Anchor::min() {
let scroll_top = self.anchor.to_display_point(snapshot).row() as f32; let scroll_top = self.anchor.to_display_point(snapshot).row() as f32;
@ -80,13 +80,13 @@ impl OngoingScroll {
} }
} }
pub fn filter(&self, delta: &mut Point<Pixels>) -> Option<Axis> { pub fn filter(&self, delta: &mut gpui::Point<Pixels>) -> Option<Axis> {
const UNLOCK_PERCENT: f32 = 1.9; const UNLOCK_PERCENT: f32 = 1.9;
const UNLOCK_LOWER_BOUND: f32 = 6.; const UNLOCK_LOWER_BOUND: f32 = 6.;
let mut axis = self.axis; let mut axis = self.axis;
let x = delta.x().abs(); let x = delta.x.abs();
let y = delta.y().abs(); let y = delta.y.abs();
let duration = Instant::now().duration_since(self.last_event); let duration = Instant::now().duration_since(self.last_event);
if duration > SCROLL_EVENT_SEPARATION { if duration > SCROLL_EVENT_SEPARATION {
//New ongoing scroll will start, determine axis //New ongoing scroll will start, determine axis
@ -115,8 +115,12 @@ impl OngoingScroll {
} }
match axis { match axis {
Some(Axis::Vertical) => *delta = point(0., delta.y()), Some(Axis::Vertical) => {
Some(Axis::Horizontal) => *delta = point(delta.x(), 0.), *delta = point(pk(0.), delta.y());
}
Some(Axis::Horizontal) => {
*delta = point(delta.x(), px(0.));
}
None => {} None => {}
} }
@ -167,13 +171,13 @@ impl ScrollManager {
self.ongoing.axis = axis; self.ongoing.axis = axis;
} }
pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> Point<Pixels> { pub fn scroll_position(&self, snapshot: &DisplaySnapshot) -> gpui::Point<Pixels> {
self.anchor.scroll_position(snapshot) self.anchor.scroll_position(snapshot)
} }
fn set_scroll_position( fn set_scroll_position(
&mut self, &mut self,
scroll_position: Point<Pixels>, scroll_position: gpui::Point<Pixels>,
map: &DisplaySnapshot, map: &DisplaySnapshot,
local: bool, local: bool,
autoscroll: bool, autoscroll: bool,
@ -282,160 +286,161 @@ impl ScrollManager {
} }
} }
impl Editor { // todo!()
pub fn vertical_scroll_margin(&mut self) -> usize { // impl Editor {
self.scroll_manager.vertical_scroll_margin as usize // pub fn vertical_scroll_margin(&mut self) -> usize {
} // self.scroll_manager.vertical_scroll_margin as usize
// }
pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) { // pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
self.scroll_manager.vertical_scroll_margin = margin_rows as f32; // self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
cx.notify(); // cx.notify();
} // }
pub fn visible_line_count(&self) -> Option<f32> { // pub fn visible_line_count(&self) -> Option<f32> {
self.scroll_manager.visible_line_count // self.scroll_manager.visible_line_count
} // }
pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext<Self>) { // pub(crate) fn set_visible_line_count(&mut self, lines: f32, cx: &mut ViewContext<Self>) {
let opened_first_time = self.scroll_manager.visible_line_count.is_none(); // let opened_first_time = self.scroll_manager.visible_line_count.is_none();
self.scroll_manager.visible_line_count = Some(lines); // self.scroll_manager.visible_line_count = Some(lines);
if opened_first_time { // if opened_first_time {
cx.spawn(|editor, mut cx| async move { // cx.spawn(|editor, mut cx| async move {
editor // editor
.update(&mut cx, |editor, cx| { // .update(&mut cx, |editor, cx| {
editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx) // editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
}) // })
.ok() // .ok()
}) // })
.detach() // .detach()
} // }
} // }
pub fn set_scroll_position( // pub fn set_scroll_position(
&mut self, // &mut self,
scroll_position: Point<Pixels>, // scroll_position: gpui::Point<Pixels>,
cx: &mut ViewContext<Self>, // cx: &mut ViewContext<Self>,
) { // ) {
self.set_scroll_position_internal(scroll_position, true, false, cx); // self.set_scroll_position_internal(scroll_position, true, false, cx);
} // }
pub(crate) fn set_scroll_position_internal( // pub(crate) fn set_scroll_position_internal(
&mut self, // &mut self,
scroll_position: Point<Pixels>, // scroll_position: gpui::Point<Pixels>,
local: bool, // local: bool,
autoscroll: bool, // autoscroll: bool,
cx: &mut ViewContext<Self>, // cx: &mut ViewContext<Self>,
) { // ) {
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); // let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
hide_hover(self, cx); // hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); // let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
self.scroll_manager.set_scroll_position( // self.scroll_manager.set_scroll_position(
scroll_position, // scroll_position,
&map, // &map,
local, // local,
autoscroll, // autoscroll,
workspace_id, // workspace_id,
cx, // cx,
); // );
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); // self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
} // }
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Point<Pixels> { // pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); // let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.scroll_manager.anchor.scroll_position(&display_map) // self.scroll_manager.anchor.scroll_position(&display_map)
} // }
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) { // pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
hide_hover(self, cx); // hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); // let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
let top_row = scroll_anchor // let top_row = scroll_anchor
.anchor // .anchor
.to_point(&self.buffer().read(cx).snapshot(cx)) // .to_point(&self.buffer().read(cx).snapshot(cx))
.row; // .row;
self.scroll_manager // self.scroll_manager
.set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx); // .set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
} // }
pub(crate) fn set_scroll_anchor_remote( // pub(crate) fn set_scroll_anchor_remote(
&mut self, // &mut self,
scroll_anchor: ScrollAnchor, // scroll_anchor: ScrollAnchor,
cx: &mut ViewContext<Self>, // cx: &mut ViewContext<Self>,
) { // ) {
hide_hover(self, cx); // hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1); // let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
let top_row = scroll_anchor // let top_row = scroll_anchor
.anchor // .anchor
.to_point(&self.buffer().read(cx).snapshot(cx)) // .to_point(&self.buffer().read(cx).snapshot(cx))
.row; // .row;
self.scroll_manager // self.scroll_manager
.set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx); // .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
} // }
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) { // pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
if matches!(self.mode, EditorMode::SingleLine) { // if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate_action(); // cx.propagate_action();
return; // return;
} // }
if self.take_rename(true, cx).is_some() { // if self.take_rename(true, cx).is_some() {
return; // return;
} // }
let cur_position = self.scroll_position(cx); // let cur_position = self.scroll_position(cx);
let new_pos = cur_position + point(0., amount.lines(self)); // let new_pos = cur_position + point(0., amount.lines(self));
self.set_scroll_position(new_pos, cx); // self.set_scroll_position(new_pos, cx);
} // }
/// Returns an ordering. The newest selection is: // /// Returns an ordering. The newest selection is:
/// Ordering::Equal => on screen // /// Ordering::Equal => on screen
/// Ordering::Less => above the screen // /// Ordering::Less => above the screen
/// Ordering::Greater => below the screen // /// Ordering::Greater => below the screen
pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering { // pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx)); // let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let newest_head = self // let newest_head = self
.selections // .selections
.newest_anchor() // .newest_anchor()
.head() // .head()
.to_display_point(&snapshot); // .to_display_point(&snapshot);
let screen_top = self // let screen_top = self
.scroll_manager // .scroll_manager
.anchor // .anchor
.anchor // .anchor
.to_display_point(&snapshot); // .to_display_point(&snapshot);
if screen_top > newest_head { // if screen_top > newest_head {
return Ordering::Less; // return Ordering::Less;
} // }
if let Some(visible_lines) = self.visible_line_count() { // if let Some(visible_lines) = self.visible_line_count() {
if newest_head.row() < screen_top.row() + visible_lines as u32 { // if newest_head.row() < screen_top.row() + visible_lines as u32 {
return Ordering::Equal; // return Ordering::Equal;
} // }
} // }
Ordering::Greater // Ordering::Greater
} // }
pub fn read_scroll_position_from_db( // pub fn read_scroll_position_from_db(
&mut self, // &mut self,
item_id: usize, // item_id: usize,
workspace_id: WorkspaceId, // workspace_id: WorkspaceId,
cx: &mut ViewContext<Editor>, // cx: &mut ViewContext<Editor>,
) { // ) {
let scroll_position = DB.get_scroll_position(item_id, workspace_id); // let scroll_position = DB.get_scroll_position(item_id, workspace_id);
if let Ok(Some((top_row, x, y))) = scroll_position { // if let Ok(Some((top_row, x, y))) = scroll_position {
let top_anchor = self // let top_anchor = self
.buffer() // .buffer()
.read(cx) // .read(cx)
.snapshot(cx) // .snapshot(cx)
.anchor_at(Point::new(top_row as u32, 0), Bias::Left); // .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
let scroll_anchor = ScrollAnchor { // let scroll_anchor = ScrollAnchor {
offset: Point::new(x, y), // offset: Point::new(x, y),
anchor: top_anchor, // anchor: top_anchor,
}; // };
self.set_scroll_anchor(scroll_anchor, cx); // self.set_scroll_anchor(scroll_anchor, cx);
} // }
} // }
} // }

View file

@ -593,29 +593,30 @@ impl<'a> MutableSelectionsCollection<'a> {
} }
pub fn select_anchor_ranges<I: IntoIterator<Item = Range<Anchor>>>(&mut self, ranges: I) { pub fn select_anchor_ranges<I: IntoIterator<Item = Range<Anchor>>>(&mut self, ranges: I) {
let buffer = self.buffer.read(self.cx).snapshot(self.cx); todo!()
let selections = ranges // let buffer = self.buffer.read(self.cx).snapshot(self.cx);
.into_iter() // let selections = ranges
.map(|range| { // .into_iter()
let mut start = range.start; // .map(|range| {
let mut end = range.end; // let mut start = range.start;
let reversed = if start.cmp(&end, &buffer).is_gt() { // let mut end = range.end;
mem::swap(&mut start, &mut end); // let reversed = if start.cmp(&end, &buffer).is_gt() {
true // mem::swap(&mut start, &mut end);
} else { // true
false // } else {
}; // false
Selection { // };
id: post_inc(&mut self.collection.next_selection_id), // Selection {
start, // id: post_inc(&mut self.collection.next_selection_id),
end, // start,
reversed, // end,
goal: SelectionGoal::None, // reversed,
} // goal: SelectionGoal::None,
}) // }
.collect::<Vec<_>>(); // })
// .collect::<Vec<_>>();
self.select_anchors(selections) // self.select_anchors(selections)
} }
pub fn new_selection_id(&mut self) -> usize { pub fn new_selection_id(&mut self) -> usize {

View file

@ -1,80 +1,81 @@
pub mod editor_lsp_test_context; pub mod editor_lsp_test_context;
pub mod editor_test_context; pub mod editor_test_context;
use crate::{ // todo!()
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, // use crate::{
DisplayPoint, Editor, EditorMode, MultiBuffer, // display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
}; // DisplayPoint, Editor, EditorMode, MultiBuffer,
// };
use gpui::{Model, ViewContext}; // use gpui::{Model, ViewContext};
use project::Project; // use project::Project;
use util::test::{marked_text_offsets, marked_text_ranges}; // use util::test::{marked_text_offsets, marked_text_ranges};
#[cfg(test)] // #[cfg(test)]
#[ctor::ctor] // #[ctor::ctor]
fn init_logger() { // fn init_logger() {
if std::env::var("RUST_LOG").is_ok() { // if std::env::var("RUST_LOG").is_ok() {
env_logger::init(); // env_logger::init();
} // }
} // }
// Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one. // // Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one.
pub fn marked_display_snapshot( // pub fn marked_display_snapshot(
text: &str, // text: &str,
cx: &mut gpui::AppContext, // cx: &mut gpui::AppContext,
) -> (DisplaySnapshot, Vec<DisplayPoint>) { // ) -> (DisplaySnapshot, Vec<DisplayPoint>) {
let (unmarked_text, markers) = marked_text_offsets(text); // let (unmarked_text, markers) = marked_text_offsets(text);
let family_id = cx // let family_id = cx
.font_cache() // .font_cache()
.load_family(&["Helvetica"], &Default::default()) // .load_family(&["Helvetica"], &Default::default())
.unwrap(); // .unwrap();
let font_id = cx // let font_id = cx
.font_cache() // .font_cache()
.select_font(family_id, &Default::default()) // .select_font(family_id, &Default::default())
.unwrap(); // .unwrap();
let font_size = 14.0; // let font_size = 14.0;
let buffer = MultiBuffer::build_simple(&unmarked_text, cx); // let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
let display_map = // let display_map =
cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx)); // cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); // let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
let markers = markers // let markers = markers
.into_iter() // .into_iter()
.map(|offset| offset.to_display_point(&snapshot)) // .map(|offset| offset.to_display_point(&snapshot))
.collect(); // .collect();
(snapshot, markers) // (snapshot, markers)
} // }
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) { // pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true); // let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), unmarked_text); // assert_eq!(editor.text(cx), unmarked_text);
editor.change_selections(None, cx, |s| s.select_ranges(text_ranges)); // editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
} // }
pub fn assert_text_with_selections( // pub fn assert_text_with_selections(
editor: &mut Editor, // editor: &mut Editor,
marked_text: &str, // marked_text: &str,
cx: &mut ViewContext<Editor>, // cx: &mut ViewContext<Editor>,
) { // ) {
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true); // let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
assert_eq!(editor.text(cx), unmarked_text); // assert_eq!(editor.text(cx), unmarked_text);
assert_eq!(editor.selections.ranges(cx), text_ranges); // assert_eq!(editor.selections.ranges(cx), text_ranges);
} // }
// RA thinks this is dead code even though it is used in a whole lot of tests // // RA thinks this is dead code even though it is used in a whole lot of tests
#[allow(dead_code)] // #[allow(dead_code)]
#[cfg(any(test, feature = "test-support"))] // #[cfg(any(test, feature = "test-support"))]
pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor { // pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
Editor::new(EditorMode::Full, buffer, None, None, cx) // Editor::new(EditorMode::Full, buffer, None, None, cx)
} // }
pub(crate) fn build_editor_with_project( // pub(crate) fn build_editor_with_project(
project: Model<Project>, // project: Model<Project>,
buffer: Model<MultiBuffer>, // buffer: Model<MultiBuffer>,
cx: &mut ViewContext<Editor>, // cx: &mut ViewContext<Editor>,
) -> Editor { // ) -> Editor {
Editor::new(EditorMode::Full, buffer, Some(project), None, cx) // Editor::new(EditorMode::Full, buffer, Some(project), None, cx)
} // }

View file

@ -17,304 +17,304 @@ use util::{
test::{generate_marked_text, marked_text_ranges}, test::{generate_marked_text, marked_text_ranges},
}; };
use super::build_editor_with_project; // use super::build_editor_with_project;
pub struct EditorTestContext<'a> { // pub struct EditorTestContext<'a> {
pub cx: &'a mut gpui::TestAppContext, // pub cx: &'a mut gpui::TestAppContext,
pub window: AnyWindowHandle, // pub window: AnyWindowHandle,
pub editor: View<Editor>, // pub editor: View<Editor>,
} // }
impl<'a> EditorTestContext<'a> { // impl<'a> EditorTestContext<'a> {
pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> { // pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
let fs = FakeFs::new(cx.background()); // let fs = FakeFs::new(cx.background());
// fs.insert_file("/file", "".to_owned()).await; // // fs.insert_file("/file", "".to_owned()).await;
fs.insert_tree( // fs.insert_tree(
"/root", // "/root",
gpui::serde_json::json!({ // gpui::serde_json::json!({
"file": "", // "file": "",
}), // }),
) // )
.await; // .await;
let project = Project::test(fs, ["/root".as_ref()], cx).await; // let project = Project::test(fs, ["/root".as_ref()], cx).await;
let buffer = project // let buffer = project
.update(cx, |project, cx| { // .update(cx, |project, cx| {
project.open_local_buffer("/root/file", cx) // project.open_local_buffer("/root/file", cx)
}) // })
.await // .await
.unwrap(); // .unwrap();
let window = cx.add_window(|cx| { // let window = cx.add_window(|cx| {
cx.focus_self(); // cx.focus_self();
build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx) // build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx)
}); // });
let editor = window.root(cx); // let editor = window.root(cx);
Self { // Self {
cx, // cx,
window: window.into(), // window: window.into(),
editor, // editor,
} // }
} // }
pub fn condition( // pub fn condition(
&self, // &self,
predicate: impl FnMut(&Editor, &AppContext) -> bool, // predicate: impl FnMut(&Editor, &AppContext) -> bool,
) -> impl Future<Output = ()> { // ) -> impl Future<Output = ()> {
self.editor.condition(self.cx, predicate) // self.editor.condition(self.cx, predicate)
} // }
pub fn editor<F, T>(&self, read: F) -> T // pub fn editor<F, T>(&self, read: F) -> T
where // where
F: FnOnce(&Editor, &ViewContext<Editor>) -> T, // F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
{ // {
self.editor.read_with(self.cx, read) // self.editor.update(self.cx, read)
} // }
pub fn update_editor<F, T>(&mut self, update: F) -> T // pub fn update_editor<F, T>(&mut self, update: F) -> T
where // where
F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T, // F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
{ // {
self.editor.update(self.cx, update) // self.editor.update(self.cx, update)
} // }
pub fn multibuffer<F, T>(&self, read: F) -> T // pub fn multibuffer<F, T>(&self, read: F) -> T
where // where
F: FnOnce(&MultiBuffer, &AppContext) -> T, // F: FnOnce(&MultiBuffer, &AppContext) -> T,
{ // {
self.editor(|editor, cx| read(editor.buffer().read(cx), cx)) // self.editor(|editor, cx| read(editor.buffer().read(cx), cx))
} // }
pub fn update_multibuffer<F, T>(&mut self, update: F) -> T // pub fn update_multibuffer<F, T>(&mut self, update: F) -> T
where // where
F: FnOnce(&mut MultiBuffer, &mut ModelContext<MultiBuffer>) -> T, // F: FnOnce(&mut MultiBuffer, &mut ModelContext<MultiBuffer>) -> T,
{ // {
self.update_editor(|editor, cx| editor.buffer().update(cx, update)) // self.update_editor(|editor, cx| editor.buffer().update(cx, update))
} // }
pub fn buffer_text(&self) -> String { // pub fn buffer_text(&self) -> String {
self.multibuffer(|buffer, cx| buffer.snapshot(cx).text()) // self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
} // }
pub fn buffer<F, T>(&self, read: F) -> T // pub fn buffer<F, T>(&self, read: F) -> T
where // where
F: FnOnce(&Buffer, &AppContext) -> T, // F: FnOnce(&Buffer, &AppContext) -> T,
{ // {
self.multibuffer(|multibuffer, cx| { // self.multibuffer(|multibuffer, cx| {
let buffer = multibuffer.as_singleton().unwrap().read(cx); // let buffer = multibuffer.as_singleton().unwrap().read(cx);
read(buffer, cx) // read(buffer, cx)
}) // })
} // }
pub fn update_buffer<F, T>(&mut self, update: F) -> T // pub fn update_buffer<F, T>(&mut self, update: F) -> T
where // where
F: FnOnce(&mut Buffer, &mut ModelContext<Buffer>) -> T, // F: FnOnce(&mut Buffer, &mut ModelContext<Buffer>) -> T,
{ // {
self.update_multibuffer(|multibuffer, cx| { // self.update_multibuffer(|multibuffer, cx| {
let buffer = multibuffer.as_singleton().unwrap(); // let buffer = multibuffer.as_singleton().unwrap();
buffer.update(cx, update) // buffer.update(cx, update)
}) // })
} // }
pub fn buffer_snapshot(&self) -> BufferSnapshot { // pub fn buffer_snapshot(&self) -> BufferSnapshot {
self.buffer(|buffer, _| buffer.snapshot()) // self.buffer(|buffer, _| buffer.snapshot())
} // }
// pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle { // pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
// let keystroke_under_test_handle = // let keystroke_under_test_handle =
// self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text)); // self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
// let keystroke = Keystroke::parse(keystroke_text).unwrap(); // let keystroke = Keystroke::parse(keystroke_text).unwrap();
// self.cx.dispatch_keystroke(self.window, keystroke, false); // self.cx.dispatch_keystroke(self.window, keystroke, false);
// keystroke_under_test_handle // keystroke_under_test_handle
// } // }
// pub fn simulate_keystrokes<const COUNT: usize>( // pub fn simulate_keystrokes<const COUNT: usize>(
// &mut self, // &mut self,
// keystroke_texts: [&str; COUNT], // keystroke_texts: [&str; COUNT],
// ) -> ContextHandle { // ) -> ContextHandle {
// let keystrokes_under_test_handle = // let keystrokes_under_test_handle =
// self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts)); // self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
// for keystroke_text in keystroke_texts.into_iter() { // for keystroke_text in keystroke_texts.into_iter() {
// self.simulate_keystroke(keystroke_text); // self.simulate_keystroke(keystroke_text);
// } // }
// // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete // // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
// // before returning. // // before returning.
// // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too // // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
// // quickly races with async actions. // // quickly races with async actions.
// if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() { // if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
// executor.run_until_parked(); // executor.run_until_parked();
// } else { // } else {
// unreachable!(); // unreachable!();
// } // }
// keystrokes_under_test_handle // keystrokes_under_test_handle
// } // }
pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> { // pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false); // let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
assert_eq!(self.buffer_text(), unmarked_text); // assert_eq!(self.buffer_text(), unmarked_text);
ranges // ranges
} // }
pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint { // pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
let ranges = self.ranges(marked_text); // let ranges = self.ranges(marked_text);
let snapshot = self // let snapshot = self
.editor // .editor
.update(self.cx, |editor, cx| editor.snapshot(cx)); // .update(self.cx, |editor, cx| editor.snapshot(cx));
ranges[0].start.to_display_point(&snapshot) // ranges[0].start.to_display_point(&snapshot)
} // }
// Returns anchors for the current buffer using `«` and `»` // // Returns anchors for the current buffer using `«` and `»`
pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> { // pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
let ranges = self.ranges(marked_text); // let ranges = self.ranges(marked_text);
let snapshot = self.buffer_snapshot(); // let snapshot = self.buffer_snapshot();
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end) // snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
} // }
pub fn set_diff_base(&mut self, diff_base: Option<&str>) { // pub fn set_diff_base(&mut self, diff_base: Option<&str>) {
let diff_base = diff_base.map(String::from); // let diff_base = diff_base.map(String::from);
self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx)); // self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
} // }
// /// Change the editor's text and selections using a string containing // /// Change the editor's text and selections using a string containing
// /// embedded range markers that represent the ranges and directions of // /// embedded range markers that represent the ranges and directions of
// /// each selection. // /// each selection.
// /// // ///
// /// Returns a context handle so that assertion failures can print what // /// Returns a context handle so that assertion failures can print what
// /// editor state was needed to cause the failure. // /// editor state was needed to cause the failure.
// /// // ///
// /// See the `util::test::marked_text_ranges` function for more information. // /// See the `util::test::marked_text_ranges` function for more information.
// pub fn set_state(&mut self, marked_text: &str) -> ContextHandle { // pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
// let state_context = self.add_assertion_context(format!( // let state_context = self.add_assertion_context(format!(
// "Initial Editor State: \"{}\"", // "Initial Editor State: \"{}\"",
// marked_text.escape_debug().to_string() // marked_text.escape_debug().to_string()
// )); // ));
// let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); // let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
// self.editor.update(self.cx, |editor, cx| { // self.editor.update(self.cx, |editor, cx| {
// editor.set_text(unmarked_text, cx); // editor.set_text(unmarked_text, cx);
// editor.change_selections(Some(Autoscroll::fit()), cx, |s| { // editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
// s.select_ranges(selection_ranges) // s.select_ranges(selection_ranges)
// }) // })
// }); // });
// state_context // state_context
// } // }
// /// Only change the editor's selections // /// Only change the editor's selections
// pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle { // pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
// let state_context = self.add_assertion_context(format!( // let state_context = self.add_assertion_context(format!(
// "Initial Editor State: \"{}\"", // "Initial Editor State: \"{}\"",
// marked_text.escape_debug().to_string() // marked_text.escape_debug().to_string()
// )); // ));
// let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true); // let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
// self.editor.update(self.cx, |editor, cx| { // self.editor.update(self.cx, |editor, cx| {
// assert_eq!(editor.text(cx), unmarked_text); // assert_eq!(editor.text(cx), unmarked_text);
// editor.change_selections(Some(Autoscroll::fit()), cx, |s| { // editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
// s.select_ranges(selection_ranges) // s.select_ranges(selection_ranges)
// }) // })
// }); // });
// state_context // state_context
// } // }
// /// Make an assertion about the editor's text and the ranges and directions // /// Make an assertion about the editor's text and the ranges and directions
// /// of its selections using a string containing embedded range markers. // /// of its selections using a string containing embedded range markers.
// /// // ///
// /// See the `util::test::marked_text_ranges` function for more information. // /// See the `util::test::marked_text_ranges` function for more information.
// #[track_caller] // #[track_caller]
// pub fn assert_editor_state(&mut self, marked_text: &str) { // pub fn assert_editor_state(&mut self, marked_text: &str) {
// let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true); // let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
// let buffer_text = self.buffer_text(); // let buffer_text = self.buffer_text();
// if buffer_text != unmarked_text { // if buffer_text != unmarked_text {
// panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}"); // panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
// } // }
// self.assert_selections(expected_selections, marked_text.to_string()) // self.assert_selections(expected_selections, marked_text.to_string())
// } // }
// pub fn editor_state(&mut self) -> String { // pub fn editor_state(&mut self) -> String {
// generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true) // generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
// } // }
// #[track_caller] // #[track_caller]
// pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) { // pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
// let expected_ranges = self.ranges(marked_text); // let expected_ranges = self.ranges(marked_text);
// let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| { // let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
// let snapshot = editor.snapshot(cx); // let snapshot = editor.snapshot(cx);
// editor // editor
// .background_highlights // .background_highlights
// .get(&TypeId::of::<Tag>()) // .get(&TypeId::of::<Tag>())
// .map(|h| h.1.clone()) // .map(|h| h.1.clone())
// .unwrap_or_default() // .unwrap_or_default()
// .into_iter() // .into_iter()
// .map(|range| range.to_offset(&snapshot.buffer_snapshot)) // .map(|range| range.to_offset(&snapshot.buffer_snapshot))
// .collect() // .collect()
// }); // });
// assert_set_eq!(actual_ranges, expected_ranges); // assert_set_eq!(actual_ranges, expected_ranges);
// } // }
// #[track_caller] // #[track_caller]
// pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) { // pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
// let expected_ranges = self.ranges(marked_text); // let expected_ranges = self.ranges(marked_text);
// let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); // let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
// let actual_ranges: Vec<Range<usize>> = snapshot // let actual_ranges: Vec<Range<usize>> = snapshot
// .text_highlight_ranges::<Tag>() // .text_highlight_ranges::<Tag>()
// .map(|ranges| ranges.as_ref().clone().1) // .map(|ranges| ranges.as_ref().clone().1)
// .unwrap_or_default() // .unwrap_or_default()
// .into_iter() // .into_iter()
// .map(|range| range.to_offset(&snapshot.buffer_snapshot)) // .map(|range| range.to_offset(&snapshot.buffer_snapshot))
// .collect(); // .collect();
// assert_set_eq!(actual_ranges, expected_ranges); // assert_set_eq!(actual_ranges, expected_ranges);
// } // }
// #[track_caller] // #[track_caller]
// pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) { // pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
// let expected_marked_text = // let expected_marked_text =
// generate_marked_text(&self.buffer_text(), &expected_selections, true); // generate_marked_text(&self.buffer_text(), &expected_selections, true);
// self.assert_selections(expected_selections, expected_marked_text) // self.assert_selections(expected_selections, expected_marked_text)
// } // }
// fn editor_selections(&self) -> Vec<Range<usize>> { // fn editor_selections(&self) -> Vec<Range<usize>> {
// self.editor // self.editor
// .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx)) // .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
// .into_iter() // .into_iter()
// .map(|s| { // .map(|s| {
// if s.reversed { // if s.reversed {
// s.end..s.start // s.end..s.start
// } else { // } else {
// s.start..s.end // s.start..s.end
// } // }
// }) // })
// .collect::<Vec<_>>() // .collect::<Vec<_>>()
// } // }
// #[track_caller] // #[track_caller]
// fn assert_selections( // fn assert_selections(
// &mut self, // &mut self,
// expected_selections: Vec<Range<usize>>, // expected_selections: Vec<Range<usize>>,
// expected_marked_text: String, // expected_marked_text: String,
// ) { // ) {
// let actual_selections = self.editor_selections(); // let actual_selections = self.editor_selections();
// let actual_marked_text = // let actual_marked_text =
// generate_marked_text(&self.buffer_text(), &actual_selections, true); // generate_marked_text(&self.buffer_text(), &actual_selections, true);
// if expected_selections != actual_selections { // if expected_selections != actual_selections {
// panic!( // panic!(
// indoc! {" // indoc! {"
// {}Editor has unexpected selections. // {}Editor has unexpected selections.
// Expected selections: // Expected selections:
// {} // {}
// Actual selections: // Actual selections:
// {} // {}
// "}, // "},
// self.assertion_context(), // self.assertion_context(),
// expected_marked_text, // expected_marked_text,
// actual_marked_text, // actual_marked_text,
// ); // );
// } // }
// } // }
} // }
impl<'a> Deref for EditorTestContext<'a> { impl<'a> Deref for EditorTestContext<'a> {
type Target = gpui::TestAppContext; type Target = gpui::TestAppContext;