This commit is contained in:
Nathan Sobo 2023-11-02 21:03:29 -06:00
parent 72b9dc8216
commit dfc7c81500
12 changed files with 8192 additions and 8211 deletions

2
Cargo.lock generated
View file

@ -2648,7 +2648,7 @@ dependencies = [
"lazy_static", "lazy_static",
"log", "log",
"lsp2", "lsp2",
"multi_buffer", "multi_buffer2",
"ordered-float 2.10.0", "ordered-float 2.10.0",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"postage", "postage",

View file

@ -6,17 +6,16 @@ mod wrap_map;
use crate::{ use crate::{
link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt, link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
EditorStyle, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
}; };
pub use block_map::{BlockMap, BlockPoint}; pub use block_map::{BlockMap, BlockPoint};
use collections::{BTreeMap, HashMap, HashSet}; use collections::{BTreeMap, HashMap, HashSet};
use fold_map::FoldMap; use fold_map::FoldMap;
use gpui::{FontId, HighlightStyle, Hsla, Line, Model, ModelContext, UnderlineStyle}; use gpui::{FontId, HighlightStyle, Hsla, Line, Model, ModelContext};
use inlay_map::InlayMap; use inlay_map::InlayMap;
use language::{ use language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription, language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
}; };
use lsp::DiagnosticSeverity;
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc}; use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
use sum_tree::{Bias, TreeMap}; use sum_tree::{Bias, TreeMap};
use tab_map::TabMap; use tab_map::TabMap;

View file

