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" }
language = { package = "language2", path = "../language2" }
lsp = { package = "lsp2", path = "../lsp2" }
multi_buffer = { path = "../multi_buffer" }
multi_buffer = { package = "multi_buffer2", path = "../multi_buffer2" }
project = { package = "project2", path = "../project2" }
rpc = { package = "rpc2", path = "../rpc2" }
rich_text = { path = "../rich_text" }
@ -80,7 +80,7 @@ util = { path = "../util", features = ["test-support"] }
project = { package = "project2", path = "../project2", features = ["test-support"] }
settings = { package = "settings2", path = "../settings2", 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
env_logger.workspace = true

View file

@ -1,5 +1,6 @@
use crate::EditorSettings;
use gpui::{Entity, ModelContext};
use gpui::ModelContext;
use settings::Settings;
use settings::SettingsStore;
use smol::Timer;
use std::time::Duration;
@ -16,7 +17,7 @@ pub struct BlinkManager {
impl BlinkManager {
pub fn new(blink_interval: Duration, cx: &mut ModelContext<Self>) -> Self {
// 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)
})
.detach();
@ -41,14 +42,9 @@ impl BlinkManager {
let epoch = self.next_blink_epoch();
let interval = self.blink_interval;
cx.spawn(|this, mut cx| {
let this = this.downgrade();
async move {
cx.spawn(|this, mut cx| async move {
Timer::after(interval).await;
if let Some(this) = this.upgrade(&cx) {
this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
}
}
})
.detach();
}
@ -68,14 +64,11 @@ impl BlinkManager {
let epoch = self.next_blink_epoch();
let interval = self.blink_interval;
cx.spawn(|this, mut cx| {
let this = this.downgrade();
async move {
cx.spawn(|this, mut cx| async move {
Timer::after(interval).await;
if let Some(this) = this.upgrade(&cx) {
if let Some(this) = this.upgrade() {
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
}
}
})
.detach();
}

View file

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

View file

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

View file

@ -4,7 +4,7 @@ use super::{
Highlights,
};
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 lazy_static::lazy_static;
use smol::future::yield_now;
@ -20,7 +20,7 @@ pub struct WrapMap {
pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
interpolated_edits: Patch<u32>,
edits_since_sync: Patch<u32>,
wrap_width: Option<f32>,
wrap_width: Option<Pixels>,
background_task: Option<Task<()>>,
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 {
return false;
}
@ -379,7 +383,7 @@ impl WrapSnapshot {
&mut self,
new_tab_snapshot: TabSnapshot,
tab_edits: &[TabEdit],
wrap_width: f32,
wrap_width: Pixels,
line_wrapper: &mut LineWrapper,
) -> Patch<u32> {
#[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 serde::{Deserialize, Serialize};
use settings::Settings;
#[derive(Deserialize)]
pub struct EditorSettings {
@ -55,7 +55,7 @@ impl Settings for EditorSettings {
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
_: &mut gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}

View file

@ -3,9 +3,11 @@ use super::{
};
use crate::{
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 std::{ops::Range, sync::Arc};
use sum_tree::Bias;
@ -1997,7 +1999,10 @@ impl Element<Editor> for EditorElement {
element_state: &mut Self::ElementState,
cx: &mut gpui::ViewContext<Editor>,
) -> 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(
@ -2011,13 +2016,8 @@ impl Element<Editor> for EditorElement {
let layout_text = cx.text_system().layout_text(
"hello world",
text_style.font_size,
&[TextRun {
len: "hello world".len(),
font: text_style.font,
color: text_style.color,
underline: text_style.underline,
}],
text_style.font_size * cx.rem_size(),
&[text_style.to_run("hello world".len())],
None,
);
}
@ -2697,19 +2697,19 @@ impl PositionMap {
position: gpui::Point<Pixels>,
) -> PointForPosition {
let scroll_position = self.snapshot.scroll_position();
let position = position - text_bounds.origin();
let y = position.y().max(0.0).min(self.size.y());
let x = position.x() + (scroll_position.x() * self.em_width);
let row = (y / self.line_height + scroll_position.y()) as u32;
let position = position - text_bounds.origin;
let y = position.y.max(px(0.)).min(self.size.width);
let x = position.x + (scroll_position.x * self.em_width);
let row = (y / self.line_height + scroll_position.y).into();
let (column, x_overshoot_after_line_end) = if let Some(line) = self
.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)
{
if let Some(ix) = line.index_for_x(x) {
(ix as u32, 0.0)
} else {
(line.len() as u32, 0f32.max(x - line.width()))
(line.len() as u32, px(0.).max(x - line.width()))
}
} else {
(0, x)

View file

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

View file

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

View file

@ -7,8 +7,8 @@ use anyhow::{Context, Result};
use collections::HashSet;
use futures::future::try_join_all;
use gpui::{
point, AnyElement, AppContext, AsyncAppContext, Entity, Model, Pixels, Subscription, Task,
View, ViewContext, WeakView,
point, AnyElement, AppContext, AsyncAppContext, Entity, Model, Pixels, SharedString,
Subscription, Task, View, ViewContext, WeakView,
};
use language::{
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(
this: WeakView<Editor>,
project: Model<Project>,
message: proto::update_view::Editor,
cx: &mut AsyncAppContext,
) -> Result<()> {
// Open all of the buffers of which excerpts were added to the editor.
let inserted_excerpt_buffer_ids = message
.inserted_excerpts
.iter()
.filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id))
.collect::<HashSet<_>>();
let inserted_excerpt_buffers = project.update(cx, |project, cx| {
inserted_excerpt_buffer_ids
.into_iter()
.map(|id| project.open_buffer_by_id(id, cx))
.collect::<Vec<_>>()
});
let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;
// async fn update_editor_from_message(
// this: WeakView<Editor>,
// project: Model<Project>,
// message: proto::update_view::Editor,
// cx: &mut AsyncAppContext,
// ) -> Result<()> {
// // Open all of the buffers of which excerpts were added to the editor.
// let inserted_excerpt_buffer_ids = message
// .inserted_excerpts
// .iter()
// .filter_map(|insertion| Some(insertion.excerpt.as_ref()?.buffer_id))
// .collect::<HashSet<_>>();
// let inserted_excerpt_buffers = project.update(cx, |project, cx| {
// inserted_excerpt_buffer_ids
// .into_iter()
// .map(|id| project.open_buffer_by_id(id, cx))
// .collect::<Vec<_>>()
// })?;
// let _inserted_excerpt_buffers = try_join_all(inserted_excerpt_buffers).await?;
// Update the editor's excerpts.
this.update(cx, |editor, cx| {
editor.buffer.update(cx, |multibuffer, cx| {
let mut removed_excerpt_ids = message
.deleted_excerpts
.into_iter()
.map(ExcerptId::from_proto)
.collect::<Vec<_>>();
removed_excerpt_ids.sort_by({
let multibuffer = multibuffer.read(cx);
move |a, b| a.cmp(&b, &multibuffer)
});
// // Update the editor's excerpts.
// this.update(cx, |editor, cx| {
// editor.buffer.update(cx, |multibuffer, cx| {
// let mut removed_excerpt_ids = message
// .deleted_excerpts
// .into_iter()
// .map(ExcerptId::from_proto)
// .collect::<Vec<_>>();
// removed_excerpt_ids.sort_by({
// let multibuffer = multibuffer.read(cx);
// move |a, b| a.cmp(&b, &multibuffer)
// });
let mut insertions = message.inserted_excerpts.into_iter().peekable();
while let Some(insertion) = insertions.next() {
let Some(excerpt) = insertion.excerpt else {
continue;
};
let Some(previous_excerpt_id) = insertion.previous_excerpt_id else {
continue;
};
let buffer_id = excerpt.buffer_id;
let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
continue;
};
// let mut insertions = message.inserted_excerpts.into_iter().peekable();
// while let Some(insertion) = insertions.next() {
// let Some(excerpt) = insertion.excerpt else {
// continue;
// };
// let Some(previous_excerpt_id) = insertion.previous_excerpt_id else {
// continue;
// };
// let buffer_id = excerpt.buffer_id;
// let Some(buffer) = project.read(cx).buffer_for_id(buffer_id) else {
// continue;
// };
let adjacent_excerpts = iter::from_fn(|| {
let insertion = insertions.peek()?;
if insertion.previous_excerpt_id.is_none()
&& insertion.excerpt.as_ref()?.buffer_id == buffer_id
{
insertions.next()?.excerpt
} else {
None
}
});
// let adjacent_excerpts = iter::from_fn(|| {
// let insertion = insertions.peek()?;
// if insertion.previous_excerpt_id.is_none()
// && insertion.excerpt.as_ref()?.buffer_id == buffer_id
// {
// insertions.next()?.excerpt
// } else {
// None
// }
// });
multibuffer.insert_excerpts_with_ids_after(
ExcerptId::from_proto(previous_excerpt_id),
buffer,
[excerpt]
.into_iter()
.chain(adjacent_excerpts)
.filter_map(|excerpt| {
Some((
ExcerptId::from_proto(excerpt.id),
deserialize_excerpt_range(excerpt)?,
))
}),
cx,
);
}
// multibuffer.insert_excerpts_with_ids_after(
// ExcerptId::from_proto(previous_excerpt_id),
// buffer,
// [excerpt]
// .into_iter()
// .chain(adjacent_excerpts)
// .filter_map(|excerpt| {
// Some((
// ExcerptId::from_proto(excerpt.id),
// deserialize_excerpt_range(excerpt)?,
// ))
// }),
// cx,
// );
// }
multibuffer.remove_excerpts(removed_excerpt_ids, cx);
});
})?;
// multibuffer.remove_excerpts(removed_excerpt_ids, cx);
// });
// })?;
// Deserialize the editor state.
let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
let buffer = editor.buffer.read(cx).read(cx);
let selections = message
.selections
.into_iter()
.filter_map(|selection| deserialize_selection(&buffer, selection))
.collect::<Vec<_>>();
let pending_selection = message
.pending_selection
.and_then(|selection| deserialize_selection(&buffer, selection));
let scroll_top_anchor = message
.scroll_top_anchor
.and_then(|anchor| deserialize_anchor(&buffer, anchor));
anyhow::Ok((selections, pending_selection, scroll_top_anchor))
})??;
// // Deserialize the editor state.
// let (selections, pending_selection, scroll_top_anchor) = this.update(cx, |editor, cx| {
// let buffer = editor.buffer.read(cx).read(cx);
// let selections = message
// .selections
// .into_iter()
// .filter_map(|selection| deserialize_selection(&buffer, selection))
// .collect::<Vec<_>>();
// let pending_selection = message
// .pending_selection
// .and_then(|selection| deserialize_selection(&buffer, selection));
// let scroll_top_anchor = message
// .scroll_top_anchor
// .and_then(|anchor| deserialize_anchor(&buffer, anchor));
// anyhow::Ok((selections, pending_selection, scroll_top_anchor))
// })??;
// Wait until the buffer has received all of the operations referenced by
// the editor's new state.
this.update(cx, |editor, cx| {
editor.buffer.update(cx, |buffer, cx| {
buffer.wait_for_anchors(
selections
.iter()
.chain(pending_selection.as_ref())
.flat_map(|selection| [selection.start, selection.end])
.chain(scroll_top_anchor),
cx,
)
})
})?
.await?;
// // Wait until the buffer has received all of the operations referenced by
// // the editor's new state.
// this.update(cx, |editor, cx| {
// editor.buffer.update(cx, |buffer, cx| {
// buffer.wait_for_anchors(
// selections
// .iter()
// .chain(pending_selection.as_ref())
// .flat_map(|selection| [selection.start, selection.end])
// .chain(scroll_top_anchor),
// cx,
// )
// })
// })?
// .await?;
// Update the editor's state.
this.update(cx, |editor, cx| {
if !selections.is_empty() || pending_selection.is_some() {
editor.set_selections_from_remote(selections, pending_selection, cx);
editor.request_autoscroll_remotely(Autoscroll::newest(), cx);
} else if let Some(scroll_top_anchor) = scroll_top_anchor {
editor.set_scroll_anchor_remote(
ScrollAnchor {
anchor: scroll_top_anchor,
offset: point(message.scroll_x, message.scroll_y),
},
cx,
);
}
})?;
Ok(())
}
// // Update the editor's state.
// this.update(cx, |editor, cx| {
// if !selections.is_empty() || pending_selection.is_some() {
// editor.set_selections_from_remote(selections, pending_selection, cx);
// editor.request_autoscroll_remotely(Autoscroll::newest(), cx);
// } else if let Some(scroll_top_anchor) = scroll_top_anchor {
// editor.set_scroll_anchor_remote(
// ScrollAnchor {
// anchor: scroll_top_anchor,
// offset: point(message.scroll_x, message.scroll_y),
// },
// cx,
// );
// }
// })?;
// Ok(())
// }
fn serialize_excerpt(
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
.buffer()
.read(cx)
@ -559,7 +559,7 @@ impl Item for Editor {
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)? {
Cow::Borrowed(path) => Some(path.to_string_lossy()),
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,
cmd_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 {
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,
ToPoint,
};
use gpui::{point, AppContext, Pixels, Task, ViewContext};
use gpui::{point, px, AppContext, Pixels, Styled, Task, ViewContext};
use language::{Bias, Point};
use std::{
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;
if self.anchor != Anchor::min() {
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_LOWER_BOUND: f32 = 6.;
let mut axis = self.axis;
let x = delta.x().abs();
let y = delta.y().abs();
let x = delta.x.abs();
let y = delta.y.abs();
let duration = Instant::now().duration_since(self.last_event);
if duration > SCROLL_EVENT_SEPARATION {
//New ongoing scroll will start, determine axis
@ -115,8 +115,12 @@ impl OngoingScroll {
}
match axis {
Some(Axis::Vertical) => *delta = point(0., delta.y()),
Some(Axis::Horizontal) => *delta = point(delta.x(), 0.),
Some(Axis::Vertical) => {
*delta = point(pk(0.), delta.y());
}
Some(Axis::Horizontal) => {
*delta = point(delta.x(), px(0.));
}
None => {}
}
@ -167,13 +171,13 @@ impl ScrollManager {
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)
}
fn set_scroll_position(
&mut self,
scroll_position: Point<Pixels>,
scroll_position: gpui::Point<Pixels>,
map: &DisplaySnapshot,
local: bool,
autoscroll: bool,
@ -282,160 +286,161 @@ impl ScrollManager {
}
}
impl Editor {
pub fn vertical_scroll_margin(&mut self) -> usize {
self.scroll_manager.vertical_scroll_margin as usize
}
// todo!()
// impl Editor {
// 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>) {
self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
cx.notify();
}
// 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;
// cx.notify();
// }
pub fn visible_line_count(&self) -> Option<f32> {
self.scroll_manager.visible_line_count
}
// pub fn visible_line_count(&self) -> Option<f32> {
// self.scroll_manager.visible_line_count
// }
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();
self.scroll_manager.visible_line_count = Some(lines);
if opened_first_time {
cx.spawn(|editor, mut cx| async move {
editor
.update(&mut cx, |editor, cx| {
editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
})
.ok()
})
.detach()
}
}
// 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();
// self.scroll_manager.visible_line_count = Some(lines);
// if opened_first_time {
// cx.spawn(|editor, mut cx| async move {
// editor
// .update(&mut cx, |editor, cx| {
// editor.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx)
// })
// .ok()
// })
// .detach()
// }
// }
pub fn set_scroll_position(
&mut self,
scroll_position: Point<Pixels>,
cx: &mut ViewContext<Self>,
) {
self.set_scroll_position_internal(scroll_position, true, false, cx);
}
// pub fn set_scroll_position(
// &mut self,
// scroll_position: gpui::Point<Pixels>,
// cx: &mut ViewContext<Self>,
// ) {
// self.set_scroll_position_internal(scroll_position, true, false, cx);
// }
pub(crate) fn set_scroll_position_internal(
&mut self,
scroll_position: Point<Pixels>,
local: bool,
autoscroll: bool,
cx: &mut ViewContext<Self>,
) {
let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
// pub(crate) fn set_scroll_position_internal(
// &mut self,
// scroll_position: gpui::Point<Pixels>,
// local: bool,
// autoscroll: bool,
// cx: &mut ViewContext<Self>,
// ) {
// let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
self.scroll_manager.set_scroll_position(
scroll_position,
&map,
local,
autoscroll,
workspace_id,
cx,
);
// hide_hover(self, cx);
// let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
// self.scroll_manager.set_scroll_position(
// scroll_position,
// &map,
// local,
// autoscroll,
// workspace_id,
// 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> {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.scroll_manager.anchor.scroll_position(&display_map)
}
// 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));
// self.scroll_manager.anchor.scroll_position(&display_map)
// }
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
let top_row = scroll_anchor
.anchor
.to_point(&self.buffer().read(cx).snapshot(cx))
.row;
self.scroll_manager
.set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
}
// pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
// hide_hover(self, cx);
// let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
// let top_row = scroll_anchor
// .anchor
// .to_point(&self.buffer().read(cx).snapshot(cx))
// .row;
// self.scroll_manager
// .set_anchor(scroll_anchor, top_row, true, false, workspace_id, cx);
// }
pub(crate) fn set_scroll_anchor_remote(
&mut self,
scroll_anchor: ScrollAnchor,
cx: &mut ViewContext<Self>,
) {
hide_hover(self, cx);
let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
let top_row = scroll_anchor
.anchor
.to_point(&self.buffer().read(cx).snapshot(cx))
.row;
self.scroll_manager
.set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
}
// pub(crate) fn set_scroll_anchor_remote(
// &mut self,
// scroll_anchor: ScrollAnchor,
// cx: &mut ViewContext<Self>,
// ) {
// hide_hover(self, cx);
// let workspace_id = self.workspace.as_ref().map(|workspace| workspace.1);
// let top_row = scroll_anchor
// .anchor
// .to_point(&self.buffer().read(cx).snapshot(cx))
// .row;
// self.scroll_manager
// .set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
// }
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate_action();
return;
}
// pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
// if matches!(self.mode, EditorMode::SingleLine) {
// cx.propagate_action();
// return;
// }
if self.take_rename(true, cx).is_some() {
return;
}
// if self.take_rename(true, cx).is_some() {
// return;
// }
let cur_position = self.scroll_position(cx);
let new_pos = cur_position + point(0., amount.lines(self));
self.set_scroll_position(new_pos, cx);
}
// let cur_position = self.scroll_position(cx);
// let new_pos = cur_position + point(0., amount.lines(self));
// self.set_scroll_position(new_pos, cx);
// }
/// Returns an ordering. The newest selection is:
/// Ordering::Equal => on screen
/// Ordering::Less => above the screen
/// Ordering::Greater => below the screen
pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let newest_head = self
.selections
.newest_anchor()
.head()
.to_display_point(&snapshot);
let screen_top = self
.scroll_manager
.anchor
.anchor
.to_display_point(&snapshot);
// /// Returns an ordering. The newest selection is:
// /// Ordering::Equal => on screen
// /// Ordering::Less => above the screen
// /// Ordering::Greater => below the screen
// pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
// let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
// let newest_head = self
// .selections
// .newest_anchor()
// .head()
// .to_display_point(&snapshot);
// let screen_top = self
// .scroll_manager
// .anchor
// .anchor
// .to_display_point(&snapshot);
if screen_top > newest_head {
return Ordering::Less;
}
// if screen_top > newest_head {
// return Ordering::Less;
// }
if let Some(visible_lines) = self.visible_line_count() {
if newest_head.row() < screen_top.row() + visible_lines as u32 {
return Ordering::Equal;
}
}
// if let Some(visible_lines) = self.visible_line_count() {
// if newest_head.row() < screen_top.row() + visible_lines as u32 {
// return Ordering::Equal;
// }
// }
Ordering::Greater
}
// Ordering::Greater
// }
pub fn read_scroll_position_from_db(
&mut self,
item_id: usize,
workspace_id: WorkspaceId,
cx: &mut ViewContext<Editor>,
) {
let scroll_position = DB.get_scroll_position(item_id, workspace_id);
if let Ok(Some((top_row, x, y))) = scroll_position {
let top_anchor = self
.buffer()
.read(cx)
.snapshot(cx)
.anchor_at(Point::new(top_row as u32, 0), Bias::Left);
let scroll_anchor = ScrollAnchor {
offset: Point::new(x, y),
anchor: top_anchor,
};
self.set_scroll_anchor(scroll_anchor, cx);
}
}
}
// pub fn read_scroll_position_from_db(
// &mut self,
// item_id: usize,
// workspace_id: WorkspaceId,
// cx: &mut ViewContext<Editor>,
// ) {
// let scroll_position = DB.get_scroll_position(item_id, workspace_id);
// if let Ok(Some((top_row, x, y))) = scroll_position {
// let top_anchor = self
// .buffer()
// .read(cx)
// .snapshot(cx)
// .anchor_at(Point::new(top_row as u32, 0), Bias::Left);
// let scroll_anchor = ScrollAnchor {
// offset: Point::new(x, y),
// anchor: top_anchor,
// };
// 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) {
let buffer = self.buffer.read(self.cx).snapshot(self.cx);
let selections = ranges
.into_iter()
.map(|range| {
let mut start = range.start;
let mut end = range.end;
let reversed = if start.cmp(&end, &buffer).is_gt() {
mem::swap(&mut start, &mut end);
true
} else {
false
};
Selection {
id: post_inc(&mut self.collection.next_selection_id),
start,
end,
reversed,
goal: SelectionGoal::None,
}
})
.collect::<Vec<_>>();
todo!()
// let buffer = self.buffer.read(self.cx).snapshot(self.cx);
// let selections = ranges
// .into_iter()
// .map(|range| {
// let mut start = range.start;
// let mut end = range.end;
// let reversed = if start.cmp(&end, &buffer).is_gt() {
// mem::swap(&mut start, &mut end);
// true
// } else {
// false
// };
// Selection {
// id: post_inc(&mut self.collection.next_selection_id),
// start,
// end,
// reversed,
// goal: SelectionGoal::None,
// }
// })
// .collect::<Vec<_>>();
self.select_anchors(selections)
// self.select_anchors(selections)
}
pub fn new_selection_id(&mut self) -> usize {

View file

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

View file

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