Detailed changes
@@ -1575,6 +1575,29 @@ dependencies = [
"getrandom 0.2.2",
]
+[[package]]
+name = "editor"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "buffer",
+ "clock",
+ "gpui",
+ "lazy_static",
+ "log",
+ "parking_lot",
+ "postage",
+ "rand 0.8.3",
+ "serde 1.0.125",
+ "smallvec",
+ "smol",
+ "sum_tree",
+ "tree-sitter",
+ "tree-sitter-rust",
+ "unindent",
+ "util",
+]
+
[[package]]
name = "either"
version = "1.6.1"
@@ -6030,6 +6053,7 @@ dependencies = [
"ctor",
"dirs 3.0.1",
"easy-parallel",
+ "editor",
"env_logger",
"fsevent",
"futures",
@@ -4,6 +4,7 @@ use crate::HighlightId;
use gpui::fonts::HighlightStyle;
use serde::Deserialize;
+#[derive(Default)]
pub struct SyntaxTheme {
pub(crate) highlights: Vec<(String, HighlightStyle)>,
}
@@ -0,0 +1,29 @@
+[package]
+name = "editor"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+test-support = ["buffer/test-support"]
+
+[dependencies]
+anyhow = "1.0"
+buffer = { path = "../buffer" }
+clock = { path = "../clock" }
+gpui = { path = "../gpui" }
+lazy_static = "1.4"
+log = "0.4"
+parking_lot = "0.11"
+postage = { version = "0.4", features = ["futures-traits"] }
+serde = { version = "1", features = ["derive", "rc"] }
+smallvec = { version = "1.6", features = ["union"] }
+smol = "1.2"
+sum_tree = { path = "../sum_tree" }
+util = { path = "../util" }
+
+[dev-dependencies]
+rand = "0.8"
+unindent = "0.1.7"
+tree-sitter = "0.19"
+tree-sitter-rust = "0.19"
+buffer = { path = "../buffer", features = ["test-support"] }
@@ -357,7 +357,7 @@ impl ToDisplayPoint for Anchor {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{editor::movement, test::*};
+ use crate::{movement, test::*};
use buffer::{History, Language, LanguageConfig, RandomCharIter, SelectionGoal, SyntaxTheme};
use gpui::{color::Color, MutableAppContext};
use rand::{prelude::StdRng, Rng};
@@ -1128,7 +1128,7 @@ impl FoldEdit {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{editor::ToPoint, test::sample_text};
+ use crate::{test::sample_text, ToPoint};
use buffer::RandomCharIter;
use rand::prelude::*;
use std::{env, mem};
@@ -2,8 +2,7 @@ use super::{
fold_map,
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary},
};
-use crate::editor::Point;
-use buffer::HighlightId;
+use buffer::{HighlightId, Point};
use gpui::{fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, Task};
use lazy_static::lazy_static;
use smol::future::yield_now;
@@ -897,13 +896,10 @@ impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
mod tests {
use super::*;
use crate::{
- editor::{
- display_map::{fold_map::FoldMap, tab_map::TabMap},
- Buffer,
- },
+ display_map::{fold_map::FoldMap, tab_map::TabMap},
test::Observer,
};
- use buffer::RandomCharIter;
+ use buffer::{Buffer, RandomCharIter};
use rand::prelude::*;
use std::env;
@@ -1,6 +1,6 @@
use super::{
- DisplayPoint, Editor, EditorMode, EditorStyle, Insert, Scroll, Select, SelectPhase, Snapshot,
- MAX_LINE_LEN,
+ DisplayPoint, Editor, EditorMode, EditorSettings, EditorStyle, Insert, Scroll, Select,
+ SelectPhase, Snapshot, MAX_LINE_LEN,
};
use buffer::HighlightId;
use clock::ReplicaId;
@@ -28,12 +28,12 @@ use std::{
pub struct EditorElement {
view: WeakViewHandle<Editor>,
- style: EditorStyle,
+ settings: EditorSettings,
}
impl EditorElement {
- pub fn new(view: WeakViewHandle<Editor>, style: EditorStyle) -> Self {
- Self { view, style }
+ pub fn new(view: WeakViewHandle<Editor>, settings: EditorSettings) -> Self {
+ Self { view, settings }
}
fn view<'a>(&self, cx: &'a AppContext) -> &'a Editor {
@@ -196,15 +196,16 @@ impl EditorElement {
let bounds = gutter_bounds.union_rect(text_bounds);
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
let editor = self.view(cx.app);
+ let style = &self.settings.style;
cx.scene.push_quad(Quad {
bounds: gutter_bounds,
- background: Some(self.style.gutter_background),
+ background: Some(style.gutter_background),
border: Border::new(0., Color::transparent_black()),
corner_radius: 0.,
});
cx.scene.push_quad(Quad {
bounds: text_bounds,
- background: Some(self.style.background),
+ background: Some(style.background),
border: Border::new(0., Color::transparent_black()),
corner_radius: 0.,
});
@@ -231,7 +232,7 @@ impl EditorElement {
);
cx.scene.push_quad(Quad {
bounds: RectF::new(origin, size),
- background: Some(self.style.active_line_background),
+ background: Some(style.active_line_background),
border: Border::default(),
corner_radius: 0.,
});
@@ -268,8 +269,7 @@ impl EditorElement {
cx: &mut PaintContext,
) {
let view = self.view(cx.app);
- let settings = self.view(cx.app).settings.borrow();
- let theme = &settings.theme.editor;
+ let style = &self.settings.style;
let local_replica_id = view.replica_id(cx);
let scroll_position = layout.snapshot.scroll_position();
let start_row = scroll_position.y() as u32;
@@ -287,11 +287,11 @@ impl EditorElement {
let content_origin = bounds.origin() + layout.text_offset;
for (replica_id, selections) in &layout.selections {
- let style_ix = *replica_id as usize % (theme.guest_selections.len() + 1);
+ let style_ix = *replica_id as usize % (style.guest_selections.len() + 1);
let style = if style_ix == 0 {
- &theme.selection
+ &style.selection
} else {
- &theme.guest_selections[style_ix - 1]
+ &style.guest_selections[style_ix - 1]
};
for selection in selections {
@@ -383,15 +383,16 @@ impl EditorElement {
fn max_line_number_width(&self, snapshot: &Snapshot, cx: &LayoutContext) -> f32 {
let digit_count = (snapshot.buffer_row_count() as f32).log10().floor() as usize + 1;
+ let style = &self.settings.style;
cx.text_layout_cache
.layout_str(
"1".repeat(digit_count).as_str(),
- self.style.text.font_size,
+ style.text.font_size,
&[(
digit_count,
RunStyle {
- font_id: self.style.text.font_id,
+ font_id: style.text.font_id,
color: Color::black(),
underline: false,
},
@@ -407,6 +408,7 @@ impl EditorElement {
snapshot: &Snapshot,
cx: &LayoutContext,
) -> Vec<Option<text_layout::Line>> {
+ let style = &self.settings.style;
let mut layouts = Vec::with_capacity(rows.len());
let mut line_number = String::new();
for (ix, (buffer_row, soft_wrapped)) in snapshot
@@ -416,9 +418,9 @@ impl EditorElement {
{
let display_row = rows.start + ix as u32;
let color = if active_rows.contains_key(&display_row) {
- self.style.line_number_active
+ style.line_number_active
} else {
- self.style.line_number
+ style.line_number
};
if soft_wrapped {
layouts.push(None);
@@ -427,11 +429,11 @@ impl EditorElement {
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
layouts.push(Some(cx.text_layout_cache.layout_str(
&line_number,
- self.style.text.font_size,
+ style.text.font_size,
&[(
line_number.len(),
RunStyle {
- font_id: self.style.text.font_id,
+ font_id: style.text.font_id,
color,
underline: false,
},
@@ -456,7 +458,7 @@ impl EditorElement {
// When the editor is empty and unfocused, then show the placeholder.
if snapshot.is_empty() && !snapshot.is_focused() {
- let placeholder_style = self.style.placeholder_text();
+ let placeholder_style = self.settings.style.placeholder_text();
let placeholder_text = snapshot.placeholder_text();
let placeholder_lines = placeholder_text
.as_ref()
@@ -482,10 +484,10 @@ impl EditorElement {
.collect();
}
- let mut prev_font_properties = self.style.text.font_properties.clone();
- let mut prev_font_id = self.style.text.font_id;
+ let style = &self.settings.style;
+ let mut prev_font_properties = style.text.font_properties.clone();
+ let mut prev_font_id = style.text.font_id;
- let theme = snapshot.theme().clone();
let mut layouts = Vec::with_capacity(rows.len());
let mut line = String::new();
let mut styles = Vec::new();
@@ -498,7 +500,7 @@ impl EditorElement {
if ix > 0 {
layouts.push(cx.text_layout_cache.layout_str(
&line,
- self.style.text.font_size,
+ style.text.font_size,
&styles,
));
line.clear();
@@ -511,17 +513,20 @@ impl EditorElement {
}
if !line_chunk.is_empty() && !line_exceeded_max_len {
- let style = theme
+ let highlight_style = style
.syntax
.highlight_style(style_ix)
- .unwrap_or(self.style.text.clone().into());
+ .unwrap_or(style.text.clone().into());
// Avoid a lookup if the font properties match the previous ones.
- let font_id = if style.font_properties == prev_font_properties {
+ let font_id = if highlight_style.font_properties == prev_font_properties {
prev_font_id
} else {
cx.font_cache
- .select_font(self.style.text.font_family_id, &style.font_properties)
- .unwrap_or(self.style.text.font_id)
+ .select_font(
+ style.text.font_family_id,
+ &highlight_style.font_properties,
+ )
+ .unwrap_or(style.text.font_id)
};
if line.len() + line_chunk.len() > MAX_LINE_LEN {
@@ -538,12 +543,12 @@ impl EditorElement {
line_chunk.len(),
RunStyle {
font_id,
- color: style.color,
- underline: style.underline,
+ color: highlight_style.color,
+ underline: highlight_style.underline,
},
));
prev_font_id = font_id;
- prev_font_properties = style.font_properties;
+ prev_font_properties = highlight_style.font_properties;
}
}
}
@@ -567,12 +572,13 @@ impl Element for EditorElement {
}
let snapshot = self.snapshot(cx.app);
- let line_height = self.style.text.line_height(cx.font_cache);
+ let style = self.settings.style.clone();
+ let line_height = style.text.line_height(cx.font_cache);
let gutter_padding;
let gutter_width;
if snapshot.mode == EditorMode::Full {
- gutter_padding = self.style.text.em_width(cx.font_cache);
+ gutter_padding = style.text.em_width(cx.font_cache);
gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0;
} else {
gutter_padding = 0.0;
@@ -580,8 +586,8 @@ impl Element for EditorElement {
};
let text_width = size.x() - gutter_width;
- let text_offset = vec2f(-self.style.text.descent(cx.font_cache), 0.);
- let em_width = self.style.text.em_width(cx.font_cache);
+ let text_offset = vec2f(-style.text.descent(cx.font_cache), 0.);
+ let em_width = style.text.em_width(cx.font_cache);
let overscroll = vec2f(em_width, 0.);
let wrap_width = text_width - text_offset.x() - overscroll.x() - em_width;
let snapshot = self.update_view(cx.app, |view, cx| {
@@ -677,7 +683,7 @@ impl Element for EditorElement {
overscroll,
text_offset,
snapshot,
- style: self.style.clone(),
+ style: self.settings.style.clone(),
active_rows,
line_layouts,
line_number_layouts,
@@ -689,7 +695,7 @@ impl Element for EditorElement {
let scroll_max = layout.scroll_max(cx.font_cache, cx.text_layout_cache).x();
let scroll_width = layout.scroll_width(cx.text_layout_cache);
- let max_glyph_width = self.style.text.em_width(&cx.font_cache);
+ let max_glyph_width = style.text.em_width(&cx.font_cache);
self.update_view(cx.app, |view, cx| {
let clamped = view.clamp_scroll_left(scroll_max);
let autoscrolled;
@@ -1035,30 +1041,27 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
mod tests {
use super::*;
use crate::{
- editor::{Buffer, Editor, EditorStyle},
- settings,
test::sample_text,
+ {Editor, EditorSettings},
};
+ use buffer::Buffer;
#[gpui::test]
fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
- let font_cache = cx.font_cache().clone();
- let settings = settings::test(&cx).1;
- let style = EditorStyle::test(&font_cache);
+ let settings = EditorSettings::test(cx);
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
Editor::for_buffer(
buffer,
- settings.clone(),
{
- let style = style.clone();
- move |_| style.clone()
+ let settings = settings.clone();
+ move |_| settings.clone()
},
cx,
)
});
- let element = EditorElement::new(editor.downgrade(), style);
+ let element = EditorElement::new(editor.downgrade(), settings);
let layouts = editor.update(cx, |editor, cx| {
let snapshot = editor.snapshot(cx);
@@ -2,8 +2,11 @@ pub mod display_map;
mod element;
pub mod movement;
-use crate::{project::ProjectPath, settings::Settings, theme::Theme, workspace};
-use anyhow::Result;
+#[cfg(test)]
+mod test;
+
+// use crate::{project::ProjectPath, settings::Settings, theme::Theme, workspace};
+
use buffer::*;
use clock::ReplicaId;
pub use display_map::DisplayPoint;
@@ -12,9 +15,8 @@ pub use element::*;
use gpui::{
action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, keymap::Binding,
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
- MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle,
+ MutableAppContext, RenderContext, View, ViewContext, WeakViewHandle,
};
-use postage::watch;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use smol::Timer;
@@ -23,14 +25,12 @@ use std::{
cmp::{self, Ordering},
mem,
ops::{Range, RangeInclusive},
- path::Path,
rc::Rc,
sync::Arc,
time::Duration,
};
use sum_tree::Bias;
use util::post_inc;
-use worktree::Worktree;
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
const MAX_LINE_LEN: usize = 1024;
@@ -279,6 +279,12 @@ pub enum EditorMode {
Full,
}
+#[derive(Clone)]
+pub struct EditorSettings {
+ pub tab_size: usize,
+ pub style: EditorStyle,
+}
+
#[derive(Clone, Deserialize)]
pub struct EditorStyle {
pub text: TextStyle,
@@ -291,6 +297,7 @@ pub struct EditorStyle {
pub line_number: Color,
pub line_number_active: Color,
pub guest_selections: Vec<SelectionStyle>,
+ pub syntax: Arc<SyntaxTheme>,
}
#[derive(Clone, Copy, Default, Deserialize)]
@@ -311,8 +318,7 @@ pub struct Editor {
scroll_position: Vector2F,
scroll_top_anchor: Anchor,
autoscroll_requested: bool,
- build_style: Rc<RefCell<dyn FnMut(&mut MutableAppContext) -> EditorStyle>>,
- settings: watch::Receiver<Settings>,
+ build_settings: Rc<RefCell<dyn Fn(&AppContext) -> EditorSettings>>,
focused: bool,
show_local_cursors: bool,
blink_epoch: usize,
@@ -325,7 +331,6 @@ pub struct Snapshot {
pub mode: EditorMode,
pub display_snapshot: DisplayMapSnapshot,
pub placeholder_text: Option<Arc<str>>,
- pub theme: Arc<Theme>,
is_focused: bool,
scroll_position: Vector2F,
scroll_top_anchor: Anchor,
@@ -344,50 +349,53 @@ struct ClipboardSelection {
impl Editor {
pub fn single_line(
- settings: watch::Receiver<Settings>,
- build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
+ build_settings: impl 'static + Fn(&AppContext) -> EditorSettings,
cx: &mut ViewContext<Self>,
) -> Self {
let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
- let mut view = Self::for_buffer(buffer, settings, build_style, cx);
+ let mut view = Self::for_buffer(buffer, build_settings, cx);
view.mode = EditorMode::SingleLine;
view
}
pub fn auto_height(
max_lines: usize,
- settings: watch::Receiver<Settings>,
- build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
+ build_settings: impl 'static + Fn(&AppContext) -> EditorSettings,
cx: &mut ViewContext<Self>,
) -> Self {
let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
- let mut view = Self::for_buffer(buffer, settings, build_style, cx);
+ let mut view = Self::for_buffer(buffer, build_settings, cx);
view.mode = EditorMode::AutoHeight { max_lines };
view
}
pub fn for_buffer(
buffer: ModelHandle<Buffer>,
- settings: watch::Receiver<Settings>,
- build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
+ build_settings: impl 'static + Fn(&AppContext) -> EditorSettings,
cx: &mut ViewContext<Self>,
) -> Self {
- Self::new(buffer, settings, Rc::new(RefCell::new(build_style)), cx)
+ Self::new(buffer, Rc::new(RefCell::new(build_settings)), cx)
}
- fn new(
+ pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
+ let mut clone = Self::new(self.buffer.clone(), self.build_settings.clone(), cx);
+ clone.scroll_position = self.scroll_position;
+ clone.scroll_top_anchor = self.scroll_top_anchor.clone();
+ clone
+ }
+
+ pub fn new(
buffer: ModelHandle<Buffer>,
- settings: watch::Receiver<Settings>,
- build_style: Rc<RefCell<dyn FnMut(&mut MutableAppContext) -> EditorStyle>>,
+ build_settings: Rc<RefCell<dyn Fn(&AppContext) -> EditorSettings>>,
cx: &mut ViewContext<Self>,
) -> Self {
- let style = build_style.borrow_mut()(cx);
+ let settings = build_settings.borrow_mut()(cx);
let display_map = cx.add_model(|cx| {
DisplayMap::new(
buffer.clone(),
- settings.borrow().tab_size,
- style.text.font_id,
- style.text.font_size,
+ settings.tab_size,
+ settings.style.text.font_id,
+ settings.style.text.font_size,
None,
cx,
)
@@ -419,11 +427,10 @@ impl Editor {
next_selection_id,
add_selections_state: None,
select_larger_syntax_node_stack: Vec::new(),
- build_style,
+ build_settings,
scroll_position: Vector2F::zero(),
scroll_top_anchor: Anchor::min(),
autoscroll_requested: false,
- settings,
focused: false,
show_local_cursors: false,
blink_epoch: 0,
@@ -442,14 +449,11 @@ impl Editor {
}
pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> Snapshot {
- let settings = self.settings.borrow();
-
Snapshot {
mode: self.mode,
display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
scroll_position: self.scroll_position,
scroll_top_anchor: self.scroll_top_anchor.clone(),
- theme: settings.theme.clone(),
placeholder_text: self.placeholder_text.clone(),
is_focused: self
.handle
@@ -719,7 +723,11 @@ impl Editor {
}
#[cfg(test)]
- fn select_display_ranges<'a, T>(&mut self, ranges: T, cx: &mut ViewContext<Self>) -> Result<()>
+ fn select_display_ranges<'a, T>(
+ &mut self,
+ ranges: T,
+ cx: &mut ViewContext<Self>,
+ ) -> anyhow::Result<()>
where
T: IntoIterator<Item = &'a Range<DisplayPoint>>,
{
@@ -2293,9 +2301,9 @@ impl Editor {
.text()
}
- pub fn font_size(&self) -> f32 {
- self.settings.borrow().buffer_font_size
- }
+ // pub fn font_size(&self) -> f32 {
+ // self.settings.font_size
+ // }
pub fn set_wrap_width(&self, width: f32, cx: &mut MutableAppContext) -> bool {
self.display_map
@@ -2409,10 +2417,6 @@ impl Snapshot {
.highlighted_chunks_for_rows(display_rows)
}
- pub fn theme(&self) -> &Arc<Theme> {
- &self.theme
- }
-
pub fn scroll_position(&self) -> Vector2F {
compute_scroll_position(
&self.display_snapshot,
@@ -2473,6 +2477,7 @@ impl EditorStyle {
line_number_active: Default::default(),
selection: Default::default(),
guest_selections: Default::default(),
+ syntax: Default::default(),
}
}
@@ -2481,6 +2486,16 @@ impl EditorStyle {
}
}
+impl EditorSettings {
+ #[cfg(any(test, feature = "test-support"))]
+ pub fn test(cx: &AppContext) -> Self {
+ Self {
+ tab_size: 4,
+ style: EditorStyle::test(cx.font_cache()),
+ }
+ }
+}
+
fn compute_scroll_position(
snapshot: &DisplayMapSnapshot,
mut scroll_position: Vector2F,
@@ -2517,11 +2532,15 @@ impl Entity for Editor {
impl View for Editor {
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
- let style = self.build_style.borrow_mut()(cx);
+ let settings = self.build_settings.borrow_mut()(cx);
self.display_map.update(cx, |map, cx| {
- map.set_font(style.text.font_id, style.text.font_size, cx)
+ map.set_font(
+ settings.style.text.font_id,
+ settings.style.text.font_size,
+ cx,
+ )
});
- EditorElement::new(self.handle.clone(), style).boxed()
+ EditorElement::new(self.handle.clone(), settings).boxed()
}
fn ui_name() -> &'static str {
@@ -2560,156 +2579,6 @@ impl View for Editor {
}
}
-impl workspace::Item for Buffer {
- type View = Editor;
-
- fn build_view(
- handle: ModelHandle<Self>,
- settings: watch::Receiver<Settings>,
- cx: &mut ViewContext<Self::View>,
- ) -> Self::View {
- Editor::for_buffer(
- handle,
- settings.clone(),
- move |cx| {
- let settings = settings.borrow();
- let font_cache = cx.font_cache();
- let font_family_id = settings.buffer_font_family;
- let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
- let font_properties = Default::default();
- let font_id = font_cache
- .select_font(font_family_id, &font_properties)
- .unwrap();
- let font_size = settings.buffer_font_size;
-
- let mut theme = settings.theme.editor.clone();
- theme.text = TextStyle {
- color: theme.text.color,
- font_family_name,
- font_family_id,
- font_id,
- font_size,
- font_properties,
- underline: false,
- };
- theme
- },
- cx,
- )
- }
-
- fn project_path(&self) -> Option<ProjectPath> {
- self.file().map(|f| ProjectPath {
- worktree_id: f.worktree_id(),
- path: f.path().clone(),
- })
- }
-}
-
-impl workspace::ItemView for Editor {
- fn should_activate_item_on_event(event: &Self::Event) -> bool {
- matches!(event, Event::Activate)
- }
-
- fn should_close_item_on_event(event: &Self::Event) -> bool {
- matches!(event, Event::Closed)
- }
-
- fn should_update_tab_on_event(event: &Self::Event) -> bool {
- matches!(
- event,
- Event::Saved | Event::Dirtied | Event::FileHandleChanged
- )
- }
-
- fn title(&self, cx: &AppContext) -> std::string::String {
- let filename = self
- .buffer
- .read(cx)
- .file()
- .and_then(|file| file.file_name(cx));
- if let Some(name) = filename {
- name.to_string_lossy().into()
- } else {
- "untitled".into()
- }
- }
-
- fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
- self.buffer.read(cx).file().map(|file| ProjectPath {
- worktree_id: file.worktree_id(),
- path: file.path().clone(),
- })
- }
-
- fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
- where
- Self: Sized,
- {
- let mut clone = Editor::new(
- self.buffer.clone(),
- self.settings.clone(),
- self.build_style.clone(),
- cx,
- );
- clone.scroll_position = self.scroll_position;
- clone.scroll_top_anchor = self.scroll_top_anchor.clone();
- Some(clone)
- }
-
- fn save(&mut self, cx: &mut ViewContext<Self>) -> Result<Task<Result<()>>> {
- let save = self.buffer.update(cx, |b, cx| b.save(cx))?;
- Ok(cx.spawn(|_, _| async move {
- save.await?;
- Ok(())
- }))
- }
-
- fn save_as(
- &mut self,
- worktree: ModelHandle<Worktree>,
- path: &Path,
- cx: &mut ViewContext<Self>,
- ) -> Task<Result<()>> {
- self.buffer.update(cx, |buffer, cx| {
- let handle = cx.handle();
- let text = buffer.as_rope().clone();
- let version = buffer.version();
-
- let save_as = worktree.update(cx, |worktree, cx| {
- worktree
- .as_local_mut()
- .unwrap()
- .save_buffer_as(handle, path, text, cx)
- });
-
- cx.spawn(|buffer, mut cx| async move {
- save_as.await.map(|new_file| {
- let language = worktree.read_with(&cx, |worktree, cx| {
- worktree
- .languages()
- .select_language(new_file.full_path(cx))
- .cloned()
- });
-
- buffer.update(&mut cx, |buffer, cx| {
- buffer.did_save(version, new_file.mtime, Some(Box::new(new_file)), cx);
- buffer.set_language(language, cx);
- });
- })
- })
- })
- }
-
- fn is_dirty(&self, cx: &AppContext) -> bool {
- self.buffer.read(cx).is_dirty()
- }
-
- fn has_conflict(&self, cx: &AppContext) -> bool {
- self.buffer.read(cx).has_conflict()
- }
-}
-
impl SelectionExt for Selection {
fn display_range(&self, map: &DisplayMapSnapshot) -> Range<DisplayPoint> {
let start = self.start.to_display_point(map, Bias::Left);
@@ -2749,18 +2618,14 @@ impl SelectionExt for Selection {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{
- editor::Point,
- settings,
- test::{self, sample_text},
- };
- use buffer::History;
+ use crate::test::sample_text;
+ use buffer::{History, Point};
use unindent::Unindent;
#[gpui::test]
fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(cx);
let (_, editor) =
cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
@@ -2827,7 +2692,7 @@ mod tests {
#[gpui::test]
fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(cx);
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -2859,7 +2724,7 @@ mod tests {
#[gpui::test]
fn test_cancel(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(cx);
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -2922,7 +2787,7 @@ mod tests {
cx,
)
});
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -2990,7 +2855,7 @@ mod tests {
#[gpui::test]
fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -3067,7 +2932,7 @@ mod tests {
#[gpui::test]
fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "βββββ\nabcde\nαβγδΡ\n", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -3125,7 +2990,7 @@ mod tests {
#[gpui::test]
fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "βββββ\nabcd\nΞ±Ξ²Ξ³\nabcd\nβββββ\n", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -3156,7 +3021,7 @@ mod tests {
#[gpui::test]
fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\n def", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
view.select_display_ranges(
@@ -3299,7 +3164,7 @@ mod tests {
fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
let buffer =
cx.add_model(|cx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
view.select_display_ranges(
@@ -3439,11 +3304,11 @@ mod tests {
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
let buffer =
cx.add_model(|cx| Buffer::new(0, "use one::{\n two::three::four::five\n};", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
- view.set_wrap_width(130., cx);
+ view.set_wrap_width(140., cx);
assert_eq!(
view.display_text(cx),
"use one::{\n two::three::\n four::five\n};"
@@ -3493,7 +3358,7 @@ mod tests {
#[gpui::test]
fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "one two three four", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -3540,7 +3405,7 @@ mod tests {
cx,
)
});
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -3576,7 +3441,7 @@ mod tests {
cx,
)
});
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
});
@@ -3605,7 +3470,7 @@ mod tests {
#[gpui::test]
fn test_delete_line(cx: &mut gpui::MutableAppContext) {
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -3629,7 +3494,7 @@ mod tests {
);
});
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -3646,7 +3511,7 @@ mod tests {
#[gpui::test]
fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -3673,7 +3538,7 @@ mod tests {
);
});
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -3699,7 +3564,7 @@ mod tests {
#[gpui::test]
fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(10, 5), cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -3797,7 +3662,7 @@ mod tests {
#[gpui::test]
fn test_clipboard(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "oneβ
two three four five six ", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let view = cx
.add_window(Default::default(), |cx| {
build_editor(buffer.clone(), settings, cx)
@@ -3932,7 +3797,7 @@ mod tests {
#[gpui::test]
fn test_select_all(cx: &mut gpui::MutableAppContext) {
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\nde\nfgh", cx));
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
view.select_all(&SelectAll, cx);
@@ -3945,7 +3810,7 @@ mod tests {
#[gpui::test]
fn test_select_line(cx: &mut gpui::MutableAppContext) {
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 5), cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -3991,7 +3856,7 @@ mod tests {
#[gpui::test]
fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(9, 5), cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
view.update(cx, |view, cx| {
@@ -4059,7 +3924,7 @@ mod tests {
#[gpui::test]
fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
- let settings = settings::test(&cx).1;
+ let settings = EditorSettings::test(&cx);
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", cx));
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
@@ -4232,9 +4097,17 @@ mod tests {
#[gpui::test]
async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
- let app_state = cx.update(test::test_app_state);
+ let settings = cx.read(EditorSettings::test);
+
+ let grammar = tree_sitter_rust::language();
+ let language = Arc::new(Language {
+ config: LanguageConfig::default(),
+ brackets_query: tree_sitter::Query::new(grammar, "").unwrap(),
+ highlight_query: tree_sitter::Query::new(grammar, "").unwrap(),
+ highlight_map: Default::default(),
+ grammar,
+ });
- let lang = app_state.languages.select_language("z.rs");
let text = r#"
use mod1::mod2::{mod3, mod4};
@@ -4245,9 +4118,9 @@ mod tests {
.unindent();
let buffer = cx.add_model(|cx| {
let history = History::new(text.into());
- Buffer::from_history(0, history, None, lang.cloned(), cx)
+ Buffer::from_history(0, history, None, Some(language), cx)
});
- let (_, view) = cx.add_window(|cx| build_editor(buffer, app_state.settings.clone(), cx));
+ let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing())
.await;
@@ -4388,36 +4261,10 @@ mod tests {
fn build_editor(
buffer: ModelHandle<Buffer>,
- settings: watch::Receiver<Settings>,
+ settings: EditorSettings,
cx: &mut ViewContext<Editor>,
) -> Editor {
- let style = {
- let font_cache = cx.font_cache();
- let settings = settings.borrow();
- EditorStyle {
- text: TextStyle {
- color: Default::default(),
- font_family_name: font_cache.family_name(settings.buffer_font_family).unwrap(),
- font_family_id: settings.buffer_font_family,
- font_id: font_cache
- .select_font(settings.buffer_font_family, &Default::default())
- .unwrap(),
- font_size: settings.buffer_font_size,
- font_properties: Default::default(),
- underline: false,
- },
- placeholder_text: None,
- background: Default::default(),
- selection: Default::default(),
- gutter_background: Default::default(),
- active_line_background: Default::default(),
- line_number: Default::default(),
- line_number_active: Default::default(),
- guest_selections: Default::default(),
- }
- };
-
- Editor::for_buffer(buffer, settings, move |_| style.clone(), cx)
+ Editor::for_buffer(buffer, move |_| settings.clone(), cx)
}
}
@@ -196,7 +196,7 @@ fn char_kind(c: char) -> CharKind {
#[cfg(test)]
mod tests {
use super::*;
- use crate::editor::{display_map::DisplayMap, Buffer};
+ use crate::{display_map::DisplayMap, Buffer};
#[gpui::test]
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
@@ -0,0 +1,39 @@
+use gpui::{Entity, ModelHandle};
+use smol::channel;
+use std::marker::PhantomData;
+
+pub fn sample_text(rows: usize, cols: usize) -> String {
+ let mut text = String::new();
+ for row in 0..rows {
+ let c: char = ('a' as u32 + row as u32) as u8 as char;
+ let mut line = c.to_string().repeat(cols);
+ if row < rows - 1 {
+ line.push('\n');
+ }
+ text += &line;
+ }
+ text
+}
+
+pub struct Observer<T>(PhantomData<T>);
+
+impl<T: 'static> Entity for Observer<T> {
+ type Event = ();
+}
+
+impl<T: Entity> Observer<T> {
+ pub fn new(
+ handle: &ModelHandle<T>,
+ cx: &mut gpui::TestAppContext,
+ ) -> (ModelHandle<Self>, channel::Receiver<()>) {
+ let (notify_tx, notify_rx) = channel::unbounded();
+ let observer = cx.add_model(|cx| {
+ cx.observe(handle, move |_, _, _| {
+ let _ = notify_tx.try_send(());
+ })
+ .detach();
+ Observer(PhantomData)
+ });
+ (observer, notify_rx)
+ }
+}
@@ -13,6 +13,7 @@ mod rpc;
mod team;
use self::errors::TideResultExt as _;
+use ::rpc::Peer;
use anyhow::Result;
use async_std::net::TcpListener;
use async_trait::async_trait;
@@ -26,7 +27,6 @@ use std::sync::Arc;
use surf::http::cookies::SameSite;
use tide::{log, sessions::SessionMiddleware};
use tide_compress::CompressMiddleware;
-use rpc::Peer;
type Request = tide::Request<Arc<AppState>>;
@@ -10,6 +10,10 @@ use async_std::{sync::RwLock, task};
use async_tungstenite::{tungstenite::protocol::Role, WebSocketStream};
use futures::{future::BoxFuture, FutureExt};
use postage::{mpsc, prelude::Sink as _, prelude::Stream as _};
+use rpc::{
+ proto::{self, AnyTypedEnvelope, EnvelopedMessage},
+ Connection, ConnectionId, Peer, TypedEnvelope,
+};
use sha1::{Digest as _, Sha1};
use std::{
any::TypeId,
@@ -27,10 +31,6 @@ use tide::{
Request, Response,
};
use time::OffsetDateTime;
-use rpc::{
- proto::{self, AnyTypedEnvelope, EnvelopedMessage},
- Connection, ConnectionId, Peer, TypedEnvelope,
-};
type MessageHandler = Box<
dyn Send
@@ -960,6 +960,7 @@ mod tests {
db::{tests::TestDb, UserId},
github, AppState, Config,
};
+ use ::rpc::Peer;
use async_std::{sync::RwLockReadGuard, task};
use gpui::{ModelHandle, TestAppContext};
use parking_lot::Mutex;
@@ -977,23 +978,20 @@ mod tests {
use zed::{
buffer::LanguageRegistry,
channel::{Channel, ChannelDetails, ChannelList},
- editor::{Editor, EditorStyle, Insert},
+ editor::{Editor, EditorSettings, Insert},
fs::{FakeFs, Fs as _},
people_panel::JoinWorktree,
project::ProjectPath,
rpc::{self, Client, Credentials, EstablishConnectionError},
- settings,
test::FakeHttpClient,
user::UserStore,
workspace::Workspace,
worktree::Worktree,
};
- use rpc::Peer;
#[gpui::test]
async fn test_share_worktree(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
let (window_b, _) = cx_b.add_window(|_| EmptyView);
- let settings = cx_b.read(settings::test).1;
let lang_registry = Arc::new(LanguageRegistry::new());
// Connect to a server as 2 clients.
@@ -1063,12 +1061,7 @@ mod tests {
// Create a selection set as client B and see that selection set as client A.
let editor_b = cx_b.add_view(window_b, |cx| {
- Editor::for_buffer(
- buffer_b,
- settings,
- |cx| EditorStyle::test(cx.font_cache()),
- cx,
- )
+ Editor::for_buffer(buffer_b, |cx| EditorSettings::test(cx), cx)
});
buffer_a
.condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 1)
@@ -33,6 +33,7 @@ clock = { path = "../clock" }
crossbeam-channel = "0.5.0"
ctor = "0.1.20"
dirs = "3.0"
+editor = { path = "../editor" }
easy-parallel = "3.1.0"
fsevent = { path = "../fsevent" }
futures = "0.3"
@@ -70,7 +71,7 @@ tree-sitter = "0.19.5"
tree-sitter-rust = "0.19.0"
url = "2.2"
util = { path = "../util" }
-worktree = { path = "../worktree" }
+worktree = { path = "../worktree" }
rpc = { path = "../rpc" }
[dev-dependencies]
@@ -80,6 +81,7 @@ serde_json = { version = "1.0.64", features = ["preserve_order"] }
tempdir = { version = "0.3.7" }
unindent = "0.1.7"
buffer = { path = "../buffer", features = ["test-support"] }
+editor = { path = "../editor", features = ["test-support"] }
gpui = { path = "../gpui", features = ["test-support"] }
rpc_client = { path = "../rpc_client", features = ["test-support"] }
util = { path = "../util", features = ["test-support"] }
@@ -208,7 +208,7 @@ padding = { left = 16, right = 16, top = 8, bottom = 4 }
[selector.item]
text = "$text.1"
-highlight_text = { extends = "$text.base", color = "$syntax.keyword.color", weight = "$syntax.keyword.weight" }
+highlight_text = { extends = "$text.base", color = "$editor.syntax.keyword.color", weight = "$editor.syntax.keyword.weight" }
padding = { left = 16, right = 16, top = 4, bottom = 4 }
corner_radius = 6
@@ -26,7 +26,7 @@ guests = [
{ selection = "#3B874B33", cursor = "#3B874B" },
{ selection = "#BD7CB433", cursor = "#BD7CB4" },
{ selection = "#EE823133", cursor = "#EE8231" },
- { selection = "#5A2B9233", cursor = "#5A2B92" }
+ { selection = "#5A2B9233", cursor = "#5A2B92" },
]
[status]
@@ -39,7 +39,7 @@ bad = "#b7372e"
active_line = "#00000033"
hover = "#00000033"
-[syntax]
+[editor.syntax]
keyword = { color = "#0086c0", weight = "bold" }
function = "#dcdcaa"
string = "#cb8f77"
@@ -26,7 +26,7 @@ guests = [
{ selection = "#3B874B33", cursor = "#3B874B" },
{ selection = "#BD7CB433", cursor = "#BD7CB4" },
{ selection = "#EE823133", cursor = "#EE8231" },
- { selection = "#5A2B9233", cursor = "#5A2B92" }
+ { selection = "#5A2B9233", cursor = "#5A2B92" },
]
[status]
@@ -39,7 +39,7 @@ bad = "#b7372e"
active_line = "#00000022"
hover = "#00000033"
-[syntax]
+[editor.syntax]
keyword = { color = "#0086c0", weight = "bold" }
function = "#dcdcaa"
string = "#cb8f77"
@@ -26,7 +26,7 @@ guests = [
{ selection = "#3B874B33", cursor = "#3B874B" },
{ selection = "#BD7CB433", cursor = "#BD7CB4" },
{ selection = "#EE823133", cursor = "#EE8231" },
- { selection = "#5A2B9233", cursor = "#5A2B92" }
+ { selection = "#5A2B9233", cursor = "#5A2B92" },
]
[status]
@@ -39,7 +39,7 @@ bad = "#b7372e"
active_line = "#00000008"
hover = "#0000000D"
-[syntax]
+[editor.syntax]
keyword = { color = "#0000fa", weight = "bold" }
function = "#795e26"
string = "#a82121"
@@ -1,10 +1,8 @@
-use std::sync::Arc;
-
use crate::{
channel::{Channel, ChannelEvent, ChannelList, ChannelMessage},
- editor::Editor,
theme, Settings,
};
+use editor::{Editor, EditorSettings};
use gpui::{
action,
elements::*,
@@ -16,6 +14,7 @@ use gpui::{
};
use postage::{prelude::Stream, watch};
use rpc_client as rpc;
+use std::sync::Arc;
use time::{OffsetDateTime, UtcOffset};
use util::{ResultExt, TryFutureExt};
@@ -55,10 +54,15 @@ impl ChatPanel {
let input_editor = cx.add_view(|cx| {
Editor::auto_height(
4,
- settings.clone(),
{
let settings = settings.clone();
- move |_| settings.borrow().theme.chat_panel.input_editor.as_editor()
+ move |_| {
+ let settings = settings.borrow();
+ EditorSettings {
+ tab_size: settings.tab_size,
+ style: settings.theme.chat_panel.input_editor.as_editor(),
+ }
+ }
},
cx,
)
@@ -1,10 +1,10 @@
use crate::{
- editor::{self, Editor},
fuzzy::PathMatch,
project::{Project, ProjectPath},
settings::Settings,
workspace::Workspace,
};
+use editor::{self, Editor, EditorSettings};
use gpui::{
action,
elements::*,
@@ -271,10 +271,15 @@ impl FileFinder {
let query_editor = cx.add_view(|cx| {
Editor::single_line(
- settings.clone(),
{
let settings = settings.clone();
- move |_| settings.borrow().theme.selector.input_editor.as_editor()
+ move |_| {
+ let settings = settings.borrow();
+ EditorSettings {
+ style: settings.theme.selector.input_editor.as_editor(),
+ tab_size: settings.tab_size,
+ }
+ }
},
cx,
)
@@ -420,11 +425,8 @@ impl FileFinder {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{
- editor::{self, Insert},
- test::test_app_state,
- workspace::Workspace,
- };
+ use crate::{test::test_app_state, workspace::Workspace};
+ use editor::{self, Insert};
use serde_json::json;
use std::path::PathBuf;
use worktree::fs::FakeFs;
@@ -1,7 +1,6 @@
pub mod assets;
pub mod channel;
pub mod chat_panel;
-pub mod editor;
pub mod file_finder;
mod fuzzy;
pub mod http;
@@ -21,6 +20,7 @@ pub mod workspace;
pub use buffer;
use buffer::LanguageRegistry;
use channel::ChannelList;
+pub use editor;
use gpui::{action, keymap::Binding, ModelHandle};
use parking_lot::Mutex;
use postage::watch;
@@ -33,7 +33,7 @@ fn main() {
let themes = settings::ThemeRegistry::new(Assets, app.font_cache());
let (settings_tx, settings) = settings::channel(&app.font_cache(), &themes).unwrap();
let languages = Arc::new(language::build_language_registry());
- languages.set_theme(&settings.borrow().theme.syntax);
+ languages.set_theme(&settings.borrow().theme.editor.syntax);
app.run(move |cx| {
let rpc = rpc::Client::new();
@@ -4,8 +4,6 @@ use std::sync::Arc;
#[cfg(target_os = "macos")]
pub fn menus(state: &Arc<AppState>) -> Vec<Menu<'static>> {
- use crate::editor;
-
vec![
Menu {
name: "Zed",
@@ -10,11 +10,10 @@ use crate::{
use anyhow::Result;
use buffer::LanguageRegistry;
use futures::{future::BoxFuture, Future};
-use gpui::{Entity, ModelHandle, MutableAppContext};
+use gpui::MutableAppContext;
use parking_lot::Mutex;
use rpc_client as rpc;
-use smol::channel;
-use std::{fmt, marker::PhantomData, sync::Arc};
+use std::{fmt, sync::Arc};
use worktree::fs::FakeFs;
#[cfg(test)]
@@ -23,19 +22,6 @@ fn init_logger() {
env_logger::init();
}
-pub fn sample_text(rows: usize, cols: usize) -> String {
- let mut text = String::new();
- for row in 0..rows {
- let c: char = ('a' as u32 + row as u32) as u8 as char;
- let mut line = c.to_string().repeat(cols);
- if row < rows - 1 {
- line.push('\n');
- }
- text += &line;
- }
- text
-}
-
pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
let (settings_tx, settings) = settings::test(cx);
let mut languages = LanguageRegistry::new();
@@ -56,29 +42,6 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
})
}
-pub struct Observer<T>(PhantomData<T>);
-
-impl<T: 'static> Entity for Observer<T> {
- type Event = ();
-}
-
-impl<T: Entity> Observer<T> {
- pub fn new(
- handle: &ModelHandle<T>,
- cx: &mut gpui::TestAppContext,
- ) -> (ModelHandle<Self>, channel::Receiver<()>) {
- let (notify_tx, notify_rx) = channel::unbounded();
- let observer = cx.add_model(|cx| {
- cx.observe(handle, move |_, _, _| {
- let _ = notify_tx.try_send(());
- })
- .detach();
- Observer(PhantomData)
- });
- (observer, notify_rx)
- }
-}
-
pub struct FakeHttpClient {
handler:
Box<dyn 'static + Send + Sync + Fn(Request) -> BoxFuture<'static, Result<ServerResponse>>>,
@@ -1,8 +1,7 @@
mod resolution;
mod theme_registry;
-use crate::editor::{EditorStyle, SelectionStyle};
-use buffer::SyntaxTheme;
+use editor::{EditorStyle, SelectionStyle};
use gpui::{
color::Color,
elements::{ContainerStyle, ImageStyle, LabelStyle},
@@ -25,7 +24,6 @@ pub struct Theme {
pub project_panel: ProjectPanel,
pub selector: Selector,
pub editor: EditorStyle,
- pub syntax: SyntaxTheme,
}
#[derive(Deserialize)]
@@ -228,6 +226,7 @@ impl InputEditorStyle {
line_number: Default::default(),
line_number_active: Default::default(),
guest_selections: Default::default(),
+ syntax: Default::default(),
}
}
}
@@ -1,12 +1,12 @@
use std::{cmp, sync::Arc};
use crate::{
- editor::{self, Editor},
fuzzy::{match_strings, StringMatch, StringMatchCandidate},
settings::ThemeRegistry,
workspace::Workspace,
AppState, Settings,
};
+use editor::{self, Editor, EditorSettings};
use gpui::{
action,
elements::*,
@@ -59,10 +59,15 @@ impl ThemeSelector {
) -> Self {
let query_editor = cx.add_view(|cx| {
Editor::single_line(
- settings.clone(),
{
let settings = settings.clone();
- move |_| settings.borrow().theme.selector.input_editor.as_editor()
+ move |_| {
+ let settings = settings.borrow();
+ EditorSettings {
+ tab_size: settings.tab_size,
+ style: settings.theme.selector.input_editor.as_editor(),
+ }
+ }
},
cx,
)
@@ -1,3 +1,4 @@
+mod items;
pub mod pane;
pub mod pane_group;
pub mod sidebar;
@@ -1156,11 +1157,8 @@ impl WorkspaceHandle for ViewHandle<Workspace> {
#[cfg(test)]
mod tests {
use super::*;
- use crate::{
- editor::{Editor, Insert},
- fs::FakeFs,
- test::test_app_state,
- };
+ use crate::{fs::FakeFs, test::test_app_state};
+ use editor::{Editor, Insert};
use serde_json::json;
use std::collections::HashSet;
use util::test::temp_tree;
@@ -0,0 +1,153 @@
+use super::{Item, ItemView};
+use crate::{project::ProjectPath, Settings};
+use anyhow::Result;
+use buffer::{Buffer, File as _};
+use editor::{Editor, EditorSettings, Event};
+use gpui::{fonts::TextStyle, AppContext, ModelHandle, Task, ViewContext};
+use postage::watch;
+use std::path::Path;
+use worktree::Worktree;
+
+impl Item for Buffer {
+ type View = Editor;
+
+ fn build_view(
+ handle: ModelHandle<Self>,
+ settings: watch::Receiver<Settings>,
+ cx: &mut ViewContext<Self::View>,
+ ) -> Self::View {
+ Editor::for_buffer(
+ handle,
+ move |cx| {
+ let settings = settings.borrow();
+ let font_cache = cx.font_cache();
+ let font_family_id = settings.buffer_font_family;
+ let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
+ let font_properties = Default::default();
+ let font_id = font_cache
+ .select_font(font_family_id, &font_properties)
+ .unwrap();
+ let font_size = settings.buffer_font_size;
+
+ let mut theme = settings.theme.editor.clone();
+ theme.text = TextStyle {
+ color: theme.text.color,
+ font_family_name,
+ font_family_id,
+ font_id,
+ font_size,
+ font_properties,
+ underline: false,
+ };
+ EditorSettings {
+ tab_size: settings.tab_size,
+ style: theme,
+ }
+ },
+ cx,
+ )
+ }
+
+ fn project_path(&self) -> Option<ProjectPath> {
+ self.file().map(|f| ProjectPath {
+ worktree_id: f.worktree_id(),
+ path: f.path().clone(),
+ })
+ }
+}
+
+impl ItemView for Editor {
+ fn should_activate_item_on_event(event: &Event) -> bool {
+ matches!(event, Event::Activate)
+ }
+
+ fn should_close_item_on_event(event: &Event) -> bool {
+ matches!(event, Event::Closed)
+ }
+
+ fn should_update_tab_on_event(event: &Event) -> bool {
+ matches!(
+ event,
+ Event::Saved | Event::Dirtied | Event::FileHandleChanged
+ )
+ }
+
+ fn title(&self, cx: &AppContext) -> String {
+ let filename = self
+ .buffer()
+ .read(cx)
+ .file()
+ .and_then(|file| file.file_name(cx));
+ if let Some(name) = filename {
+ name.to_string_lossy().into()
+ } else {
+ "untitled".into()
+ }
+ }
+
+ fn project_path(&self, cx: &AppContext) -> Option<ProjectPath> {
+ self.buffer().read(cx).file().map(|file| ProjectPath {
+ worktree_id: file.worktree_id(),
+ path: file.path().clone(),
+ })
+ }
+
+ fn clone_on_split(&self, cx: &mut ViewContext<Self>) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ Some(self.clone(cx))
+ }
+
+ fn save(&mut self, cx: &mut ViewContext<Self>) -> Result<Task<Result<()>>> {
+ let save = self.buffer().update(cx, |b, cx| b.save(cx))?;
+ Ok(cx.spawn(|_, _| async move {
+ save.await?;
+ Ok(())
+ }))
+ }
+
+ fn save_as(
+ &mut self,
+ worktree: ModelHandle<Worktree>,
+ path: &Path,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<()>> {
+ self.buffer().update(cx, |buffer, cx| {
+ let handle = cx.handle();
+ let text = buffer.as_rope().clone();
+ let version = buffer.version();
+
+ let save_as = worktree.update(cx, |worktree, cx| {
+ worktree
+ .as_local_mut()
+ .unwrap()
+ .save_buffer_as(handle, path, text, cx)
+ });
+
+ cx.spawn(|buffer, mut cx| async move {
+ save_as.await.map(|new_file| {
+ let language = worktree.read_with(&cx, |worktree, cx| {
+ worktree
+ .languages()
+ .select_language(new_file.full_path(cx))
+ .cloned()
+ });
+
+ buffer.update(&mut cx, |buffer, cx| {
+ buffer.did_save(version, new_file.mtime, Some(Box::new(new_file)), cx);
+ buffer.set_language(language, cx);
+ });
+ })
+ })
+ })
+ }
+
+ fn is_dirty(&self, cx: &AppContext) -> bool {
+ self.buffer().read(cx).is_dirty()
+ }
+
+ fn has_conflict(&self, cx: &AppContext) -> bool {
+ self.buffer().read(cx).has_conflict()
+ }
+}