@ -73,7 +73,7 @@ impl WrapMap {
wrap_width: Option<f32>, wrap_width: Option<f32>,
cx: &mut AppContext, cx: &mut AppContext,
) -> (Model<Self>, WrapSnapshot) { ) -> (Model<Self>, WrapSnapshot) {
let handle = cx.add_model(|cx| { let handle = cx.build_model(|cx| {
let mut this = Self { let mut this = Self {
font: (font_id, font_size), font: (font_id, font_size),
wrap_width: None, wrap_width: None,

File diff suppressed because it is too large Load diff

View file

@ -48,106 +48,108 @@ impl FollowableItem for Editor {
state: &mut Option<proto::view::Variant>, state: &mut Option<proto::view::Variant>,
cx: &mut AppContext, cx: &mut AppContext,
) -> Option<Task<Result<View<Self>>>> { ) -> Option<Task<Result<View<Self>>>> {
let project = workspace.read(cx).project().to_owned(); todo!()
let Some(proto::view::Variant::Editor(_)) = state else {
return None;
};
let Some(proto::view::Variant::Editor(state)) = state.take() else {
unreachable!()
};
let client = project.read(cx).client();
let replica_id = project.read(cx).replica_id();
let buffer_ids = state
.excerpts
.iter()
.map(|excerpt| excerpt.buffer_id)
.collect::<HashSet<_>>();
let buffers = project.update(cx, |project, cx| {
buffer_ids
.iter()
.map(|id| project.open_buffer_by_id(*id, cx))
.collect::<Vec<_>>()
});
let pane = pane.downgrade();
Some(cx.spawn(|mut cx| async move {
let mut buffers = futures::future::try_join_all(buffers).await?;
let editor = pane.read_with(&cx, |pane, cx| {
let mut editors = pane.items_of_type::<Self>();
editors.find(|editor| {
let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
let singleton_buffer_matches = state.singleton
&& buffers.first()
== editor.read(cx).buffer.read(cx).as_singleton().as_ref();
ids_match || singleton_buffer_matches
})
})?;
let editor = if let Some(editor) = editor {
editor
} else {
pane.update(&mut cx, |_, cx| {
let multibuffer = cx.add_model(|cx| {
let mut multibuffer;
if state.singleton && buffers.len() == 1 {
multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
} else {
multibuffer = MultiBuffer::new(replica_id);
let mut excerpts = state.excerpts.into_iter().peekable();
while let Some(excerpt) = excerpts.peek() {
let buffer_id = excerpt.buffer_id;
let buffer_excerpts = iter::from_fn(|| {
let excerpt = excerpts.peek()?;
(excerpt.buffer_id == buffer_id)
.then(|| excerpts.next().unwrap())
});
let buffer =
buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
if let Some(buffer) = buffer {
multibuffer.push_excerpts(
buffer.clone(),
buffer_excerpts.filter_map(deserialize_excerpt_range),
cx,
);
} }
} // let project = workspace.read(cx).project().to_owned();
}; // let Some(proto::view::Variant::Editor(_)) = state else {
// return None;
// };
// let Some(proto::view::Variant::Editor(state)) = state.take() else {
// unreachable!()
// };
if let Some(title) = &state.title { // let client = project.read(cx).client();
multibuffer = multibuffer.with_title(title.clone()) // let replica_id = project.read(cx).replica_id();
} // let buffer_ids = state
// .excerpts
// .iter()
// .map(|excerpt| excerpt.buffer_id)
// .collect::<HashSet<_>>();
// let buffers = project.update(cx, |project, cx| {
// buffer_ids
// .iter()
// .map(|id| project.open_buffer_by_id(*id, cx))
// .collect::<Vec<_>>()
// });
multibuffer // let pane = pane.downgrade();
}); // Some(cx.spawn(|mut cx| async move {
// let mut buffers = futures::future::try_join_all(buffers).await?;
// let editor = pane.read_with(&cx, |pane, cx| {
// let mut editors = pane.items_of_type::<Self>();
// editors.find(|editor| {
// let ids_match = editor.remote_id(&client, cx) == Some(remote_id);
// let singleton_buffer_matches = state.singleton
// && buffers.first()
// == editor.read(cx).buffer.read(cx).as_singleton().as_ref();
// ids_match || singleton_buffer_matches
// })
// })?;
cx.add_view(|cx| { // let editor = if let Some(editor) = editor {
let mut editor = // editor
Editor::for_multibuffer(multibuffer, Some(project.clone()), cx); // } else {
editor.remote_id = Some(remote_id); // pane.update(&mut cx, |_, cx| {
editor // let multibuffer = cx.add_model(|cx| {
}) // let mut multibuffer;
})? // if state.singleton && buffers.len() == 1 {
}; // multibuffer = MultiBuffer::singleton(buffers.pop().unwrap(), cx)
// } else {
// multibuffer = MultiBuffer::new(replica_id);
// let mut excerpts = state.excerpts.into_iter().peekable();
// while let Some(excerpt) = excerpts.peek() {
// let buffer_id = excerpt.buffer_id;
// let buffer_excerpts = iter::from_fn(|| {
// let excerpt = excerpts.peek()?;
// (excerpt.buffer_id == buffer_id)
// .then(|| excerpts.next().unwrap())
// });
// let buffer =
// buffers.iter().find(|b| b.read(cx).remote_id() == buffer_id);
// if let Some(buffer) = buffer {
// multibuffer.push_excerpts(
// buffer.clone(),
// buffer_excerpts.filter_map(deserialize_excerpt_range),
// cx,
// );
// }
// }
// };
update_editor_from_message( // if let Some(title) = &state.title {
editor.downgrade(), // multibuffer = multibuffer.with_title(title.clone())
project, // }
proto::update_view::Editor {
selections: state.selections,
pending_selection: state.pending_selection,
scroll_top_anchor: state.scroll_top_anchor,
scroll_x: state.scroll_x,
scroll_y: state.scroll_y,
..Default::default()
},
&mut cx,
)
.await?;
Ok(editor) // multibuffer
})) // });
}
// cx.add_view(|cx| {
// let mut editor =
// Editor::for_multibuffer(multibuffer, Some(project.clone()), cx);
// editor.remote_id = Some(remote_id);
// editor
// })
// })?
// };
// update_editor_from_message(
// editor.downgrade(),
// project,
// proto::update_view::Editor {
// selections: state.selections,
// pending_selection: state.pending_selection,
// scroll_top_anchor: state.scroll_top_anchor,
// scroll_x: state.scroll_x,
// scroll_y: state.scroll_y,
// ..Default::default()
// },
// &mut cx,
// )
// .await?;
// Ok(editor)
// }))
// }
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) { fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>) {
self.leader_peer_id = leader_peer_id; self.leader_peer_id = leader_peer_id;
@ -197,8 +199,8 @@ impl FollowableItem for Editor {
title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()), title: (!buffer.is_singleton()).then(|| buffer.title(cx).into()),
excerpts, excerpts,
scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)), scroll_top_anchor: Some(serialize_anchor(&scroll_anchor.anchor)),
scroll_x: scroll_anchor.offset.x(), scroll_x: scroll_anchor.offset.x,
scroll_y: scroll_anchor.offset.y(), scroll_y: scroll_anchor.offset.y,
selections: self selections: self
.selections .selections
.disjoint_anchors() .disjoint_anchors()
@ -254,8 +256,8 @@ impl FollowableItem for Editor {
Event::ScrollPositionChanged { .. } => { Event::ScrollPositionChanged { .. } => {
let scroll_anchor = self.scroll_manager.anchor(); let scroll_anchor = self.scroll_manager.anchor();
update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor)); update.scroll_top_anchor = Some(serialize_anchor(&scroll_anchor.anchor));
update.scroll_x = scroll_anchor.offset.x(); update.scroll_x = scroll_anchor.offset.x;
update.scroll_y = scroll_anchor.offset.y(); update.scroll_y = scroll_anchor.offset.y;
true true
} }
Event::SelectionsChanged { .. } => { Event::SelectionsChanged { .. } => {
@ -561,8 +563,8 @@ impl Item for Editor {
fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<SharedString> { 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()),
} }
} }
@ -598,7 +600,11 @@ impl Item for Editor {
self.buffer.read(cx).is_singleton() self.buffer.read(cx).is_singleton()
} }
fn clone_on_split(&self, _workspace_id: WorkspaceId, cx: &mut ViewContext<Self>) -> Option<Self> fn clone_on_split(
&self,
_workspace_id: WorkspaceId,
cx: &mut ViewContext<Self>,
) -> Option<View<Self>>
where where
Self: Sized, Self: Sized,
{ {
@ -611,7 +617,8 @@ impl Item for Editor {
fn deactivated(&mut self, cx: &mut ViewContext<Self>) { fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
let selection = self.selections.newest_anchor(); let selection = self.selections.newest_anchor();
self.push_to_nav_history(selection.head(), None, cx); todo!()
// self.push_to_nav_history(selection.head(), None, cx);
} }
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) { fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
@ -652,7 +659,7 @@ impl Item for Editor {
// we simulate saving by calling `Buffer::did_save`, so that language servers or // we simulate saving by calling `Buffer::did_save`, so that language servers or
// other downstream listeners of save events get notified. // other downstream listeners of save events get notified.
let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| { let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| {
buffer.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict()) buffer.read_with(&cx, |buffer, _| buffer.is_dirty || buffer.has_conflict())
}); });
project project
@ -686,9 +693,7 @@ impl Item for Editor {
.as_singleton() .as_singleton()
.expect("cannot call save_as on an excerpt list"); .expect("cannot call save_as on an excerpt list");
let file_extension = abs_path let file_extension = abs_path.extension().map(|a| a.to_string_lossy.to_string());
.extension()
.map(|a| a.to_string_lossy().to_string());
self.report_editor_event("save", file_extension, cx); self.report_editor_event("save", file_extension, cx);
project.update(cx, |project, cx| { project.update(cx, |project, cx| {

View file

@ -2,7 +2,7 @@ use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint}; use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::TextSystem; use gpui::TextSystem;
use language::Point; use language::Point;
use std::{ops::Range, sync::Arc}; use std::ops::Range;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum FindRange { pub enum FindRange {
@ -444,483 +444,483 @@ pub fn split_display_range_by_lines(
result result
} }
#[cfg(test)] // #[cfg(test)]
mod tests { // mod tests {
use super::*; // use super::*;
use crate::{ // use crate::{
display_map::Inlay, // display_map::Inlay,
test::{editor_test_context::EditorTestContext, marked_display_snapshot}, // test::{},
Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer, // Buffer, DisplayMap, ExcerptRange, InlayId, MultiBuffer,
}; // };
use project::Project; // use project::Project;
use settings::SettingsStore; // use settings::SettingsStore;
use util::post_inc; // use util::post_inc;
#[gpui::test] // #[gpui::test]
fn test_previous_word_start(cx: &mut gpui::AppContext) { // fn test_previous_word_start(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) { // fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
previous_word_start(&snapshot, display_points[1]), // previous_word_start(&snapshot, display_points[1]),
display_points[0] // display_points[0]
); // );
} // }
assert("\nˇ ˇlorem", cx); // assert("\nˇ ˇlorem", cx);
assert("ˇ\nˇ lorem", cx); // assert("ˇ\nˇ lorem", cx);
assert(" ˇloremˇ", cx); // assert(" ˇloremˇ", cx);
assert("ˇ ˇlorem", cx); // assert("ˇ ˇlorem", cx);
assert(" ˇlorˇem", cx); // assert(" ˇlorˇem", cx);
assert("\nlorem\nˇ ˇipsum", cx); // assert("\nlorem\nˇ ˇipsum", cx);
assert("\n\nˇ\nˇ", cx); // assert("\n\nˇ\nˇ", cx);
assert(" ˇlorem ˇipsum", cx); // assert(" ˇlorem ˇipsum", cx);
assert("loremˇ-ˇipsum", cx); // assert("loremˇ-ˇipsum", cx);
assert("loremˇ-#$@ˇipsum", cx); // assert("loremˇ-#$@ˇipsum", cx);
assert("ˇlorem_ˇipsum", cx); // assert("ˇlorem_ˇipsum", cx);
assert(" ˇdefγˇ", cx); // assert(" ˇdefγˇ", cx);
assert(" ˇbcΔˇ", cx); // assert(" ˇbcΔˇ", cx);
assert(" abˇ——ˇcd", cx); // assert(" abˇ——ˇcd", cx);
} // }
#[gpui::test] // #[gpui::test]
fn test_previous_subword_start(cx: &mut gpui::AppContext) { // fn test_previous_subword_start(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) { // fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
previous_subword_start(&snapshot, display_points[1]), // previous_subword_start(&snapshot, display_points[1]),
display_points[0] // display_points[0]
); // );
} // }
// Subword boundaries are respected // // Subword boundaries are respected
assert("lorem_ˇipˇsum", cx); // assert("lorem_ˇipˇsum", cx);
assert("lorem_ˇipsumˇ", cx); // assert("lorem_ˇipsumˇ", cx);
assert("ˇlorem_ˇipsum", cx); // assert("ˇlorem_ˇipsum", cx);
assert("lorem_ˇipsum_ˇdolor", cx); // assert("lorem_ˇipsum_ˇdolor", cx);
assert("loremˇIpˇsum", cx); // assert("loremˇIpˇsum", cx);
assert("loremˇIpsumˇ", cx); // assert("loremˇIpsumˇ", cx);
// Word boundaries are still respected // // Word boundaries are still respected
assert("\nˇ ˇlorem", cx); // assert("\nˇ ˇlorem", cx);
assert(" ˇloremˇ", cx); // assert(" ˇloremˇ", cx);
assert(" ˇlorˇem", cx); // assert(" ˇlorˇem", cx);
assert("\nlorem\nˇ ˇipsum", cx); // assert("\nlorem\nˇ ˇipsum", cx);
assert("\n\nˇ\nˇ", cx); // assert("\n\nˇ\nˇ", cx);
assert(" ˇlorem ˇipsum", cx); // assert(" ˇlorem ˇipsum", cx);
assert("loremˇ-ˇipsum", cx); // assert("loremˇ-ˇipsum", cx);
assert("loremˇ-#$@ˇipsum", cx); // assert("loremˇ-#$@ˇipsum", cx);
assert(" ˇdefγˇ", cx); // assert(" ˇdefγˇ", cx);
assert(" bcˇΔˇ", cx); // assert(" bcˇΔˇ", cx);
assert(" ˇbcδˇ", cx); // assert(" ˇbcδˇ", cx);
assert(" abˇ——ˇcd", cx); // assert(" abˇ——ˇcd", cx);
} // }
#[gpui::test] // #[gpui::test]
fn test_find_preceding_boundary(cx: &mut gpui::AppContext) { // fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert( // fn assert(
marked_text: &str, // marked_text: &str,
cx: &mut gpui::AppContext, // cx: &mut gpui::AppContext,
is_boundary: impl FnMut(char, char) -> bool, // is_boundary: impl FnMut(char, char) -> bool,
) { // ) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
find_preceding_boundary( // find_preceding_boundary(
&snapshot, // &snapshot,
display_points[1], // display_points[1],
FindRange::MultiLine, // FindRange::MultiLine,
is_boundary // is_boundary
), // ),
display_points[0] // display_points[0]
); // );
} // }
assert("abcˇdef\ngh\nijˇk", cx, |left, right| { // assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
left == 'c' && right == 'd' // left == 'c' && right == 'd'
}); // });
assert("abcdef\nˇgh\nijˇk", cx, |left, right| { // assert("abcdef\nˇgh\nijˇk", cx, |left, right| {
left == '\n' && right == 'g' // left == '\n' && right == 'g'
}); // });
let mut line_count = 0; // let mut line_count = 0;
assert("abcdef\nˇgh\nijˇk", cx, |left, _| { // assert("abcdef\nˇgh\nijˇk", cx, |left, _| {
if left == '\n' { // if left == '\n' {
line_count += 1; // line_count += 1;
line_count == 2 // line_count == 2
} else { // } else {
false // false
} // }
}); // });
} // }
#[gpui::test] // #[gpui::test]
fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) { // fn test_find_preceding_boundary_with_inlays(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
let input_text = "abcdefghijklmnopqrstuvwxys"; // let input_text = "abcdefghijklmnopqrstuvwxys";
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(input_text, cx); // let buffer = MultiBuffer::build_simple(input_text, cx);
let buffer_snapshot = buffer.read(cx).snapshot(cx); // let buffer_snapshot = buffer.read(cx).snapshot(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));
// add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary // // add all kinds of inlays between two word boundaries: we should be able to cross them all, when looking for another boundary
let mut id = 0; // let mut id = 0;
let inlays = (0..buffer_snapshot.len()) // let inlays = (0..buffer_snapshot.len())
.map(|offset| { // .map(|offset| {
[ // [
Inlay { // Inlay {
id: InlayId::Suggestion(post_inc(&mut id)), // id: InlayId::Suggestion(post_inc(&mut id)),
position: buffer_snapshot.anchor_at(offset, Bias::Left), // position: buffer_snapshot.anchor_at(offset, Bias::Left),
text: format!("test").into(), // text: format!("test").into(),
}, // },
Inlay { // Inlay {
id: InlayId::Suggestion(post_inc(&mut id)), // id: InlayId::Suggestion(post_inc(&mut id)),
position: buffer_snapshot.anchor_at(offset, Bias::Right), // position: buffer_snapshot.anchor_at(offset, Bias::Right),
text: format!("test").into(), // text: format!("test").into(),
}, // },
Inlay { // Inlay {
id: InlayId::Hint(post_inc(&mut id)), // id: InlayId::Hint(post_inc(&mut id)),
position: buffer_snapshot.anchor_at(offset, Bias::Left), // position: buffer_snapshot.anchor_at(offset, Bias::Left),
text: format!("test").into(), // text: format!("test").into(),
}, // },
Inlay { // Inlay {
id: InlayId::Hint(post_inc(&mut id)), // id: InlayId::Hint(post_inc(&mut id)),
position: buffer_snapshot.anchor_at(offset, Bias::Right), // position: buffer_snapshot.anchor_at(offset, Bias::Right),
text: format!("test").into(), // text: format!("test").into(),
}, // },
] // ]
}) // })
.flatten() // .flatten()
.collect(); // .collect();
let snapshot = display_map.update(cx, |map, cx| { // let snapshot = display_map.update(cx, |map, cx| {
map.splice_inlays(Vec::new(), inlays, cx); // map.splice_inlays(Vec::new(), inlays, cx);
map.snapshot(cx) // map.snapshot(cx)
}); // });
assert_eq!( // assert_eq!(
find_preceding_boundary( // find_preceding_boundary(
&snapshot, // &snapshot,
buffer_snapshot.len().to_display_point(&snapshot), // buffer_snapshot.len().to_display_point(&snapshot),
FindRange::MultiLine, // FindRange::MultiLine,
|left, _| left == 'e', // |left, _| left == 'e',
), // ),
snapshot // snapshot
.buffer_snapshot // .buffer_snapshot
.offset_to_point(5) // .offset_to_point(5)
.to_display_point(&snapshot), // .to_display_point(&snapshot),
"Should not stop at inlays when looking for boundaries" // "Should not stop at inlays when looking for boundaries"
); // );
} // }
#[gpui::test] // #[gpui::test]
fn test_next_word_end(cx: &mut gpui::AppContext) { // fn test_next_word_end(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) { // fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
next_word_end(&snapshot, display_points[0]), // next_word_end(&snapshot, display_points[0]),
display_points[1] // display_points[1]
); // );
} // }
assert("\nˇ loremˇ", cx); // assert("\nˇ loremˇ", cx);
assert(" ˇloremˇ", cx); // assert(" ˇloremˇ", cx);
assert(" lorˇemˇ", cx); // assert(" lorˇemˇ", cx);
assert(" loremˇ ˇ\nipsum\n", cx); // assert(" loremˇ ˇ\nipsum\n", cx);
assert("\nˇ\nˇ\n\n", cx); // assert("\nˇ\nˇ\n\n", cx);
assert("loremˇ ipsumˇ ", cx); // assert("loremˇ ipsumˇ ", cx);
assert("loremˇ-ˇipsum", cx); // assert("loremˇ-ˇipsum", cx);
assert("loremˇ#$@-ˇipsum", cx); // assert("loremˇ#$@-ˇipsum", cx);
assert("loremˇ_ipsumˇ", cx); // assert("loremˇ_ipsumˇ", cx);
assert(" ˇbcΔˇ", cx); // assert(" ˇbcΔˇ", cx);
assert(" abˇ——ˇcd", cx); // assert(" abˇ——ˇcd", cx);
} // }
#[gpui::test] // #[gpui::test]
fn test_next_subword_end(cx: &mut gpui::AppContext) { // fn test_next_subword_end(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) { // fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
next_subword_end(&snapshot, display_points[0]), // next_subword_end(&snapshot, display_points[0]),
display_points[1] // display_points[1]
); // );
} // }
// Subword boundaries are respected // // Subword boundaries are respected
assert("loˇremˇ_ipsum", cx); // assert("loˇremˇ_ipsum", cx);
assert("ˇloremˇ_ipsum", cx); // assert("ˇloremˇ_ipsum", cx);
assert("loremˇ_ipsumˇ", cx); // assert("loremˇ_ipsumˇ", cx);
assert("loremˇ_ipsumˇ_dolor", cx); // assert("loremˇ_ipsumˇ_dolor", cx);
assert("loˇremˇIpsum", cx); // assert("loˇremˇIpsum", cx);
assert("loremˇIpsumˇDolor", cx); // assert("loremˇIpsumˇDolor", cx);
// Word boundaries are still respected // // Word boundaries are still respected
assert("\nˇ loremˇ", cx); // assert("\nˇ loremˇ", cx);
assert(" ˇloremˇ", cx); // assert(" ˇloremˇ", cx);
assert(" lorˇemˇ", cx); // assert(" lorˇemˇ", cx);
assert(" loremˇ ˇ\nipsum\n", cx); // assert(" loremˇ ˇ\nipsum\n", cx);
assert("\nˇ\nˇ\n\n", cx); // assert("\nˇ\nˇ\n\n", cx);
assert("loremˇ ipsumˇ ", cx); // assert("loremˇ ipsumˇ ", cx);
assert("loremˇ-ˇipsum", cx); // assert("loremˇ-ˇipsum", cx);
assert("loremˇ#$@-ˇipsum", cx); // assert("loremˇ#$@-ˇipsum", cx);
assert("loremˇ_ipsumˇ", cx); // assert("loremˇ_ipsumˇ", cx);
assert(" ˇbcˇΔ", cx); // assert(" ˇbcˇΔ", cx);
assert(" abˇ——ˇcd", cx); // assert(" abˇ——ˇcd", cx);
} // }
#[gpui::test] // #[gpui::test]
fn test_find_boundary(cx: &mut gpui::AppContext) { // fn test_find_boundary(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert( // fn assert(
marked_text: &str, // marked_text: &str,
cx: &mut gpui::AppContext, // cx: &mut gpui::AppContext,
is_boundary: impl FnMut(char, char) -> bool, // is_boundary: impl FnMut(char, char) -> bool,
) { // ) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
find_boundary( // find_boundary(
&snapshot, // &snapshot,
display_points[0], // display_points[0],
FindRange::MultiLine, // FindRange::MultiLine,
is_boundary // is_boundary
), // ),
display_points[1] // display_points[1]
); // );
} // }
assert("abcˇdef\ngh\nijˇk", cx, |left, right| { // assert("abcˇdef\ngh\nijˇk", cx, |left, right| {
left == 'j' && right == 'k' // left == 'j' && right == 'k'
}); // });
assert("abˇcdef\ngh\nˇijk", cx, |left, right| { // assert("abˇcdef\ngh\nˇijk", cx, |left, right| {
left == '\n' && right == 'i' // left == '\n' && right == 'i'
}); // });
let mut line_count = 0; // let mut line_count = 0;
assert("abcˇdef\ngh\nˇijk", cx, |left, _| { // assert("abcˇdef\ngh\nˇijk", cx, |left, _| {
if left == '\n' { // if left == '\n' {
line_count += 1; // line_count += 1;
line_count == 2 // line_count == 2
} else { // } else {
false // false
} // }
}); // });
} // }
#[gpui::test] // #[gpui::test]
fn test_surrounding_word(cx: &mut gpui::AppContext) { // fn test_surrounding_word(cx: &mut gpui::AppContext) {
init_test(cx); // init_test(cx);
fn assert(marked_text: &str, cx: &mut gpui::AppContext) { // fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx); // let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
assert_eq!( // assert_eq!(
surrounding_word(&snapshot, display_points[1]), // surrounding_word(&snapshot, display_points[1]),
display_points[0]..display_points[2], // display_points[0]..display_points[2],
"{}", // "{}",
marked_text.to_string() // marked_text.to_string()
); // );
} // }
assert("ˇˇloremˇ ipsum", cx); // assert("ˇˇloremˇ ipsum", cx);
assert("ˇloˇremˇ ipsum", cx); // assert("ˇloˇremˇ ipsum", cx);
assert("ˇloremˇˇ ipsum", cx); // assert("ˇloremˇˇ ipsum", cx);
assert("loremˇ ˇ ˇipsum", cx); // assert("loremˇ ˇ ˇipsum", cx);
assert("lorem\nˇˇˇ\nipsum", cx); // assert("lorem\nˇˇˇ\nipsum", cx);
assert("lorem\nˇˇipsumˇ", cx); // assert("lorem\nˇˇipsumˇ", cx);
assert("loremˇ,ˇˇ ipsum", cx); // assert("loremˇ,ˇˇ ipsum", cx);
assert("ˇloremˇˇ, ipsum", cx); // assert("ˇloremˇˇ, ipsum", cx);
} // }
#[gpui::test] // #[gpui::test]
async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) { // async fn test_move_up_and_down_with_excerpts(cx: &mut gpui::TestAppContext) {
cx.update(|cx| { // cx.update(|cx| {
init_test(cx); // init_test(cx);
}); // });
let mut cx = EditorTestContext::new(cx).await; // let mut cx = EditorTestContext::new(cx).await;
let editor = cx.editor.clone(); // let editor = cx.editor.clone();
let window = cx.window.clone(); // let window = cx.window.clone();
cx.update_window(window, |cx| { // cx.update_window(window, |cx| {
let text_layout_details = // let text_layout_details =
editor.read_with(cx, |editor, cx| editor.text_layout_details(cx)); // editor.read_with(cx, |editor, cx| editor.text_layout_details(cx));
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 buffer = // let buffer =
cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abc\ndefg\nhijkl\nmn")); // cx.add_model(|cx| Buffer::new(0, cx.model_id() as u64, "abc\ndefg\nhijkl\nmn"));
let multibuffer = cx.add_model(|cx| { // let multibuffer = cx.add_model(|cx| {
let mut multibuffer = MultiBuffer::new(0); // let mut multibuffer = MultiBuffer::new(0);
multibuffer.push_excerpts( // multibuffer.push_excerpts(
buffer.clone(), // buffer.clone(),
[ // [
ExcerptRange { // ExcerptRange {
context: Point::new(0, 0)..Point::new(1, 4), // context: Point::new(0, 0)..Point::new(1, 4),
primary: None, // primary: None,
}, // },
ExcerptRange { // ExcerptRange {
context: Point::new(2, 0)..Point::new(3, 2), // context: Point::new(2, 0)..Point::new(3, 2),
primary: None, // primary: None,
}, // },
], // ],
cx, // cx,
); // );
multibuffer // multibuffer
}); // });
let display_map = // let display_map =
cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx)); // cx.add_model(|cx| DisplayMap::new(multibuffer, font_id, 14.0, None, 2, 2, cx));
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx)); // let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn"); // assert_eq!(snapshot.text(), "\n\nabc\ndefg\n\n\nhijkl\nmn");
let col_2_x = snapshot.x_for_point(DisplayPoint::new(2, 2), &text_layout_details); // let col_2_x = snapshot.x_for_point(DisplayPoint::new(2, 2), &text_layout_details);
// Can't move up into the first excerpt's header // // Can't move up into the first excerpt's header
assert_eq!( // assert_eq!(
up( // up(
&snapshot, // &snapshot,
DisplayPoint::new(2, 2), // DisplayPoint::new(2, 2),
SelectionGoal::HorizontalPosition(col_2_x), // SelectionGoal::HorizontalPosition(col_2_x),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(2, 0), // DisplayPoint::new(2, 0),
SelectionGoal::HorizontalPosition(0.0) // SelectionGoal::HorizontalPosition(0.0)
), // ),
); // );
assert_eq!( // assert_eq!(
up( // up(
&snapshot, // &snapshot,
DisplayPoint::new(2, 0), // DisplayPoint::new(2, 0),
SelectionGoal::None, // SelectionGoal::None,
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(2, 0), // DisplayPoint::new(2, 0),
SelectionGoal::HorizontalPosition(0.0) // SelectionGoal::HorizontalPosition(0.0)
), // ),
); // );
let col_4_x = snapshot.x_for_point(DisplayPoint::new(3, 4), &text_layout_details); // let col_4_x = snapshot.x_for_point(DisplayPoint::new(3, 4), &text_layout_details);
// Move up and down within first excerpt // // Move up and down within first excerpt
assert_eq!( // assert_eq!(
up( // up(
&snapshot, // &snapshot,
DisplayPoint::new(3, 4), // DisplayPoint::new(3, 4),
SelectionGoal::HorizontalPosition(col_4_x), // SelectionGoal::HorizontalPosition(col_4_x),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(2, 3), // DisplayPoint::new(2, 3),
SelectionGoal::HorizontalPosition(col_4_x) // SelectionGoal::HorizontalPosition(col_4_x)
), // ),
); // );
assert_eq!( // assert_eq!(
down( // down(
&snapshot, // &snapshot,
DisplayPoint::new(2, 3), // DisplayPoint::new(2, 3),
SelectionGoal::HorizontalPosition(col_4_x), // SelectionGoal::HorizontalPosition(col_4_x),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(3, 4), // DisplayPoint::new(3, 4),
SelectionGoal::HorizontalPosition(col_4_x) // SelectionGoal::HorizontalPosition(col_4_x)
), // ),
); // );
let col_5_x = snapshot.x_for_point(DisplayPoint::new(6, 5), &text_layout_details); // let col_5_x = snapshot.x_for_point(DisplayPoint::new(6, 5), &text_layout_details);
// Move up and down across second excerpt's header // // Move up and down across second excerpt's header
assert_eq!( // assert_eq!(
up( // up(
&snapshot, // &snapshot,
DisplayPoint::new(6, 5), // DisplayPoint::new(6, 5),
SelectionGoal::HorizontalPosition(col_5_x), // SelectionGoal::HorizontalPosition(col_5_x),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(3, 4), // DisplayPoint::new(3, 4),
SelectionGoal::HorizontalPosition(col_5_x) // SelectionGoal::HorizontalPosition(col_5_x)
), // ),
); // );
assert_eq!( // assert_eq!(
down( // down(
&snapshot, // &snapshot,
DisplayPoint::new(3, 4), // DisplayPoint::new(3, 4),
SelectionGoal::HorizontalPosition(col_5_x), // SelectionGoal::HorizontalPosition(col_5_x),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(6, 5), // DisplayPoint::new(6, 5),
SelectionGoal::HorizontalPosition(col_5_x) // SelectionGoal::HorizontalPosition(col_5_x)
), // ),
); // );
let max_point_x = snapshot.x_for_point(DisplayPoint::new(7, 2), &text_layout_details); // let max_point_x = snapshot.x_for_point(DisplayPoint::new(7, 2), &text_layout_details);
// Can't move down off the end // // Can't move down off the end
assert_eq!( // assert_eq!(
down( // down(
&snapshot, // &snapshot,
DisplayPoint::new(7, 0), // DisplayPoint::new(7, 0),
SelectionGoal::HorizontalPosition(0.0), // SelectionGoal::HorizontalPosition(0.0),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(7, 2), // DisplayPoint::new(7, 2),
SelectionGoal::HorizontalPosition(max_point_x) // SelectionGoal::HorizontalPosition(max_point_x)
), // ),
); // );
assert_eq!( // assert_eq!(
down( // down(
&snapshot, // &snapshot,
DisplayPoint::new(7, 2), // DisplayPoint::new(7, 2),
SelectionGoal::HorizontalPosition(max_point_x), // SelectionGoal::HorizontalPosition(max_point_x),
false, // false,
&text_layout_details // &text_layout_details
), // ),
( // (
DisplayPoint::new(7, 2), // DisplayPoint::new(7, 2),
SelectionGoal::HorizontalPosition(max_point_x) // SelectionGoal::HorizontalPosition(max_point_x)
), // ),
); // );
}); // });
} // }
fn init_test(cx: &mut gpui::AppContext) { // fn init_test(cx: &mut gpui::AppContext) {
cx.set_global(SettingsStore::test(cx)); // cx.set_global(SettingsStore::test(cx));
theme::init(cx); // theme::init(cx);
language::init(cx); // language::init(cx);
crate::init(cx); // crate::init(cx);
Project::init_settings(cx); // Project::init_settings(cx);
} // }
} // }

