mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Move editor into its own crate
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
d5b60ad124
commit
1d97f08901
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -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)>,
|
||||
}
|
||||
|
29
crates/editor/Cargo.toml
Normal file
29
crates/editor/Cargo.toml
Normal file
@ -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) {
|
39
crates/editor/src/test.rs
Normal file
39
crates/editor/src/test.rs
Normal file
@ -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;
|
||||
|
153
crates/zed/src/workspace/items.rs
Normal file
153
crates/zed/src/workspace/items.rs
Normal file
@ -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()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user