This commit is contained in:
Nathan Sobo
2021-07-23 12:25:09 -06:00
parent 11285f3762
commit f81f043058
8 changed files with 513 additions and 568 deletions
+7 -1
View File
@@ -23,7 +23,7 @@ use std::{
fmt::{self, Debug},
hash::{Hash, Hasher},
marker::PhantomData,
ops::Deref,
ops::{Deref, DerefMut},
path::{Path, PathBuf},
rc::{self, Rc},
sync::{Arc, Weak},
@@ -2110,6 +2110,12 @@ impl<M> Deref for ViewContext<'_, M> {
}
}
impl<M> DerefMut for ViewContext<'_, M> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.app
}
}
impl<M> AsMut<MutableAppContext> for ViewContext<'_, M> {
fn as_mut(&mut self) -> &mut MutableAppContext {
self.app
+382 -463
View File
File diff suppressed because it is too large Load Diff
+32 -33
View File
@@ -5,7 +5,7 @@ mod wrap_map;
use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint};
use fold_map::FoldMap;
use gpui::{AppContext, ModelHandle};
use gpui::{ModelHandle, MutableAppContext};
use postage::prelude::Stream;
use std::ops::Range;
use tab_map::TabMap;
@@ -24,7 +24,7 @@ impl DisplayMap {
buffer: ModelHandle<Buffer>,
settings: Settings,
wrap_width: Option<f32>,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> Self {
let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx);
let (tab_map, snapshot) = TabMap::new(snapshot, settings.tab_size);
@@ -37,7 +37,7 @@ impl DisplayMap {
}
}
pub fn snapshot(&self, cx: &AppContext) -> DisplayMapSnapshot {
pub fn snapshot(&self, cx: &mut MutableAppContext) -> DisplayMapSnapshot {
let (folds_snapshot, edits) = self.fold_map.read(cx);
let (tabs_snapshot, edits) = self.tab_map.sync(folds_snapshot.clone(), edits);
let wraps_snapshot = self.wrap_map.sync(tabs_snapshot.clone(), edits, cx);
@@ -52,7 +52,7 @@ impl DisplayMap {
pub fn fold<T: ToOffset>(
&mut self,
ranges: impl IntoIterator<Item = Range<T>>,
cx: &AppContext,
cx: &mut MutableAppContext,
) {
let (mut fold_map, snapshot, edits) = self.fold_map.write(cx);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
@@ -65,7 +65,7 @@ impl DisplayMap {
pub fn unfold<T: ToOffset>(
&mut self,
ranges: impl IntoIterator<Item = Range<T>>,
cx: &AppContext,
cx: &mut MutableAppContext,
) {
let (mut fold_map, snapshot, edits) = self.fold_map.write(cx);
let (snapshot, edits) = self.tab_map.sync(snapshot, edits);
@@ -75,7 +75,7 @@ impl DisplayMap {
self.wrap_map.sync(snapshot, edits, cx);
}
pub fn set_wrap_width(&self, width: Option<f32>, cx: &AppContext) {
pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) {
self.wrap_map.set_wrap_width(width, cx);
}
@@ -96,6 +96,10 @@ impl DisplayMapSnapshot {
self.wraps_snapshot.buffer_rows(start_row)
}
pub fn buffer_row_count(&self) -> u32 {
self.buffer_snapshot.max_point().row + 1
}
pub fn max_point(&self) -> DisplayPoint {
DisplayPoint(self.wraps_snapshot.max_point())
}
@@ -317,11 +321,11 @@ mod tests {
Buffer::new(0, text, cx)
});
let wrap_width = Some(rng.gen_range(20.0..=100.0));
let map = cx.read(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
let map = cx.update(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
for _op_ix in 0..operations {
buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx));
let snapshot = cx.read(|cx| map.snapshot(cx));
let snapshot = cx.update(|cx| map.snapshot(cx));
let expected_buffer_rows = (0..=snapshot.max_point().row())
.map(|display_row| {
DisplayPoint::new(display_row, 0)
@@ -362,9 +366,9 @@ mod tests {
let text = "one two three four five\nsix seven eight";
let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx));
let map = cx.read(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
let map = cx.update(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
let snapshot = cx.read(|cx| map.snapshot(cx));
let snapshot = cx.update(|cx| map.snapshot(cx));
assert_eq!(
snapshot
.chunks_at(DisplayPoint::new(0, 3))
@@ -385,7 +389,7 @@ mod tests {
buffer.edit(vec![ix..ix], "and ", cx);
});
let snapshot = cx.read(|cx| map.snapshot(cx));
let snapshot = cx.update(|cx| map.snapshot(cx));
assert_eq!(
snapshot
.chunks_at(DisplayPoint::new(1, 0))
@@ -402,7 +406,7 @@ mod tests {
buffer.clone(),
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
None,
cx.as_ref(),
cx,
);
buffer.update(cx, |buffer, cx| {
buffer.edit(
@@ -417,19 +421,19 @@ mod tests {
});
assert_eq!(
&map.snapshot(cx.as_ref())
&map.snapshot(cx)
.chunks_at(DisplayPoint::new(1, 0))
.collect::<String>()[0..10],
" b bb"
);
assert_eq!(
&map.snapshot(cx.as_ref())
&map.snapshot(cx)
.chunks_at(DisplayPoint::new(1, 2))
.collect::<String>()[0..10],
" b bbbb"
);
assert_eq!(
&map.snapshot(cx.as_ref())
&map.snapshot(cx)
.chunks_at(DisplayPoint::new(1, 6))
.collect::<String>()[0..13],
" bbbbb\nc c"
@@ -480,7 +484,7 @@ mod tests {
});
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
let mut map = cx.read(|cx| {
let mut map = cx.update(|cx| {
DisplayMap::new(
buffer,
Settings::new(cx.font_cache()).unwrap().with_tab_size(2),
@@ -489,7 +493,7 @@ mod tests {
)
});
assert_eq!(
cx.read(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
vec![
("fn ".to_string(), None),
("outer".to_string(), Some("fn.name")),
@@ -500,7 +504,7 @@ mod tests {
]
);
assert_eq!(
cx.read(|cx| highlighted_chunks(3..5, &map, &theme, cx)),
cx.update(|cx| highlighted_chunks(3..5, &map, &theme, cx)),
vec![
(" fn ".to_string(), Some("mod.body")),
("inner".to_string(), Some("fn.name")),
@@ -508,9 +512,9 @@ mod tests {
]
);
cx.read(|cx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx));
cx.update(|cx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx));
assert_eq!(
cx.read(|cx| highlighted_chunks(0..2, &map, &theme, cx)),
cx.update(|cx| highlighted_chunks(0..2, &map, &theme, cx)),
vec![
("fn ".to_string(), None),
("out".to_string(), Some("fn.name")),
@@ -575,9 +579,9 @@ mod tests {
buffer_font_size: 16.0,
..Settings::new(&font_cache).unwrap()
};
let mut map = cx.read(|cx| DisplayMap::new(buffer, settings, Some(40.0), cx));
let mut map = cx.update(|cx| DisplayMap::new(buffer, settings, Some(40.0), cx));
assert_eq!(
cx.read(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
[
("fn \n".to_string(), None),
("oute\nr".to_string(), Some("fn.name")),
@@ -585,13 +589,13 @@ mod tests {
]
);
assert_eq!(
cx.read(|cx| highlighted_chunks(3..5, &map, &theme, cx)),
cx.update(|cx| highlighted_chunks(3..5, &map, &theme, cx)),
[("{}\n\n".to_string(), None)]
);
cx.read(|cx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx));
cx.update(|cx| map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx));
assert_eq!(
cx.read(|cx| highlighted_chunks(1..4, &map, &theme, cx)),
cx.update(|cx| highlighted_chunks(1..4, &map, &theme, cx)),
[
("out".to_string(), Some("fn.name")),
("\n".to_string(), None),
@@ -607,7 +611,6 @@ mod tests {
let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n";
let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n";
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
let cx = cx.as_ref();
let map = DisplayMap::new(
buffer.clone(),
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
@@ -647,7 +650,6 @@ mod tests {
fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
let text = "\t\tα\nβ\t\n🏀β\t\tγ";
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
let cx = cx.as_ref();
let map = DisplayMap::new(
buffer.clone(),
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
@@ -718,19 +720,16 @@ mod tests {
buffer.clone(),
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
None,
cx.as_ref(),
cx,
);
assert_eq!(
map.snapshot(cx.as_ref()).max_point(),
DisplayPoint::new(1, 11)
)
assert_eq!(map.snapshot(cx).max_point(), DisplayPoint::new(1, 11))
}
fn highlighted_chunks<'a>(
rows: Range<u32>,
map: &DisplayMap,
theme: &'a Theme,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> Vec<(String, Option<&'a str>)> {
let mut chunks: Vec<(String, Option<&str>)> = Vec::new();
for (chunk, style_id) in map.snapshot(cx).highlighted_chunks_for_rows(rows) {
+1 -1
View File
@@ -126,7 +126,7 @@ mod tests {
..Settings::new(&font_cache).unwrap()
};
let mut wrapper = LineWrapper::new(font_system, font_cache, settings);
let wrapper = LineWrapper::new(font_system, font_cache, settings);
assert_eq!(
wrapper.wrap_line_with_shaping("aa bbb cccc ddddd eeee", 72.0),
+8 -8
View File
@@ -12,7 +12,7 @@ use crate::{
util::Bias,
Settings,
};
use gpui::{executor::Background, AppContext, Task};
use gpui::{executor::Background, MutableAppContext, Task};
use parking_lot::Mutex;
use postage::{prelude::Stream, sink::Sink, watch};
use smol::future::yield_now;
@@ -80,7 +80,7 @@ impl WrapMap {
tab_snapshot: TabSnapshot,
settings: Settings,
wrap_width: Option<f32>,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> Self {
let this = Self(Arc::new(Mutex::new(WrapMapState {
background_task: None,
@@ -116,14 +116,14 @@ impl WrapMap {
&self,
tab_snapshot: TabSnapshot,
edits: Vec<TabEdit>,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> Snapshot {
self.0.lock().pending_edits.push_back((tab_snapshot, edits));
self.flush_edits(cx.background());
self.0.lock().snapshot.clone()
}
pub fn set_wrap_width(&self, wrap_width: Option<f32>, cx: &AppContext) {
pub fn set_wrap_width(&self, wrap_width: Option<f32>, cx: &mut MutableAppContext) {
let mut state = self.0.lock();
if wrap_width == state.wrap_width {
return;
@@ -814,7 +814,7 @@ mod tests {
folds_snapshot.text()
);
log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
let wrap_map = cx.read(|cx| {
let wrap_map = cx.update(|cx| {
WrapMap::new(
tabs_snapshot.clone(),
settings.clone(),
@@ -832,7 +832,7 @@ mod tests {
notifications.recv().await;
}
let snapshot = cx.read(|cx| wrap_map.sync(tabs_snapshot, Vec::new(), cx));
let snapshot = cx.update(|cx| wrap_map.sync(tabs_snapshot, Vec::new(), cx));
let actual_text = snapshot.text();
assert_eq!(
actual_text, expected_text,
@@ -856,12 +856,12 @@ mod tests {
let unwrapped_text = tabs_snapshot.text();
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
let mut snapshot = cx.read(|cx| wrap_map.sync(tabs_snapshot.clone(), edits, cx));
let mut snapshot = cx.update(|cx| wrap_map.sync(tabs_snapshot.clone(), edits, cx));
snapshot.check_invariants(&mut rng);
if wrap_map.is_rewrapping() {
notifications.recv().await;
snapshot = cx.read(|cx| wrap_map.sync(tabs_snapshot, Vec::new(), cx));
snapshot = cx.update(|cx| wrap_map.sync(tabs_snapshot, Vec::new(), cx));
}
snapshot.check_invariants(&mut rng);
+80 -59
View File
@@ -1,3 +1,5 @@
use crate::time::ReplicaId;
use super::{DisplayPoint, Editor, SelectAction};
use gpui::{
color::ColorU,
@@ -9,12 +11,15 @@ use gpui::{
json::{self, ToJson},
text_layout::{self, TextLayoutCache},
AfterLayoutContext, AppContext, Border, Element, Event, EventContext, FontCache, LayoutContext,
PaintContext, Quad, Scene, SizeConstraint, WeakViewHandle,
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
};
use json::json;
use smallvec::SmallVec;
use std::cmp::Ordering;
use std::cmp::{self};
use std::{cmp::Ordering, ops::Range};
use std::{
cmp::{self},
collections::HashMap,
};
pub struct EditorElement {
view: WeakViewHandle<Editor>,
@@ -29,6 +34,13 @@ impl EditorElement {
self.view.upgrade(cx).unwrap().read(cx)
}
fn update_view<F, T>(&self, cx: &mut MutableAppContext, f: F) -> T
where
F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
{
self.view.upgrade(cx).unwrap().update(cx, f)
}
fn mouse_down(
&self,
position: Vector2F,
@@ -38,9 +50,10 @@ impl EditorElement {
cx: &mut EventContext,
) -> bool {
if paint.text_bounds.contains_point(position) {
let view = self.view(cx.app.as_ref());
let position =
paint.point_for_position(view, layout, position, cx.font_cache, cx.app.as_ref());
let position = self.update_view(cx.app, |view, cx| {
let font_cache = cx.font_cache().clone();
paint.point_for_position(view, layout, position, &font_cache, cx)
});
cx.dispatch_action("buffer:select", SelectAction::Begin { position, add: cmd });
true
} else {
@@ -94,19 +107,15 @@ impl EditorElement {
))
}
let action = SelectAction::Update {
position: paint.point_for_position(
view,
layout,
position,
cx.font_cache,
cx.app.as_ref(),
),
let font_cache = cx.font_cache.clone();
let text_layout_cache = cx.text_layout_cache.clone();
let action = self.update_view(cx.app, |view, cx| SelectAction::Update {
position: paint.point_for_position(view, layout, position, &font_cache, cx),
scroll_position: (view.scroll_position() + scroll_delta).clamp(
Vector2F::zero(),
layout.scroll_max(view, cx.font_cache, cx.text_layout_cache, cx.app),
layout.scroll_max(view, &font_cache, &text_layout_cache, cx),
),
};
});
cx.dispatch_action("buffer:select", action);
true
@@ -160,7 +169,9 @@ impl EditorElement {
let y = (view.scroll_position().y() * line_height - delta.y()) / line_height;
let scroll_position = vec2f(x, y).clamp(
Vector2F::zero(),
layout.scroll_max(view, font_cache, layout_cache, cx.app),
self.update_view(cx.app, |view, cx| {
layout.scroll_max(view, font_cache, layout_cache, cx)
}),
);
cx.dispatch_action("buffer:scroll", scroll_position);
@@ -226,14 +237,11 @@ impl EditorElement {
let mut cursors = SmallVec::<[Cursor; 32]>::new();
let content_origin = bounds.origin() + layout.text_offset;
for selection_set_id in view.active_selection_sets(cx.app) {
let (selection_color, cursor_color) =
colors[selection_set_id.replica_id as usize % colors.len()];
for selection in view.selections_in_range(
selection_set_id,
DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0),
cx.app,
) {
for (replica_id, selections) in &layout.selections {
let (selection_color, cursor_color) = colors[*replica_id as usize % colors.len()];
for selection in selections {
if selection.start != selection.end {
let range_start = cmp::min(selection.start, selection.end);
let range_end = cmp::max(selection.start, selection.end);
@@ -326,17 +334,12 @@ impl Element for EditorElement {
constraint: SizeConstraint,
cx: &mut LayoutContext,
) -> (Vector2F, Self::LayoutState) {
let app = &mut cx.app;
let mut size = constraint.max;
if size.y().is_infinite() {
let view = self.view(app);
size.set_y((view.max_point(app).row() + 1) as f32 * view.line_height(cx.font_cache));
}
if size.x().is_infinite() {
unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
}
let view = self.view(app);
let view = self.view(cx.app);
let font_cache = &cx.font_cache;
let layout_cache = &cx.text_layout_cache;
@@ -346,7 +349,7 @@ impl Element for EditorElement {
let gutter_width;
if view.is_gutter_visible() {
gutter_padding = view.em_width(cx.font_cache);
match view.max_line_number_width(cx.font_cache, cx.text_layout_cache, app) {
match view.max_line_number_width(cx.font_cache, cx.text_layout_cache, cx.app) {
Err(error) => {
log::error!("error computing max line number width: {}", error);
return (size, None);
@@ -366,12 +369,18 @@ impl Element for EditorElement {
let wrap_width = text_size.x() - text_offset.x() - overscroll.x();
// TODO: Core text doesn't seem to be keeping our lines below the specified wrap width. Find out why.
let wrap_width = wrap_width - em_width;
view.set_wrap_width(wrap_width, app);
self.update_view(cx.app, |view, cx| {
view.set_wrap_width(wrap_width, cx);
});
let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, app);
if size.y().is_infinite() {
size.set_y((view.max_point(cx.app).row() + 1) as f32 * view.line_height(cx.font_cache));
}
let autoscroll_horizontally = view.autoscroll_vertically(size.y(), line_height, cx.app);
let line_number_layouts = if view.is_gutter_visible() {
match view.layout_line_numbers(size.y(), cx.font_cache, cx.text_layout_cache, app) {
match view.layout_line_numbers(size.y(), cx.font_cache, cx.text_layout_cache, cx.app) {
Err(error) => {
log::error!("error laying out line numbers: {}", error);
return (size, None);
@@ -388,7 +397,7 @@ impl Element for EditorElement {
let mut max_visible_line_width = 0.0;
let line_layouts =
match view.layout_lines(start_row..end_row, font_cache, layout_cache, app) {
match view.layout_lines(start_row..end_row, font_cache, layout_cache, cx.app) {
Err(error) => {
log::error!("error laying out lines: {}", error);
return (size, None);
@@ -404,21 +413,34 @@ impl Element for EditorElement {
}
};
(
let mut selections = HashMap::new();
for selection_set_id in view.active_selection_sets(cx.app) {
selections.insert(
selection_set_id.replica_id,
view.selections_in_range(
selection_set_id,
DisplayPoint::new(start_row, 0)..DisplayPoint::new(end_row, 0),
cx.app,
)
.collect(),
);
}
let layout_state = Some(LayoutState {
size,
Some(LayoutState {
size,
gutter_size,
gutter_padding,
text_size,
overscroll,
text_offset,
line_layouts,
line_number_layouts,
max_visible_line_width,
autoscroll_horizontally,
}),
)
gutter_size,
gutter_padding,
text_size,
overscroll,
text_offset,
line_layouts,
line_number_layouts,
selections,
max_visible_line_width,
autoscroll_horizontally,
});
(size, layout_state)
}
fn after_layout(
@@ -428,12 +450,10 @@ impl Element for EditorElement {
cx: &mut AfterLayoutContext,
) {
if let Some(layout) = layout {
let app = cx.app.as_ref();
let view = self.view(app);
let view = self.view(cx.app);
view.clamp_scroll_left(
layout
.scroll_max(view, cx.font_cache, cx.text_layout_cache, app)
.scroll_max(view, cx.font_cache, cx.text_layout_cache, cx.app)
.x(),
);
@@ -441,10 +461,10 @@ impl Element for EditorElement {
view.autoscroll_horizontally(
view.scroll_position().y() as u32,
layout.text_size.x(),
layout.scroll_width(view, cx.font_cache, cx.text_layout_cache, app),
layout.scroll_width(view, cx.font_cache, cx.text_layout_cache, cx.app),
view.em_width(cx.font_cache),
&layout.line_layouts,
app,
cx.app,
);
}
}
@@ -528,6 +548,7 @@ pub struct LayoutState {
text_size: Vector2F,
line_layouts: Vec<text_layout::Line>,
line_number_layouts: Vec<text_layout::Line>,
selections: HashMap<ReplicaId, Vec<Range<DisplayPoint>>>,
overscroll: Vector2F,
text_offset: Vector2F,
max_visible_line_width: f32,
@@ -540,7 +561,7 @@ impl LayoutState {
view: &Editor,
font_cache: &FontCache,
layout_cache: &TextLayoutCache,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> f32 {
let row = view.longest_row(cx);
let longest_line_width = view
@@ -555,7 +576,7 @@ impl LayoutState {
view: &Editor,
font_cache: &FontCache,
layout_cache: &TextLayoutCache,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> Vector2F {
vec2f(
((self.scroll_width(view, font_cache, layout_cache, cx) - self.text_size.x())
@@ -578,7 +599,7 @@ impl PaintState {
layout: &LayoutState,
position: Vector2F,
font_cache: &FontCache,
cx: &AppContext,
cx: &mut MutableAppContext,
) -> DisplayPoint {
let scroll_position = view.scroll_position();
let position = position - self.text_bounds.origin();
+2 -2
View File
@@ -311,7 +311,7 @@ impl FileFinder {
}
fn workspace_updated(&mut self, _: ViewHandle<Workspace>, cx: &mut ViewContext<Self>) {
if let Some(task) = self.spawn_search(self.query_buffer.read(cx).text(cx.as_ref()), cx) {
if let Some(task) = self.spawn_search(self.query_buffer.read(cx).text(cx), cx) {
task.detach();
}
}
@@ -324,7 +324,7 @@ impl FileFinder {
) {
match event {
editor::Event::Edited => {
let query = self.query_buffer.read(cx).text(cx.as_ref());
let query = self.query_buffer.update(cx, |buffer, cx| buffer.text(cx));
if query.is_empty() {
self.latest_search_id = util::post_inc(&mut self.search_count);
self.matches.clear();
+1 -1
View File
@@ -1329,7 +1329,7 @@ mod tests {
.to_any()
.downcast::<Editor>()
.unwrap();
assert!(editor.read(cx).text(cx.as_ref()).is_empty());
assert!(editor.update(cx, |editor, cx| editor.text(cx).is_empty()));
});
}