View file

@ -39,7 +39,7 @@ pub struct ScrollAnchor {
impl ScrollAnchor { impl ScrollAnchor {
fn new() -> Self { fn new() -> Self {
Self { Self {
offset: Point::zero(), offset: gpui::Point::zero(),
anchor: Anchor::min(), anchor: Anchor::min(),
} }
} }
@ -48,7 +48,7 @@ impl ScrollAnchor {
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;
scroll_position.set_y(scroll_top + scroll_position.y()); scroll_position.set_y(scroll_top + scroll_position.y);
} else { } else {
scroll_position.set_y(0.); scroll_position.set_y(0.);
} }
@ -82,7 +82,7 @@ impl OngoingScroll {
pub fn filter(&self, delta: &mut gpui::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: Pixels = px(6.);
let mut axis = self.axis; let mut axis = self.axis;
let x = delta.x.abs(); let x = delta.x.abs();
@ -116,10 +116,10 @@ impl OngoingScroll {
match axis { match axis {
Some(Axis::Vertical) => { Some(Axis::Vertical) => {
*delta = point(pk(0.), delta.y()); *delta = point(px(0.), delta.y);
} }
Some(Axis::Horizontal) => { Some(Axis::Horizontal) => {
*delta = point(delta.x(), px(0.)); *delta = point(delta.x, px(0.));
} }
None => {} None => {}
} }
@ -177,14 +177,14 @@ impl ScrollManager {
fn set_scroll_position( fn set_scroll_position(
&mut self, &mut self,
scroll_position: gpui::Point<Pixels>, scroll_position: gpui::Point<f32>,
map: &DisplaySnapshot, map: &DisplaySnapshot,
local: bool, local: bool,
autoscroll: bool, autoscroll: bool,
workspace_id: Option<i64>, workspace_id: Option<i64>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let (new_anchor, top_row) = if scroll_position.y() <= 0. { let (new_anchor, top_row) = if scroll_position.y <= 0. {
( (
ScrollAnchor { ScrollAnchor {
anchor: Anchor::min(), anchor: Anchor::min(),
@ -194,7 +194,7 @@ impl ScrollManager {
) )
} else { } else {
let scroll_top_buffer_point = let scroll_top_buffer_point =
DisplayPoint::new(scroll_position.y() as u32, 0).to_point(&map); DisplayPoint::new(scroll_position.y as u32, 0).to_point(&map);
let top_anchor = map let top_anchor = map
.buffer_snapshot .buffer_snapshot
.anchor_at(scroll_top_buffer_point, Bias::Right); .anchor_at(scroll_top_buffer_point, Bias::Right);
@ -203,8 +203,8 @@ impl ScrollManager {
ScrollAnchor { ScrollAnchor {
anchor: top_anchor, anchor: top_anchor,
offset: point( offset: point(
scroll_position.x(), scroll_position.x,
scroll_position.y() - top_anchor.to_display_point(&map).row() as f32, scroll_position.y - top_anchor.to_display_point(&map).row() as f32,
), ),
}, },
scroll_top_buffer_point.row, scroll_top_buffer_point.row,
@ -236,8 +236,8 @@ impl ScrollManager {
item_id, item_id,
workspace_id, workspace_id,
top_row, top_row,
anchor.offset.x(), anchor.offset.x,
anchor.offset.y(), anchor.offset.y,
) )
.await .await
.log_err() .log_err()
@ -277,8 +277,8 @@ impl ScrollManager {
} }
pub fn clamp_scroll_left(&mut self, max: f32) -> bool { pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
if max < self.anchor.offset.x() { if max < self.anchor.offset.x {
self.anchor.offset.set_x(max); self.anchor.offset.x = max;
true true
} else { } else {
false false

View file

@ -60,7 +60,7 @@ impl Editor {
} else { } else {
display_map.max_point().row() as f32 display_map.max_point().row() as f32
}; };
if scroll_position.y() > max_scroll_top { if scroll_position.y > max_scroll_top {
scroll_position.set_y(max_scroll_top); scroll_position.set_y(max_scroll_top);
self.set_scroll_position(scroll_position, cx); self.set_scroll_position(scroll_position, cx);
} }
@ -136,7 +136,7 @@ impl Editor {
let margin = margin.min(self.scroll_manager.vertical_scroll_margin); let margin = margin.min(self.scroll_manager.vertical_scroll_margin);
let target_top = (target_top - margin).max(0.0); let target_top = (target_top - margin).max(0.0);
let target_bottom = target_bottom + margin; let target_bottom = target_bottom + margin;
let start_row = scroll_position.y(); let start_row = scroll_position.y;
let end_row = start_row + visible_lines; let end_row = start_row + visible_lines;
let needs_scroll_up = target_top < start_row; let needs_scroll_up = target_top < start_row;
@ -222,20 +222,15 @@ impl Editor {
return false; return false;
} }
let scroll_left = self.scroll_manager.anchor.offset.x() * max_glyph_width; let scroll_left = self.scroll_manager.anchor.offset.x * max_glyph_width;
let scroll_right = scroll_left + viewport_width; let scroll_right = scroll_left + viewport_width;
if target_left < scroll_left { if target_left < scroll_left {
self.scroll_manager self.scroll_manager.anchor.offset.x = (target_left / max_glyph_width);
.anchor
.offset
.set_x(target_left / max_glyph_width);
true true
} else if target_right > scroll_right { } else if target_right > scroll_right {
self.scroll_manager self.scroll_manager.anchor.offset.x =
.anchor ((target_right - viewport_width) / max_glyph_width);
.offset
.set_x((target_right - viewport_width) / max_glyph_width);
true true
} else { } else {
false false

View file

@ -1,297 +1,297 @@
use std::{ // use std::{
borrow::Cow, // borrow::Cow,
ops::{Deref, DerefMut, Range}, // ops::{Deref, DerefMut, Range},
sync::Arc, // sync::Arc,
}; // };
use anyhow::Result; // use anyhow::Result;
use crate::{Editor, ToPoint}; // use crate::{Editor, ToPoint};
use collections::HashSet; // use collections::HashSet;
use futures::Future; // use futures::Future;
use gpui::{json, View, ViewContext}; // use gpui::{json, View, ViewContext};
use indoc::indoc; // use indoc::indoc;
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries}; // use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
use lsp::{notification, request}; // use lsp::{notification, request};
use multi_buffer::ToPointUtf16; // use multi_buffer::ToPointUtf16;
use project::Project; // use project::Project;
use smol::stream::StreamExt; // use smol::stream::StreamExt;
use workspace::{AppState, Workspace, WorkspaceHandle}; // use workspace::{AppState, Workspace, WorkspaceHandle};
use super::editor_test_context::EditorTestContext; // use super::editor_test_context::EditorTestContext;
pub struct EditorLspTestContext<'a> { // pub struct EditorLspTestContext<'a> {
pub cx: EditorTestContext<'a>, // pub cx: EditorTestContext<'a>,
pub lsp: lsp::FakeLanguageServer, // pub lsp: lsp::FakeLanguageServer,
pub workspace: ViewHandle<Workspace>, // pub workspace: View<Workspace>,
pub buffer_lsp_url: lsp::Url, // pub buffer_lsp_url: lsp::Url,
} // }
impl<'a> EditorLspTestContext<'a> { // impl<'a> EditorLspTestContext<'a> {
pub async fn new( // pub async fn new(
mut language: Language, // mut language: Language,
capabilities: lsp::ServerCapabilities, // capabilities: lsp::ServerCapabilities,
cx: &'a mut gpui::TestAppContext, // cx: &'a mut gpui::TestAppContext,
) -> EditorLspTestContext<'a> { // ) -> EditorLspTestContext<'a> {
use json::json; // use json::json;
let app_state = cx.update(AppState::test); // let app_state = cx.update(AppState::test);
cx.update(|cx| { // cx.update(|cx| {
language::init(cx); // language::init(cx);
crate::init(cx); // crate::init(cx);
workspace::init(app_state.clone(), cx); // workspace::init(app_state.clone(), cx);
Project::init_settings(cx); // Project::init_settings(cx);
}); // });
let file_name = format!( // let file_name = format!(
"file.{}", // "file.{}",
language // language
.path_suffixes() // .path_suffixes()
.first() // .first()
.expect("language must have a path suffix for EditorLspTestContext") // .expect("language must have a path suffix for EditorLspTestContext")
); // );
let mut fake_servers = language // let mut fake_servers = language
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter { // .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
capabilities, // capabilities,
..Default::default() // ..Default::default()
})) // }))
.await; // .await;
let project = Project::test(app_state.fs.clone(), [], cx).await; // let project = Project::test(app_state.fs.clone(), [], cx).await;
project.update(cx, |project, _| project.languages().add(Arc::new(language))); // project.update(cx, |project, _| project.languages().add(Arc::new(language)));
app_state // app_state
.fs // .fs
.as_fake() // .as_fake()
.insert_tree("/root", json!({ "dir": { file_name.clone(): "" }})) // .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
.await; // .await;
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); // let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
let workspace = window.root(cx); // let workspace = window.root(cx);
project // project
.update(cx, |project, cx| { // .update(cx, |project, cx| {
project.find_or_create_local_worktree("/root", true, cx) // project.find_or_create_local_worktree("/root", true, cx)
}) // })
.await // .await
.unwrap(); // .unwrap();
cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx)) // cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
.await; // .await;
let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone()); // let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
let item = workspace // let item = workspace
.update(cx, |workspace, cx| { // .update(cx, |workspace, cx| {
workspace.open_path(file, None, true, cx) // workspace.open_path(file, None, true, cx)
}) // })
.await // .await
.expect("Could not open test file"); // .expect("Could not open test file");
let editor = cx.update(|cx| { // let editor = cx.update(|cx| {
item.act_as::<Editor>(cx) // item.act_as::<Editor>(cx)
.expect("Opened test file wasn't an editor") // .expect("Opened test file wasn't an editor")
}); // });
editor.update(cx, |_, cx| cx.focus_self()); // editor.update(cx, |_, cx| cx.focus_self());
let lsp = fake_servers.next().await.unwrap(); // let lsp = fake_servers.next().await.unwrap();
Self { // Self {
cx: EditorTestContext { // cx: EditorTestContext {
cx, // cx,
window: window.into(), // window: window.into(),
editor, // editor,
}, // },
lsp, // lsp,
workspace, // workspace,
buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(), // buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(),
} // }
} // }
pub async fn new_rust( // pub async fn new_rust(
capabilities: lsp::ServerCapabilities, // capabilities: lsp::ServerCapabilities,
cx: &'a mut gpui::TestAppContext, // cx: &'a mut gpui::TestAppContext,
) -> EditorLspTestContext<'a> { // ) -> EditorLspTestContext<'a> {
let language = Language::new( // let language = Language::new(
LanguageConfig { // LanguageConfig {
name: "Rust".into(), // name: "Rust".into(),
path_suffixes: vec!["rs".to_string()], // path_suffixes: vec!["rs".to_string()],
..Default::default() // ..Default::default()
}, // },
Some(tree_sitter_rust::language()), // Some(tree_sitter_rust::language()),
) // )
.with_queries(LanguageQueries { // .with_queries(LanguageQueries {
indents: Some(Cow::from(indoc! {r#" // indents: Some(Cow::from(indoc! {r#"
[ // [
((where_clause) _ @end) // ((where_clause) _ @end)
(field_expression) // (field_expression)
(call_expression) // (call_expression)
(assignment_expression) // (assignment_expression)
(let_declaration) // (let_declaration)
(let_chain) // (let_chain)
(await_expression) // (await_expression)
] @indent // ] @indent
(_ "[" "]" @end) @indent // (_ "[" "]" @end) @indent
(_ "<" ">" @end) @indent // (_ "<" ">" @end) @indent
(_ "{" "}" @end) @indent // (_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent"#})), // (_ "(" ")" @end) @indent"#})),
brackets: Some(Cow::from(indoc! {r#" // brackets: Some(Cow::from(indoc! {r#"
("(" @open ")" @close) // ("(" @open ")" @close)
("[" @open "]" @close) // ("[" @open "]" @close)
("{" @open "}" @close) // ("{" @open "}" @close)
("<" @open ">" @close) // ("<" @open ">" @close)
("\"" @open "\"" @close) // ("\"" @open "\"" @close)
(closure_parameters "|" @open "|" @close)"#})), // (closure_parameters "|" @open "|" @close)"#})),
..Default::default() // ..Default::default()
}) // })
.expect("Could not parse queries"); // .expect("Could not parse queries");
Self::new(language, capabilities, cx).await // Self::new(language, capabilities, cx).await
} // }
pub async fn new_typescript( // pub async fn new_typescript(
capabilities: lsp::ServerCapabilities, // capabilities: lsp::ServerCapabilities,
cx: &'a mut gpui::TestAppContext, // cx: &'a mut gpui::TestAppContext,
) -> EditorLspTestContext<'a> { // ) -> EditorLspTestContext<'a> {
let mut word_characters: HashSet<char> = Default::default(); // let mut word_characters: HashSet<char> = Default::default();
word_characters.insert('$'); // word_characters.insert('$');
word_characters.insert('#'); // word_characters.insert('#');
let language = Language::new( // let language = Language::new(
LanguageConfig { // LanguageConfig {
name: "Typescript".into(), // name: "Typescript".into(),
path_suffixes: vec!["ts".to_string()], // path_suffixes: vec!["ts".to_string()],
brackets: language::BracketPairConfig { // brackets: language::BracketPairConfig {
pairs: vec![language::BracketPair { // pairs: vec![language::BracketPair {
start: "{".to_string(), // start: "{".to_string(),
end: "}".to_string(), // end: "}".to_string(),
close: true, // close: true,
newline: true, // newline: true,
}], // }],
disabled_scopes_by_bracket_ix: Default::default(), // disabled_scopes_by_bracket_ix: Default::default(),
}, // },
word_characters, // word_characters,
..Default::default() // ..Default::default()
}, // },
Some(tree_sitter_typescript::language_typescript()), // Some(tree_sitter_typescript::language_typescript()),
) // )
.with_queries(LanguageQueries { // .with_queries(LanguageQueries {
brackets: Some(Cow::from(indoc! {r#" // brackets: Some(Cow::from(indoc! {r#"
("(" @open ")" @close) // ("(" @open ")" @close)
("[" @open "]" @close) // ("[" @open "]" @close)
("{" @open "}" @close) // ("{" @open "}" @close)
("<" @open ">" @close) // ("<" @open ">" @close)
("\"" @open "\"" @close)"#})), // ("\"" @open "\"" @close)"#})),
indents: Some(Cow::from(indoc! {r#" // indents: Some(Cow::from(indoc! {r#"
[ // [
(call_expression) // (call_expression)
(assignment_expression) // (assignment_expression)
(member_expression) // (member_expression)
(lexical_declaration) // (lexical_declaration)
(variable_declaration) // (variable_declaration)
(assignment_expression) // (assignment_expression)
(if_statement) // (if_statement)
(for_statement) // (for_statement)
] @indent // ] @indent
(_ "[" "]" @end) @indent // (_ "[" "]" @end) @indent
(_ "<" ">" @end) @indent // (_ "<" ">" @end) @indent
(_ "{" "}" @end) @indent // (_ "{" "}" @end) @indent
(_ "(" ")" @end) @indent // (_ "(" ")" @end) @indent
"#})), // "#})),
..Default::default() // ..Default::default()
}) // })
.expect("Could not parse queries"); // .expect("Could not parse queries");
Self::new(language, capabilities, cx).await // Self::new(language, capabilities, cx).await
} // }
// Constructs lsp range using a marked string with '[', ']' range delimiters // // Constructs lsp range using a marked string with '[', ']' range delimiters
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range { // pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
let ranges = self.ranges(marked_text); // let ranges = self.ranges(marked_text);
self.to_lsp_range(ranges[0].clone()) // self.to_lsp_range(ranges[0].clone())
} // }
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range { // pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); // let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
let start_point = range.start.to_point(&snapshot.buffer_snapshot); // let start_point = range.start.to_point(&snapshot.buffer_snapshot);
let end_point = range.end.to_point(&snapshot.buffer_snapshot); // let end_point = range.end.to_point(&snapshot.buffer_snapshot);
self.editor(|editor, cx| { // self.editor(|editor, cx| {
let buffer = editor.buffer().read(cx); // let buffer = editor.buffer().read(cx);
let start = point_to_lsp( // let start = point_to_lsp(
buffer // buffer
.point_to_buffer_offset(start_point, cx) // .point_to_buffer_offset(start_point, cx)
.unwrap() // .unwrap()
.1 // .1
.to_point_utf16(&buffer.read(cx)), // .to_point_utf16(&buffer.read(cx)),
); // );
let end = point_to_lsp( // let end = point_to_lsp(
buffer // buffer
.point_to_buffer_offset(end_point, cx) // .point_to_buffer_offset(end_point, cx)
.unwrap() // .unwrap()
.1 // .1
.to_point_utf16(&buffer.read(cx)), // .to_point_utf16(&buffer.read(cx)),
); // );
lsp::Range { start, end } // lsp::Range { start, end }
}) // })
} // }
pub fn to_lsp(&mut self, offset: usize) -> lsp::Position { // pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx)); // let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
let point = offset.to_point(&snapshot.buffer_snapshot); // let point = offset.to_point(&snapshot.buffer_snapshot);
self.editor(|editor, cx| { // self.editor(|editor, cx| {
let buffer = editor.buffer().read(cx); // let buffer = editor.buffer().read(cx);
point_to_lsp( // point_to_lsp(
buffer // buffer
.point_to_buffer_offset(point, cx) // .point_to_buffer_offset(point, cx)
.unwrap() // .unwrap()
.1 // .1
.to_point_utf16(&buffer.read(cx)), // .to_point_utf16(&buffer.read(cx)),
) // )
}) // })
} // }
pub fn update_workspace<F, T>(&mut self, update: F) -> T // pub fn update_workspace<F, T>(&mut self, update: F) -> T
where // where
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T, // F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
{ // {
self.workspace.update(self.cx.cx, update) // self.workspace.update(self.cx.cx, update)
} // }
pub fn handle_request<T, F, Fut>( // pub fn handle_request<T, F, Fut>(
&self, // &self,
mut handler: F, // mut handler: F,
) -> futures::channel::mpsc::UnboundedReceiver<()> // ) -> futures::channel::mpsc::UnboundedReceiver<()>
where // where
T: 'static + request::Request, // T: 'static + request::Request,
T::Params: 'static + Send, // T::Params: 'static + Send,
F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut, // F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
Fut: 'static + Send + Future<Output = Result<T::Result>>, // Fut: 'static + Send + Future<Output = Result<T::Result>>,
{ // {
let url = self.buffer_lsp_url.clone(); // let url = self.buffer_lsp_url.clone();
self.lsp.handle_request::<T, _, _>(move |params, cx| { // self.lsp.handle_request::<T, _, _>(move |params, cx| {
let url = url.clone(); // let url = url.clone();
handler(url, params, cx) // handler(url, params, cx)
}) // })
} // }
pub fn notify<T: notification::Notification>(&self, params: T::Params) { // pub fn notify<T: notification::Notification>(&self, params: T::Params) {
self.lsp.notify::<T>(params); // self.lsp.notify::<T>(params);
} // }
} // }
impl<'a> Deref for EditorLspTestContext<'a> { // impl<'a> Deref for EditorLspTestContext<'a> {
type Target = EditorTestContext<'a>; // type Target = EditorTestContext<'a>;
fn deref(&self) -> &Self::Target { // fn deref(&self) -> &Self::Target {
&self.cx // &self.cx
} // }
} // }
impl<'a> DerefMut for EditorLspTestContext<'a> { // impl<'a> DerefMut for EditorLspTestContext<'a> {
fn deref_mut(&mut self) -> &mut Self::Target { // fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cx // &mut self.cx
} // }
} // }

View file

@ -315,17 +315,17 @@ use util::{
// } // }
// } // }
// } // }
//
// impl<'a> Deref for EditorTestContext<'a> {
// type Target = gpui::TestAppContext;
impl<'a> Deref for EditorTestContext<'a> { // fn deref(&self) -> &Self::Target {
type Target = gpui::TestAppContext; // self.cx
// }
// }
fn deref(&self) -> &Self::Target { // impl<'a> DerefMut for EditorTestContext<'a> {
self.cx // fn deref_mut(&mut self) -> &mut Self::Target {
} // &mut self.cx
} // }
// }
impl<'a> DerefMut for EditorTestContext<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cx
}
}

View file

@ -755,6 +755,10 @@ impl Pixels {
pub fn pow(&self, exponent: f32) -> Self { pub fn pow(&self, exponent: f32) -> Self {
Self(self.0.powf(exponent)) Self(self.0.powf(exponent))
} }
pub fn abs(&self) -> Self {
Self(self.0.abs())
}
} }
impl Mul<Pixels> for Pixels { impl Mul<Pixels> for Pixels {

View file

@ -90,7 +90,7 @@ pub struct BreadcrumbText {
pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>, pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
} }
pub trait Item: Render + EventEmitter + Send { pub trait Item: Render + EventEmitter {
fn deactivated(&mut self, _: &mut ViewContext<Self>) {} fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {} fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool { fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {