mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Merge pull request #149 from zed-industries/editor-style
Specify UI editor fonts via the theme instead of the settings
This commit is contained in:
commit
ae9251a783
14 changed files with 730 additions and 631 deletions
|
|
@ -2335,6 +2335,16 @@ impl<V: View> ReadModel for RenderContext<'_, V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: View> UpdateModel for RenderContext<'_, V> {
|
||||
fn update_model<T, F, S>(&mut self, handle: &ModelHandle<T>, update: F) -> S
|
||||
where
|
||||
T: Entity,
|
||||
F: FnOnce(&mut T, &mut ModelContext<T>) -> S,
|
||||
{
|
||||
self.app.update_model(handle, update)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> AsRef<AppContext> for ViewContext<'_, M> {
|
||||
fn as_ref(&self) -> &AppContext {
|
||||
&self.app.cx
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use std::{
|
|||
pub struct FamilyId(usize);
|
||||
|
||||
struct Family {
|
||||
name: String,
|
||||
name: Arc<str>,
|
||||
font_ids: Vec<FontId>,
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ impl FontCache {
|
|||
}))
|
||||
}
|
||||
|
||||
pub fn family_name(&self, family_id: FamilyId) -> Result<String> {
|
||||
pub fn family_name(&self, family_id: FamilyId) -> Result<Arc<str>> {
|
||||
self.0
|
||||
.read()
|
||||
.families
|
||||
|
|
@ -62,7 +62,7 @@ impl FontCache {
|
|||
for name in names {
|
||||
let state = self.0.upgradable_read();
|
||||
|
||||
if let Some(ix) = state.families.iter().position(|f| f.name == *name) {
|
||||
if let Some(ix) = state.families.iter().position(|f| f.name.as_ref() == *name) {
|
||||
return Ok(FamilyId(ix));
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ impl FontCache {
|
|||
}
|
||||
|
||||
state.families.push(Family {
|
||||
name: String::from(*name),
|
||||
name: Arc::from(*name),
|
||||
font_ids,
|
||||
});
|
||||
return Ok(family_id);
|
||||
|
|
@ -141,8 +141,8 @@ impl FontCache {
|
|||
|
||||
pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F {
|
||||
let bounding_box = self.metric(font_id, |m| m.bounding_box);
|
||||
let width = self.scale_metric(bounding_box.width(), font_id, font_size);
|
||||
let height = self.scale_metric(bounding_box.height(), font_id, font_size);
|
||||
let width = bounding_box.width() * self.em_scale(font_id, font_size);
|
||||
let height = bounding_box.height() * self.em_scale(font_id, font_size);
|
||||
vec2f(width, height)
|
||||
}
|
||||
|
||||
|
|
@ -154,28 +154,28 @@ impl FontCache {
|
|||
glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap();
|
||||
bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap();
|
||||
}
|
||||
self.scale_metric(bounds.width(), font_id, font_size)
|
||||
bounds.width() * self.em_scale(font_id, font_size)
|
||||
}
|
||||
|
||||
pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 {
|
||||
let height = self.metric(font_id, |m| m.bounding_box.height());
|
||||
self.scale_metric(height, font_id, font_size)
|
||||
(height * self.em_scale(font_id, font_size)).ceil()
|
||||
}
|
||||
|
||||
pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 {
|
||||
self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size)
|
||||
self.metric(font_id, |m| m.cap_height) * self.em_scale(font_id, font_size)
|
||||
}
|
||||
|
||||
pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 {
|
||||
self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size)
|
||||
self.metric(font_id, |m| m.ascent) * self.em_scale(font_id, font_size)
|
||||
}
|
||||
|
||||
pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 {
|
||||
self.scale_metric(self.metric(font_id, |m| -m.descent), font_id, font_size)
|
||||
self.metric(font_id, |m| -m.descent) * self.em_scale(font_id, font_size)
|
||||
}
|
||||
|
||||
pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 {
|
||||
metric * font_size / self.metric(font_id, |m| m.units_per_em as f32)
|
||||
pub fn em_scale(&self, font_id: FontId, font_size: f32) -> f32 {
|
||||
font_size / self.metric(font_id, |m| m.units_per_em as f32)
|
||||
}
|
||||
|
||||
pub fn line_wrapper(self: &Arc<Self>, font_id: FontId, font_size: f32) -> LineWrapperHandle {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
color::Color,
|
||||
font_cache::FamilyId,
|
||||
json::{json, ToJson},
|
||||
text_layout::RunStyle,
|
||||
FontCache,
|
||||
|
|
@ -22,6 +23,7 @@ pub type GlyphId = u32;
|
|||
pub struct TextStyle {
|
||||
pub color: Color,
|
||||
pub font_family_name: Arc<str>,
|
||||
pub font_family_id: FamilyId,
|
||||
pub font_id: FontId,
|
||||
pub font_size: f32,
|
||||
pub font_properties: Properties,
|
||||
|
|
@ -85,11 +87,12 @@ impl TextStyle {
|
|||
font_cache: &FontCache,
|
||||
) -> anyhow::Result<Self> {
|
||||
let font_family_name = font_family_name.into();
|
||||
let family_id = font_cache.load_family(&[&font_family_name])?;
|
||||
let font_id = font_cache.select_font(family_id, &font_properties)?;
|
||||
let font_family_id = font_cache.load_family(&[&font_family_name])?;
|
||||
let font_id = font_cache.select_font(font_family_id, &font_properties)?;
|
||||
Ok(Self {
|
||||
color,
|
||||
font_family_name,
|
||||
font_family_id,
|
||||
font_id,
|
||||
font_size,
|
||||
font_properties,
|
||||
|
|
@ -124,6 +127,32 @@ impl TextStyle {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn line_height(&self, font_cache: &FontCache) -> f32 {
|
||||
font_cache.line_height(self.font_id, self.font_size)
|
||||
}
|
||||
|
||||
pub fn em_width(&self, font_cache: &FontCache) -> f32 {
|
||||
font_cache.em_width(self.font_id, self.font_size)
|
||||
}
|
||||
|
||||
pub fn descent(&self, font_cache: &FontCache) -> f32 {
|
||||
font_cache.metric(self.font_id, |m| m.descent) * self.em_scale(font_cache)
|
||||
}
|
||||
|
||||
fn em_scale(&self, font_cache: &FontCache) -> f32 {
|
||||
font_cache.em_scale(self.font_id, self.font_size)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TextStyle> for HighlightStyle {
|
||||
fn from(other: TextStyle) -> Self {
|
||||
Self {
|
||||
color: other.color,
|
||||
font_properties: other.font_properties,
|
||||
underline: other.underline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HighlightStyle {
|
||||
|
|
|
|||
|
|
@ -1037,7 +1037,7 @@ mod tests {
|
|||
};
|
||||
use zed::{
|
||||
channel::{Channel, ChannelDetails, ChannelList},
|
||||
editor::{Editor, Insert},
|
||||
editor::{Editor, EditorStyle, Insert},
|
||||
fs::{FakeFs, Fs as _},
|
||||
language::LanguageRegistry,
|
||||
rpc::{self, Client, Credentials, EstablishConnectionError},
|
||||
|
|
@ -1121,7 +1121,14 @@ mod tests {
|
|||
.unwrap();
|
||||
|
||||
// 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));
|
||||
let editor_b = cx_b.add_view(window_b, |cx| {
|
||||
Editor::for_buffer(
|
||||
buffer_b,
|
||||
settings,
|
||||
|cx| EditorStyle::test(cx.font_cache()),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
buffer_a
|
||||
.condition(&cx_a, |buffer, _| buffer.selection_sets().count() == 1)
|
||||
.await;
|
||||
|
|
|
|||
|
|
@ -107,8 +107,8 @@ shadow = { offset = [0, 2], blur = 16, color = "$shadow.0" }
|
|||
background = "$surface.1"
|
||||
corner_radius = 6
|
||||
padding = { left = 8, right = 8, top = 7, bottom = 7 }
|
||||
text = "$text.0.color"
|
||||
placeholder_text = "$text.2.color"
|
||||
text = "$text.0"
|
||||
placeholder_text = "$text.2"
|
||||
selection = "$selection.host"
|
||||
border = { width = 1, color = "$border.0" }
|
||||
|
||||
|
|
@ -132,8 +132,8 @@ border = { width = 1, color = "$border.0" }
|
|||
background = "$surface.1"
|
||||
corner_radius = 6
|
||||
padding = { left = 16, right = 16, top = 7, bottom = 7 }
|
||||
text = "$text.0.color"
|
||||
placeholder_text = "$text.2.color"
|
||||
text = "$text.0"
|
||||
placeholder_text = "$text.2"
|
||||
selection = "$selection.host"
|
||||
border = { width = 1, color = "$border.0" }
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ background = "$state.hover"
|
|||
text = "$text.0"
|
||||
|
||||
[editor]
|
||||
text = "$text.1.color"
|
||||
text = "$text.1"
|
||||
background = "$surface.1"
|
||||
gutter_background = "$surface.1"
|
||||
active_line_background = "$state.active_line"
|
||||
|
|
|
|||
|
|
@ -54,10 +54,15 @@ impl ChatPanel {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let input_editor = cx.add_view(|cx| {
|
||||
Editor::auto_height(4, settings.clone(), cx).with_style({
|
||||
let settings = settings.clone();
|
||||
move |_| settings.borrow().theme.chat_panel.input_editor.as_editor()
|
||||
})
|
||||
Editor::auto_height(
|
||||
4,
|
||||
settings.clone(),
|
||||
{
|
||||
let settings = settings.clone();
|
||||
move |_| settings.borrow().theme.chat_panel.input_editor.as_editor()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let channel_select = cx.add_view(|cx| {
|
||||
let channel_list = channel_list.clone();
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ mod element;
|
|||
pub mod movement;
|
||||
|
||||
use crate::{
|
||||
settings::{HighlightId, Settings},
|
||||
theme::{EditorStyle, Theme},
|
||||
settings::Settings,
|
||||
theme::Theme,
|
||||
time::ReplicaId,
|
||||
util::{post_inc, Bias},
|
||||
workspace,
|
||||
|
|
@ -17,15 +17,9 @@ pub use display_map::DisplayPoint;
|
|||
use display_map::*;
|
||||
pub use element::*;
|
||||
use gpui::{
|
||||
action,
|
||||
color::Color,
|
||||
font_cache::FamilyId,
|
||||
fonts::Properties as FontProperties,
|
||||
geometry::vector::Vector2F,
|
||||
keymap::Binding,
|
||||
text_layout::{self, RunStyle},
|
||||
AppContext, ClipboardItem, Element, ElementBox, Entity, FontCache, ModelHandle,
|
||||
MutableAppContext, RenderContext, Task, TextLayoutCache, View, ViewContext, WeakViewHandle,
|
||||
action, color::Color, fonts::TextStyle, geometry::vector::Vector2F, keymap::Binding,
|
||||
text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
|
||||
MutableAppContext, RenderContext, Task, View, ViewContext, WeakViewHandle,
|
||||
};
|
||||
use postage::watch;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -34,8 +28,6 @@ use smol::Timer;
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
cmp::{self, Ordering},
|
||||
collections::BTreeMap,
|
||||
fmt::Write,
|
||||
iter::FromIterator,
|
||||
mem,
|
||||
ops::{Range, RangeInclusive},
|
||||
|
|
@ -278,6 +270,26 @@ pub enum EditorMode {
|
|||
Full,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct EditorStyle {
|
||||
pub text: TextStyle,
|
||||
#[serde(default)]
|
||||
pub placeholder_text: Option<TextStyle>,
|
||||
pub background: Color,
|
||||
pub selection: SelectionStyle,
|
||||
pub gutter_background: Color,
|
||||
pub active_line_background: Color,
|
||||
pub line_number: Color,
|
||||
pub line_number_active: Color,
|
||||
pub guest_selections: Vec<SelectionStyle>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Deserialize)]
|
||||
pub struct SelectionStyle {
|
||||
pub cursor: Color,
|
||||
pub selection: Color,
|
||||
}
|
||||
|
||||
pub struct Editor {
|
||||
handle: WeakViewHandle<Self>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
|
|
@ -290,7 +302,7 @@ pub struct Editor {
|
|||
scroll_position: Vector2F,
|
||||
scroll_top_anchor: Anchor,
|
||||
autoscroll_requested: bool,
|
||||
build_style: Option<Rc<RefCell<dyn FnMut(&mut MutableAppContext) -> EditorStyle>>>,
|
||||
build_style: Rc<RefCell<dyn FnMut(&mut MutableAppContext) -> EditorStyle>>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
focused: bool,
|
||||
cursors_visible: bool,
|
||||
|
|
@ -305,8 +317,6 @@ pub struct Snapshot {
|
|||
pub display_snapshot: DisplayMapSnapshot,
|
||||
pub placeholder_text: Option<Arc<str>>,
|
||||
pub theme: Arc<Theme>,
|
||||
pub font_family: FamilyId,
|
||||
pub font_size: f32,
|
||||
is_focused: bool,
|
||||
scroll_position: Vector2F,
|
||||
scroll_top_anchor: Anchor,
|
||||
|
|
@ -324,9 +334,13 @@ struct ClipboardSelection {
|
|||
}
|
||||
|
||||
impl Editor {
|
||||
pub fn single_line(settings: watch::Receiver<Settings>, cx: &mut ViewContext<Self>) -> Self {
|
||||
pub fn single_line(
|
||||
settings: watch::Receiver<Settings>,
|
||||
build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
|
||||
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, cx);
|
||||
let mut view = Self::for_buffer(buffer, settings, build_style, cx);
|
||||
view.mode = EditorMode::SingleLine;
|
||||
view
|
||||
}
|
||||
|
|
@ -334,10 +348,11 @@ impl Editor {
|
|||
pub fn auto_height(
|
||||
max_lines: usize,
|
||||
settings: watch::Receiver<Settings>,
|
||||
build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
|
||||
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, cx);
|
||||
let mut view = Self::for_buffer(buffer, settings, build_style, cx);
|
||||
view.mode = EditorMode::AutoHeight { max_lines };
|
||||
view
|
||||
}
|
||||
|
|
@ -345,10 +360,29 @@ impl Editor {
|
|||
pub fn for_buffer(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
build_style: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let display_map =
|
||||
cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.clone(), None, cx));
|
||||
Self::new(buffer, settings, Rc::new(RefCell::new(build_style)), cx)
|
||||
}
|
||||
|
||||
fn new(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
build_style: Rc<RefCell<dyn FnMut(&mut MutableAppContext) -> EditorStyle>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let style = build_style.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,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.observe(&buffer, Self::on_buffer_changed).detach();
|
||||
cx.subscribe(&buffer, Self::on_buffer_event).detach();
|
||||
cx.observe(&display_map, Self::on_display_map_changed)
|
||||
|
|
@ -376,7 +410,7 @@ impl Editor {
|
|||
next_selection_id,
|
||||
add_selections_state: None,
|
||||
select_larger_syntax_node_stack: Vec::new(),
|
||||
build_style: None,
|
||||
build_style,
|
||||
scroll_position: Vector2F::zero(),
|
||||
scroll_top_anchor: Anchor::min(),
|
||||
autoscroll_requested: false,
|
||||
|
|
@ -390,14 +424,6 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn with_style(
|
||||
mut self,
|
||||
f: impl 'static + FnMut(&mut MutableAppContext) -> EditorStyle,
|
||||
) -> Self {
|
||||
self.build_style = Some(Rc::new(RefCell::new(f)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
|
||||
self.buffer.read(cx).replica_id()
|
||||
}
|
||||
|
|
@ -416,8 +442,6 @@ impl Editor {
|
|||
scroll_top_anchor: self.scroll_top_anchor.clone(),
|
||||
theme: settings.theme.clone(),
|
||||
placeholder_text: self.placeholder_text.clone(),
|
||||
font_family: settings.buffer_font_family,
|
||||
font_size: settings.buffer_font_size,
|
||||
is_focused: self
|
||||
.handle
|
||||
.upgrade(cx)
|
||||
|
|
@ -2301,6 +2325,38 @@ impl Editor {
|
|||
}
|
||||
|
||||
impl Snapshot {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.display_snapshot.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_focused(&self) -> bool {
|
||||
self.is_focused
|
||||
}
|
||||
|
||||
pub fn placeholder_text(&self) -> Option<&Arc<str>> {
|
||||
self.placeholder_text.as_ref()
|
||||
}
|
||||
|
||||
pub fn buffer_row_count(&self) -> u32 {
|
||||
self.display_snapshot.buffer_row_count()
|
||||
}
|
||||
|
||||
pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
|
||||
self.display_snapshot.buffer_rows(start_row)
|
||||
}
|
||||
|
||||
pub fn highlighted_chunks_for_rows(
|
||||
&mut self,
|
||||
display_rows: Range<u32>,
|
||||
) -> display_map::HighlightedChunks {
|
||||
self.display_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,
|
||||
|
|
@ -2321,243 +2377,8 @@ impl Snapshot {
|
|||
self.display_snapshot.line_len(display_row)
|
||||
}
|
||||
|
||||
pub fn font_ascent(&self, font_cache: &FontCache) -> f32 {
|
||||
let font_id = font_cache.default_font(self.font_family);
|
||||
let ascent = font_cache.metric(font_id, |m| m.ascent);
|
||||
font_cache.scale_metric(ascent, font_id, self.font_size)
|
||||
}
|
||||
|
||||
pub fn font_descent(&self, font_cache: &FontCache) -> f32 {
|
||||
let font_id = font_cache.default_font(self.font_family);
|
||||
let descent = font_cache.metric(font_id, |m| m.descent);
|
||||
font_cache.scale_metric(descent, font_id, self.font_size)
|
||||
}
|
||||
|
||||
pub fn line_height(&self, font_cache: &FontCache) -> f32 {
|
||||
let font_id = font_cache.default_font(self.font_family);
|
||||
font_cache.line_height(font_id, self.font_size).ceil()
|
||||
}
|
||||
|
||||
pub fn em_width(&self, font_cache: &FontCache) -> f32 {
|
||||
let font_id = font_cache.default_font(self.font_family);
|
||||
font_cache.em_width(font_id, self.font_size)
|
||||
}
|
||||
|
||||
// TODO: Can we make this not return a result?
|
||||
pub fn max_line_number_width(
|
||||
&self,
|
||||
font_cache: &FontCache,
|
||||
layout_cache: &TextLayoutCache,
|
||||
) -> Result<f32> {
|
||||
let font_size = self.font_size;
|
||||
let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?;
|
||||
let digit_count = (self.display_snapshot.buffer_row_count() as f32)
|
||||
.log10()
|
||||
.floor() as usize
|
||||
+ 1;
|
||||
|
||||
Ok(layout_cache
|
||||
.layout_str(
|
||||
"1".repeat(digit_count).as_str(),
|
||||
font_size,
|
||||
&[(
|
||||
digit_count,
|
||||
RunStyle {
|
||||
font_id,
|
||||
color: Color::black(),
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)
|
||||
.width())
|
||||
}
|
||||
|
||||
pub fn layout_line_numbers(
|
||||
&self,
|
||||
rows: Range<u32>,
|
||||
active_rows: &BTreeMap<u32, bool>,
|
||||
font_cache: &FontCache,
|
||||
layout_cache: &TextLayoutCache,
|
||||
theme: &Theme,
|
||||
) -> Result<Vec<Option<text_layout::Line>>> {
|
||||
let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?;
|
||||
|
||||
let mut layouts = Vec::with_capacity(rows.len());
|
||||
let mut line_number = String::new();
|
||||
for (ix, (buffer_row, soft_wrapped)) in self
|
||||
.display_snapshot
|
||||
.buffer_rows(rows.start)
|
||||
.take((rows.end - rows.start) as usize)
|
||||
.enumerate()
|
||||
{
|
||||
let display_row = rows.start + ix as u32;
|
||||
let color = if active_rows.contains_key(&display_row) {
|
||||
theme.editor.line_number_active
|
||||
} else {
|
||||
theme.editor.line_number
|
||||
};
|
||||
if soft_wrapped {
|
||||
layouts.push(None);
|
||||
} else {
|
||||
line_number.clear();
|
||||
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
|
||||
layouts.push(Some(layout_cache.layout_str(
|
||||
&line_number,
|
||||
self.font_size,
|
||||
&[(
|
||||
line_number.len(),
|
||||
RunStyle {
|
||||
font_id,
|
||||
color,
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(layouts)
|
||||
}
|
||||
|
||||
pub fn layout_lines(
|
||||
&mut self,
|
||||
mut rows: Range<u32>,
|
||||
style: &EditorStyle,
|
||||
font_cache: &FontCache,
|
||||
layout_cache: &TextLayoutCache,
|
||||
) -> Result<Vec<text_layout::Line>> {
|
||||
rows.end = cmp::min(rows.end, self.display_snapshot.max_point().row() + 1);
|
||||
if rows.start >= rows.end {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
// When the editor is empty and unfocused, then show the placeholder.
|
||||
if self.display_snapshot.is_empty() && !self.is_focused {
|
||||
let placeholder_lines = self
|
||||
.placeholder_text
|
||||
.as_ref()
|
||||
.map_or("", AsRef::as_ref)
|
||||
.split('\n')
|
||||
.skip(rows.start as usize)
|
||||
.take(rows.len());
|
||||
let font_id = font_cache
|
||||
.select_font(self.font_family, &style.placeholder_text.font_properties)?;
|
||||
return Ok(placeholder_lines
|
||||
.into_iter()
|
||||
.map(|line| {
|
||||
layout_cache.layout_str(
|
||||
line,
|
||||
self.font_size,
|
||||
&[(
|
||||
line.len(),
|
||||
RunStyle {
|
||||
font_id,
|
||||
color: style.placeholder_text.color,
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
|
||||
let mut prev_font_properties = FontProperties::new();
|
||||
let mut prev_font_id = font_cache
|
||||
.select_font(self.font_family, &prev_font_properties)
|
||||
.unwrap();
|
||||
|
||||
let mut layouts = Vec::with_capacity(rows.len());
|
||||
let mut line = String::new();
|
||||
let mut styles = Vec::new();
|
||||
let mut row = rows.start;
|
||||
let mut line_exceeded_max_len = false;
|
||||
let chunks = self
|
||||
.display_snapshot
|
||||
.highlighted_chunks_for_rows(rows.clone());
|
||||
|
||||
'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", HighlightId::default()))) {
|
||||
for (ix, mut line_chunk) in chunk.split('\n').enumerate() {
|
||||
if ix > 0 {
|
||||
layouts.push(layout_cache.layout_str(&line, self.font_size, &styles));
|
||||
line.clear();
|
||||
styles.clear();
|
||||
row += 1;
|
||||
line_exceeded_max_len = false;
|
||||
if row == rows.end {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||
let style = self
|
||||
.theme
|
||||
.syntax
|
||||
.highlight_style(style_ix)
|
||||
.unwrap_or(style.text.clone());
|
||||
// Avoid a lookup if the font properties match the previous ones.
|
||||
let font_id = if style.font_properties == prev_font_properties {
|
||||
prev_font_id
|
||||
} else {
|
||||
font_cache.select_font(self.font_family, &style.font_properties)?
|
||||
};
|
||||
|
||||
if line.len() + line_chunk.len() > MAX_LINE_LEN {
|
||||
let mut chunk_len = MAX_LINE_LEN - line.len();
|
||||
while !line_chunk.is_char_boundary(chunk_len) {
|
||||
chunk_len -= 1;
|
||||
}
|
||||
line_chunk = &line_chunk[..chunk_len];
|
||||
line_exceeded_max_len = true;
|
||||
}
|
||||
|
||||
line.push_str(line_chunk);
|
||||
styles.push((
|
||||
line_chunk.len(),
|
||||
RunStyle {
|
||||
font_id,
|
||||
color: style.color,
|
||||
underline: style.underline,
|
||||
},
|
||||
));
|
||||
prev_font_id = font_id;
|
||||
prev_font_properties = style.font_properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(layouts)
|
||||
}
|
||||
|
||||
pub fn layout_line(
|
||||
&self,
|
||||
row: u32,
|
||||
font_cache: &FontCache,
|
||||
layout_cache: &TextLayoutCache,
|
||||
) -> Result<text_layout::Line> {
|
||||
let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?;
|
||||
|
||||
let mut line = self.display_snapshot.line(row);
|
||||
|
||||
if line.len() > MAX_LINE_LEN {
|
||||
let mut len = MAX_LINE_LEN;
|
||||
while !line.is_char_boundary(len) {
|
||||
len -= 1;
|
||||
}
|
||||
line.truncate(len);
|
||||
}
|
||||
|
||||
Ok(layout_cache.layout_str(
|
||||
&line,
|
||||
self.font_size,
|
||||
&[(
|
||||
self.display_snapshot.line_len(row) as usize,
|
||||
RunStyle {
|
||||
font_id,
|
||||
color: Color::black(),
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
))
|
||||
pub fn line(&self, display_row: u32) -> String {
|
||||
self.display_snapshot.line(display_row)
|
||||
}
|
||||
|
||||
pub fn prev_row_boundary(&self, point: DisplayPoint) -> (DisplayPoint, Point) {
|
||||
|
|
@ -2569,6 +2390,41 @@ impl Snapshot {
|
|||
}
|
||||
}
|
||||
|
||||
impl EditorStyle {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test(font_cache: &gpui::FontCache) -> Self {
|
||||
let font_family_name = Arc::from("Monaco");
|
||||
let font_properties = Default::default();
|
||||
let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(font_family_id, &font_properties)
|
||||
.unwrap();
|
||||
Self {
|
||||
text: TextStyle {
|
||||
font_family_name,
|
||||
font_family_id,
|
||||
font_id,
|
||||
font_size: 14.,
|
||||
color: Color::from_u32(0xff0000ff),
|
||||
font_properties,
|
||||
underline: false,
|
||||
},
|
||||
placeholder_text: None,
|
||||
background: Default::default(),
|
||||
gutter_background: Default::default(),
|
||||
active_line_background: Default::default(),
|
||||
line_number: Default::default(),
|
||||
line_number_active: Default::default(),
|
||||
selection: Default::default(),
|
||||
guest_selections: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn placeholder_text(&self) -> &TextStyle {
|
||||
self.placeholder_text.as_ref().unwrap_or(&self.text)
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_scroll_position(
|
||||
snapshot: &DisplayMapSnapshot,
|
||||
mut scroll_position: Vector2F,
|
||||
|
|
@ -2604,10 +2460,10 @@ impl Entity for Editor {
|
|||
|
||||
impl View for Editor {
|
||||
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
|
||||
let style = self
|
||||
.build_style
|
||||
.as_ref()
|
||||
.map_or(Default::default(), |build| (build.borrow_mut())(cx));
|
||||
let style = self.build_style.borrow_mut()(cx);
|
||||
self.display_map.update(cx, |map, cx| {
|
||||
map.set_font(style.text.font_id, style.text.font_size, cx)
|
||||
});
|
||||
EditorElement::new(self.handle.clone(), style).boxed()
|
||||
}
|
||||
|
||||
|
|
@ -2659,8 +2515,34 @@ impl workspace::Item for Buffer {
|
|||
settings: watch::Receiver<Settings>,
|
||||
cx: &mut ViewContext<Self::View>,
|
||||
) -> Self::View {
|
||||
Editor::for_buffer(handle, settings.clone(), cx)
|
||||
.with_style(move |_| settings.borrow().theme.editor.clone())
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2697,10 +2579,14 @@ impl workspace::ItemView for Editor {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mut clone = Editor::for_buffer(self.buffer.clone(), self.settings.clone(), cx);
|
||||
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();
|
||||
clone.build_style = self.build_style.clone();
|
||||
Some(clone)
|
||||
}
|
||||
|
||||
|
|
@ -2742,9 +2628,8 @@ mod tests {
|
|||
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 (_, editor) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, editor) =
|
||||
cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
|
||||
editor.update(cx, |view, cx| {
|
||||
view.begin_selection(DisplayPoint::new(2, 2), false, cx);
|
||||
|
|
@ -2810,9 +2695,7 @@ mod tests {
|
|||
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 (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
view.begin_selection(DisplayPoint::new(2, 2), false, cx);
|
||||
|
|
@ -2844,9 +2727,7 @@ mod tests {
|
|||
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 (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
view.begin_selection(DisplayPoint::new(3, 4), false, cx);
|
||||
|
|
@ -2882,33 +2763,6 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_layout_line_numbers(cx: &mut gpui::MutableAppContext) {
|
||||
let layout_cache = TextLayoutCache::new(cx.platform().fonts());
|
||||
let font_cache = cx.font_cache().clone();
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
||||
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings.clone(), cx)
|
||||
});
|
||||
|
||||
let layouts = editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.snapshot(cx)
|
||||
.layout_line_numbers(
|
||||
0..6,
|
||||
&Default::default(),
|
||||
&font_cache,
|
||||
&layout_cache,
|
||||
&settings.borrow().theme,
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
assert_eq!(layouts.len(), 6);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_fold(cx: &mut gpui::MutableAppContext) {
|
||||
let buffer = cx.add_model(|cx| {
|
||||
|
|
@ -2937,7 +2791,7 @@ mod tests {
|
|||
});
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
});
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
|
|
@ -3005,7 +2859,7 @@ mod tests {
|
|||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
});
|
||||
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
|
|
@ -3082,7 +2936,7 @@ mod tests {
|
|||
let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx));
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
});
|
||||
|
||||
assert_eq!('ⓐ'.len_utf8(), 3);
|
||||
|
|
@ -3140,7 +2994,7 @@ mod tests {
|
|||
let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx));
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
});
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx)
|
||||
|
|
@ -3170,9 +3024,7 @@ mod tests {
|
|||
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 (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
|
|
@ -3315,9 +3167,7 @@ mod tests {
|
|||
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 (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
|
|
@ -3509,9 +3359,7 @@ mod tests {
|
|||
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 (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, 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);
|
||||
|
|
@ -3572,7 +3420,7 @@ mod tests {
|
|||
});
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
});
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
|
|
@ -3608,7 +3456,7 @@ mod tests {
|
|||
});
|
||||
let settings = settings::test(&cx).1;
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
});
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
|
|
@ -3637,9 +3485,7 @@ mod tests {
|
|||
fn test_delete_line(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
|
|
@ -3663,9 +3509,7 @@ mod tests {
|
|||
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx)
|
||||
.unwrap();
|
||||
|
|
@ -3682,9 +3526,7 @@ mod tests {
|
|||
fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
|
|
@ -3711,9 +3553,7 @@ mod tests {
|
|||
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
|
|
@ -3739,9 +3579,7 @@ mod tests {
|
|||
fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(10, 5), cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.fold_ranges(
|
||||
vec![
|
||||
|
|
@ -3840,7 +3678,7 @@ mod tests {
|
|||
let settings = settings::test(&cx).1;
|
||||
let view = cx
|
||||
.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||
build_editor(buffer.clone(), settings, cx)
|
||||
})
|
||||
.1;
|
||||
|
||||
|
|
@ -3973,9 +3811,7 @@ mod tests {
|
|||
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 (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_all(&SelectAll, cx);
|
||||
assert_eq!(
|
||||
|
|
@ -3989,9 +3825,7 @@ mod tests {
|
|||
fn test_select_line(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 5), cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
|
|
@ -4037,9 +3871,7 @@ mod tests {
|
|||
fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(9, 5), cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
view.update(cx, |view, cx| {
|
||||
view.fold_ranges(
|
||||
vec![
|
||||
|
|
@ -4107,9 +3939,7 @@ mod tests {
|
|||
fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = settings::test(&cx).1;
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", cx));
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||
Editor::for_buffer(buffer, settings, cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx)
|
||||
|
|
@ -4295,7 +4125,7 @@ mod tests {
|
|||
let history = History::new(text.into());
|
||||
Buffer::from_history(0, history, None, lang.cloned(), cx)
|
||||
});
|
||||
let (_, view) = cx.add_window(|cx| Editor::for_buffer(buffer, settings.clone(), cx));
|
||||
let (_, view) = cx.add_window(|cx| build_editor(buffer, settings.clone(), cx));
|
||||
view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing())
|
||||
.await;
|
||||
|
||||
|
|
@ -4433,6 +4263,40 @@ mod tests {
|
|||
let point = DisplayPoint::new(row as u32, column as u32);
|
||||
point..point
|
||||
}
|
||||
|
||||
fn build_editor(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
trait RangeExt<T> {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@ mod fold_map;
|
|||
mod tab_map;
|
||||
mod wrap_map;
|
||||
|
||||
use super::{buffer, Anchor, Bias, Buffer, Point, Settings, ToOffset, ToPoint};
|
||||
use super::{buffer, Anchor, Bias, Buffer, Point, ToOffset, ToPoint};
|
||||
use fold_map::FoldMap;
|
||||
use gpui::{Entity, ModelContext, ModelHandle};
|
||||
use postage::watch;
|
||||
use gpui::{fonts::FontId, Entity, ModelContext, ModelHandle};
|
||||
use std::ops::Range;
|
||||
use tab_map::TabMap;
|
||||
pub use wrap_map::BufferRows;
|
||||
use wrap_map::WrapMap;
|
||||
pub use wrap_map::{BufferRows, HighlightedChunks};
|
||||
|
||||
pub struct DisplayMap {
|
||||
buffer: ModelHandle<Buffer>,
|
||||
|
|
@ -25,13 +24,16 @@ impl Entity for DisplayMap {
|
|||
impl DisplayMap {
|
||||
pub fn new(
|
||||
buffer: ModelHandle<Buffer>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
tab_size: usize,
|
||||
font_id: FontId,
|
||||
font_size: f32,
|
||||
wrap_width: Option<f32>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let (fold_map, snapshot) = FoldMap::new(buffer.clone(), cx);
|
||||
let (tab_map, snapshot) = TabMap::new(snapshot, settings.borrow().tab_size);
|
||||
let wrap_map = cx.add_model(|cx| WrapMap::new(snapshot, settings, wrap_width, cx));
|
||||
let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
|
||||
let wrap_map =
|
||||
cx.add_model(|cx| WrapMap::new(snapshot, font_id, font_size, wrap_width, cx));
|
||||
cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
|
||||
DisplayMap {
|
||||
buffer,
|
||||
|
|
@ -85,6 +87,11 @@ impl DisplayMap {
|
|||
.update(cx, |map, cx| map.sync(snapshot, edits, cx));
|
||||
}
|
||||
|
||||
pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) {
|
||||
self.wrap_map
|
||||
.update(cx, |map, cx| map.set_font(font_id, font_size, cx));
|
||||
}
|
||||
|
||||
pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
|
||||
self.wrap_map
|
||||
.update(cx, |map, cx| map.set_wrap_width(width, cx))
|
||||
|
|
@ -367,12 +374,12 @@ mod tests {
|
|||
.unwrap_or(10);
|
||||
|
||||
let font_cache = cx.font_cache().clone();
|
||||
let settings = Settings {
|
||||
tab_size: rng.gen_range(1..=4),
|
||||
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
||||
buffer_font_size: 14.0,
|
||||
..cx.read(Settings::test)
|
||||
};
|
||||
let tab_size = rng.gen_range(1..=4);
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
let max_wrap_width = 300.0;
|
||||
let mut wrap_width = if rng.gen_bool(0.1) {
|
||||
None
|
||||
|
|
@ -380,7 +387,7 @@ mod tests {
|
|||
Some(rng.gen_range(0.0..=max_wrap_width))
|
||||
};
|
||||
|
||||
log::info!("tab size: {}", settings.tab_size);
|
||||
log::info!("tab size: {}", tab_size);
|
||||
log::info!("wrap width: {:?}", wrap_width);
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
|
|
@ -388,9 +395,10 @@ mod tests {
|
|||
let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
|
||||
Buffer::new(0, text, cx)
|
||||
});
|
||||
let settings = watch::channel_with(settings).1;
|
||||
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings, wrap_width, cx));
|
||||
let map = cx.add_model(|cx| {
|
||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx)
|
||||
});
|
||||
let (_observer, notifications) = Observer::new(&map, &mut cx);
|
||||
let mut fold_count = 0;
|
||||
|
||||
|
|
@ -529,26 +537,27 @@ mod tests {
|
|||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_soft_wraps(mut cx: gpui::TestAppContext) {
|
||||
fn test_soft_wraps(cx: &mut MutableAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
let font_cache = cx.font_cache();
|
||||
|
||||
let settings = Settings {
|
||||
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
||||
buffer_font_size: 12.0,
|
||||
tab_size: 4,
|
||||
..cx.read(Settings::test)
|
||||
};
|
||||
let tab_size = 4;
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 12.0;
|
||||
let wrap_width = Some(64.);
|
||||
|
||||
let text = "one two three four five\nsix seven eight";
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text.to_string(), cx));
|
||||
let (mut settings_tx, settings_rx) = watch::channel_with(settings);
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings_rx, wrap_width, cx));
|
||||
let map = cx.add_model(|cx| {
|
||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, wrap_width, cx)
|
||||
});
|
||||
|
||||
let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx));
|
||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.chunks_at(0).collect::<String>(),
|
||||
"one two \nthree four \nfive\nsix seven \neight"
|
||||
|
|
@ -592,23 +601,21 @@ mod tests {
|
|||
(DisplayPoint::new(2, 4), SelectionGoal::Column(10))
|
||||
);
|
||||
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
let ix = buffer.text().find("seven").unwrap();
|
||||
buffer.edit(vec![ix..ix], "and ", cx);
|
||||
});
|
||||
|
||||
let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx));
|
||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.chunks_at(1).collect::<String>(),
|
||||
"three four \nfive\nsix and \nseven eight"
|
||||
);
|
||||
|
||||
// Re-wrap on font size changes
|
||||
settings_tx.borrow_mut().buffer_font_size += 3.;
|
||||
map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx));
|
||||
|
||||
map.next_notification(&mut cx).await;
|
||||
|
||||
let snapshot = map.update(&mut cx, |map, cx| map.snapshot(cx));
|
||||
let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
snapshot.chunks_at(1).collect::<String>(),
|
||||
"three \nfour five\nsix and \nseven \neight"
|
||||
|
|
@ -619,11 +626,16 @@ mod tests {
|
|||
fn test_chunks_at(cx: &mut gpui::MutableAppContext) {
|
||||
let text = sample_text(6, 6);
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
|
||||
let settings = watch::channel_with(Settings {
|
||||
tab_size: 4,
|
||||
..Settings::test(cx)
|
||||
let tab_size = 4;
|
||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = cx
|
||||
.font_cache()
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
let map = cx.add_model(|cx| {
|
||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
||||
});
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx));
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
vec![
|
||||
|
|
@ -695,13 +707,16 @@ mod tests {
|
|||
});
|
||||
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
|
||||
|
||||
let settings = cx.update(|cx| {
|
||||
watch::channel_with(Settings {
|
||||
tab_size: 2,
|
||||
..Settings::test(cx)
|
||||
})
|
||||
});
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer, settings.1, None, cx));
|
||||
let tab_size = 2;
|
||||
let font_cache = cx.font_cache();
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
|
||||
let map =
|
||||
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
|
||||
assert_eq!(
|
||||
cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
|
||||
vec![
|
||||
|
|
@ -782,15 +797,16 @@ mod tests {
|
|||
buffer.condition(&cx, |buf, _| !buf.is_parsing()).await;
|
||||
|
||||
let font_cache = cx.font_cache();
|
||||
let settings = cx.update(|cx| {
|
||||
watch::channel_with(Settings {
|
||||
tab_size: 4,
|
||||
buffer_font_family: font_cache.load_family(&["Courier"]).unwrap(),
|
||||
buffer_font_size: 16.0,
|
||||
..Settings::test(cx)
|
||||
})
|
||||
});
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer, settings.1, Some(40.0), cx));
|
||||
|
||||
let tab_size = 4;
|
||||
let family_id = font_cache.load_family(&["Courier"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 16.0;
|
||||
|
||||
let map = cx
|
||||
.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, Some(40.0), cx));
|
||||
assert_eq!(
|
||||
cx.update(|cx| highlighted_chunks(0..5, &map, &theme, cx)),
|
||||
[
|
||||
|
|
@ -825,11 +841,17 @@ mod tests {
|
|||
let text = "\n'a', 'α',\t'✋',\t'❎', '🍐'\n";
|
||||
let display_text = "\n'a', 'α', '✋', '❎', '🍐'\n";
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
|
||||
let settings = watch::channel_with(Settings {
|
||||
tab_size: 4,
|
||||
..Settings::test(cx)
|
||||
|
||||
let tab_size = 4;
|
||||
let font_cache = cx.font_cache();
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
let map = cx.add_model(|cx| {
|
||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
||||
});
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx));
|
||||
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
|
||||
assert_eq!(map.text(), display_text);
|
||||
|
|
@ -863,11 +885,17 @@ mod tests {
|
|||
fn test_tabs_with_multibyte_chars(cx: &mut gpui::MutableAppContext) {
|
||||
let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
|
||||
let settings = watch::channel_with(Settings {
|
||||
tab_size: 4,
|
||||
..Settings::test(cx)
|
||||
let tab_size = 4;
|
||||
let font_cache = cx.font_cache();
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
|
||||
let map = cx.add_model(|cx| {
|
||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
||||
});
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx));
|
||||
let map = map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(map.text(), "✅ α\nβ \n🏀β γ");
|
||||
assert_eq!(
|
||||
|
|
@ -924,11 +952,16 @@ mod tests {
|
|||
#[gpui::test]
|
||||
fn test_max_point(cx: &mut gpui::MutableAppContext) {
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "aaa\n\t\tbbb", cx));
|
||||
let settings = watch::channel_with(Settings {
|
||||
tab_size: 4,
|
||||
..Settings::test(cx)
|
||||
let tab_size = 4;
|
||||
let font_cache = cx.font_cache();
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
let map = cx.add_model(|cx| {
|
||||
DisplayMap::new(buffer.clone(), tab_size, font_id, font_size, None, cx)
|
||||
});
|
||||
let map = cx.add_model(|cx| DisplayMap::new(buffer.clone(), settings.1, None, cx));
|
||||
assert_eq!(
|
||||
map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
|
||||
DisplayPoint::new(1, 11)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use super::{
|
|||
fold_map,
|
||||
tab_map::{self, Edit as TabEdit, Snapshot as TabSnapshot, TabPoint, TextSummary},
|
||||
};
|
||||
use crate::{editor::Point, settings::HighlightId, util::Bias, Settings};
|
||||
use crate::{editor::Point, settings::HighlightId, util::Bias};
|
||||
use gpui::{
|
||||
fonts::FontId,
|
||||
sum_tree::{self, Cursor, SumTree},
|
||||
text_layout::LineWrapper,
|
||||
Entity, ModelContext, Task,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use postage::{prelude::Stream, watch};
|
||||
use smol::future::yield_now;
|
||||
use std::{collections::VecDeque, ops::Range, time::Duration};
|
||||
|
||||
|
|
@ -18,8 +18,7 @@ pub struct WrapMap {
|
|||
pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
|
||||
wrap_width: Option<f32>,
|
||||
background_task: Option<Task<()>>,
|
||||
_watch_settings: Task<()>,
|
||||
settings: watch::Receiver<Settings>,
|
||||
font: (FontId, f32),
|
||||
}
|
||||
|
||||
impl Entity for WrapMap {
|
||||
|
|
@ -76,36 +75,17 @@ pub struct BufferRows<'a> {
|
|||
impl WrapMap {
|
||||
pub fn new(
|
||||
tab_snapshot: TabSnapshot,
|
||||
settings: watch::Receiver<Settings>,
|
||||
font_id: FontId,
|
||||
font_size: f32,
|
||||
wrap_width: Option<f32>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let _watch_settings = cx.spawn_weak({
|
||||
let mut prev_font = (
|
||||
settings.borrow().buffer_font_size,
|
||||
settings.borrow().buffer_font_family,
|
||||
);
|
||||
let mut settings = settings.clone();
|
||||
move |this, mut cx| async move {
|
||||
while let Some(settings) = settings.recv().await {
|
||||
if let Some(this) = this.upgrade(&cx) {
|
||||
let font = (settings.buffer_font_size, settings.buffer_font_family);
|
||||
if font != prev_font {
|
||||
prev_font = font;
|
||||
this.update(&mut cx, |this, cx| this.rewrap(cx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut this = Self {
|
||||
font: (font_id, font_size),
|
||||
wrap_width: None,
|
||||
pending_edits: Default::default(),
|
||||
snapshot: Snapshot::new(tab_snapshot),
|
||||
settings,
|
||||
background_task: None,
|
||||
_watch_settings,
|
||||
};
|
||||
this.set_wrap_width(wrap_width, cx);
|
||||
|
||||
|
|
@ -128,6 +108,13 @@ impl WrapMap {
|
|||
self.snapshot.clone()
|
||||
}
|
||||
|
||||
pub fn set_font(&mut self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) {
|
||||
if (font_id, font_size) != self.font {
|
||||
self.font = (font_id, font_size);
|
||||
self.rewrap(cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_wrap_width(&mut self, wrap_width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
|
||||
if wrap_width == self.wrap_width {
|
||||
return false;
|
||||
|
|
@ -144,15 +131,9 @@ impl WrapMap {
|
|||
if let Some(wrap_width) = self.wrap_width {
|
||||
let mut new_snapshot = self.snapshot.clone();
|
||||
let font_cache = cx.font_cache().clone();
|
||||
let settings = self.settings.clone();
|
||||
let (font_id, font_size) = self.font;
|
||||
let task = cx.background().spawn(async move {
|
||||
let mut line_wrapper = {
|
||||
let settings = settings.borrow();
|
||||
let font_id = font_cache
|
||||
.select_font(settings.buffer_font_family, &Default::default())
|
||||
.unwrap();
|
||||
font_cache.line_wrapper(font_id, settings.buffer_font_size)
|
||||
};
|
||||
let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
|
||||
let tab_snapshot = new_snapshot.tab_snapshot.clone();
|
||||
let range = TabPoint::zero()..tab_snapshot.max_point();
|
||||
new_snapshot
|
||||
|
|
@ -222,15 +203,9 @@ impl WrapMap {
|
|||
let pending_edits = self.pending_edits.clone();
|
||||
let mut snapshot = self.snapshot.clone();
|
||||
let font_cache = cx.font_cache().clone();
|
||||
let settings = self.settings.clone();
|
||||
let (font_id, font_size) = self.font;
|
||||
let update_task = cx.background().spawn(async move {
|
||||
let mut line_wrapper = {
|
||||
let settings = settings.borrow();
|
||||
let font_id = font_cache
|
||||
.select_font(settings.buffer_font_family, &Default::default())
|
||||
.unwrap();
|
||||
font_cache.line_wrapper(font_id, settings.buffer_font_size)
|
||||
};
|
||||
let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
|
||||
|
||||
for (tab_snapshot, edits) in pending_edits {
|
||||
snapshot
|
||||
|
|
@ -950,13 +925,14 @@ mod tests {
|
|||
} else {
|
||||
Some(rng.gen_range(0.0..=1000.0))
|
||||
};
|
||||
let settings = Settings {
|
||||
tab_size: rng.gen_range(1..=4),
|
||||
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
||||
buffer_font_size: 14.0,
|
||||
..cx.read(Settings::test)
|
||||
};
|
||||
log::info!("Tab size: {}", settings.tab_size);
|
||||
let tab_size = rng.gen_range(1..=4);
|
||||
let family_id = font_cache.load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = font_cache
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
|
||||
log::info!("Tab size: {}", tab_size);
|
||||
log::info!("Wrap width: {:?}", wrap_width);
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
|
|
@ -965,7 +941,7 @@ mod tests {
|
|||
Buffer::new(0, text, cx)
|
||||
});
|
||||
let (mut fold_map, folds_snapshot) = cx.read(|cx| FoldMap::new(buffer.clone(), cx));
|
||||
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), settings.tab_size);
|
||||
let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
|
||||
log::info!(
|
||||
"Unwrapped text (no folds): {:?}",
|
||||
buffer.read_with(&cx, |buf, _| buf.text())
|
||||
|
|
@ -976,16 +952,13 @@ mod tests {
|
|||
);
|
||||
log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
|
||||
|
||||
let font_id = font_cache
|
||||
.select_font(settings.buffer_font_family, &Default::default())
|
||||
.unwrap();
|
||||
let mut line_wrapper = LineWrapper::new(font_id, settings.buffer_font_size, font_system);
|
||||
let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system);
|
||||
let unwrapped_text = tabs_snapshot.text();
|
||||
let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
|
||||
|
||||
let settings = watch::channel_with(settings).1;
|
||||
let wrap_map = cx
|
||||
.add_model(|cx| WrapMap::new(tabs_snapshot.clone(), settings.clone(), wrap_width, cx));
|
||||
let wrap_map = cx.add_model(|cx| {
|
||||
WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx)
|
||||
});
|
||||
let (_observer, notifications) = Observer::new(&wrap_map, &mut cx);
|
||||
|
||||
if wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use super::{DisplayPoint, Editor, EditorMode, Insert, Scroll, Select, SelectPhase, Snapshot};
|
||||
use crate::{theme::EditorStyle, time::ReplicaId};
|
||||
use super::{
|
||||
DisplayPoint, Editor, EditorMode, EditorStyle, Insert, Scroll, Select, SelectPhase, Snapshot,
|
||||
MAX_LINE_LEN,
|
||||
};
|
||||
use crate::{theme::HighlightId, time::ReplicaId};
|
||||
use gpui::{
|
||||
color::Color,
|
||||
geometry::{
|
||||
|
|
@ -9,7 +12,7 @@ use gpui::{
|
|||
},
|
||||
json::{self, ToJson},
|
||||
keymap::Keystroke,
|
||||
text_layout::{self, TextLayoutCache},
|
||||
text_layout::{self, RunStyle, TextLayoutCache},
|
||||
AppContext, Axis, Border, Element, Event, EventContext, FontCache, LayoutContext,
|
||||
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
|
||||
};
|
||||
|
|
@ -18,6 +21,7 @@ use smallvec::SmallVec;
|
|||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
collections::{BTreeMap, HashMap},
|
||||
fmt::Write,
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
|
|
@ -374,6 +378,176 @@ impl EditorElement {
|
|||
|
||||
cx.scene.pop_layer();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
cx.text_layout_cache
|
||||
.layout_str(
|
||||
"1".repeat(digit_count).as_str(),
|
||||
self.style.text.font_size,
|
||||
&[(
|
||||
digit_count,
|
||||
RunStyle {
|
||||
font_id: self.style.text.font_id,
|
||||
color: Color::black(),
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)
|
||||
.width()
|
||||
}
|
||||
|
||||
fn layout_line_numbers(
|
||||
&self,
|
||||
rows: Range<u32>,
|
||||
active_rows: &BTreeMap<u32, bool>,
|
||||
snapshot: &Snapshot,
|
||||
cx: &LayoutContext,
|
||||
) -> Vec<Option<text_layout::Line>> {
|
||||
let mut layouts = Vec::with_capacity(rows.len());
|
||||
let mut line_number = String::new();
|
||||
for (ix, (buffer_row, soft_wrapped)) in snapshot
|
||||
.buffer_rows(rows.start)
|
||||
.take((rows.end - rows.start) as usize)
|
||||
.enumerate()
|
||||
{
|
||||
let display_row = rows.start + ix as u32;
|
||||
let color = if active_rows.contains_key(&display_row) {
|
||||
self.style.line_number_active
|
||||
} else {
|
||||
self.style.line_number
|
||||
};
|
||||
if soft_wrapped {
|
||||
layouts.push(None);
|
||||
} else {
|
||||
line_number.clear();
|
||||
write!(&mut line_number, "{}", buffer_row + 1).unwrap();
|
||||
layouts.push(Some(cx.text_layout_cache.layout_str(
|
||||
&line_number,
|
||||
self.style.text.font_size,
|
||||
&[(
|
||||
line_number.len(),
|
||||
RunStyle {
|
||||
font_id: self.style.text.font_id,
|
||||
color,
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
layouts
|
||||
}
|
||||
|
||||
fn layout_lines(
|
||||
&mut self,
|
||||
mut rows: Range<u32>,
|
||||
snapshot: &mut Snapshot,
|
||||
cx: &LayoutContext,
|
||||
) -> Vec<text_layout::Line> {
|
||||
rows.end = cmp::min(rows.end, snapshot.max_point().row() + 1);
|
||||
if rows.start >= rows.end {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// 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_text = snapshot.placeholder_text();
|
||||
let placeholder_lines = placeholder_text
|
||||
.as_ref()
|
||||
.map_or("", AsRef::as_ref)
|
||||
.split('\n')
|
||||
.skip(rows.start as usize)
|
||||
.take(rows.len());
|
||||
return placeholder_lines
|
||||
.map(|line| {
|
||||
cx.text_layout_cache.layout_str(
|
||||
line,
|
||||
placeholder_style.font_size,
|
||||
&[(
|
||||
line.len(),
|
||||
RunStyle {
|
||||
font_id: placeholder_style.font_id,
|
||||
color: placeholder_style.color,
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
let mut prev_font_properties = self.style.text.font_properties.clone();
|
||||
let mut prev_font_id = self.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();
|
||||
let mut row = rows.start;
|
||||
let mut line_exceeded_max_len = false;
|
||||
let chunks = snapshot.highlighted_chunks_for_rows(rows.clone());
|
||||
|
||||
'outer: for (chunk, style_ix) in chunks.chain(Some(("\n", HighlightId::default()))) {
|
||||
for (ix, mut line_chunk) in chunk.split('\n').enumerate() {
|
||||
if ix > 0 {
|
||||
layouts.push(cx.text_layout_cache.layout_str(
|
||||
&line,
|
||||
self.style.text.font_size,
|
||||
&styles,
|
||||
));
|
||||
line.clear();
|
||||
styles.clear();
|
||||
row += 1;
|
||||
line_exceeded_max_len = false;
|
||||
if row == rows.end {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||
let style = theme
|
||||
.syntax
|
||||
.highlight_style(style_ix)
|
||||
.unwrap_or(self.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 {
|
||||
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)
|
||||
};
|
||||
|
||||
if line.len() + line_chunk.len() > MAX_LINE_LEN {
|
||||
let mut chunk_len = MAX_LINE_LEN - line.len();
|
||||
while !line_chunk.is_char_boundary(chunk_len) {
|
||||
chunk_len -= 1;
|
||||
}
|
||||
line_chunk = &line_chunk[..chunk_len];
|
||||
line_exceeded_max_len = true;
|
||||
}
|
||||
|
||||
line.push_str(line_chunk);
|
||||
styles.push((
|
||||
line_chunk.len(),
|
||||
RunStyle {
|
||||
font_id,
|
||||
color: style.color,
|
||||
underline: style.underline,
|
||||
},
|
||||
));
|
||||
prev_font_id = font_id;
|
||||
prev_font_properties = style.font_properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layouts
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for EditorElement {
|
||||
|
|
@ -390,30 +564,22 @@ impl Element for EditorElement {
|
|||
unimplemented!("we don't yet handle an infinite width constraint on buffer elements");
|
||||
}
|
||||
|
||||
let font_cache = &cx.font_cache;
|
||||
let layout_cache = &cx.text_layout_cache;
|
||||
let snapshot = self.snapshot(cx.app);
|
||||
let line_height = snapshot.line_height(font_cache);
|
||||
let line_height = self.style.text.line_height(cx.font_cache);
|
||||
|
||||
let gutter_padding;
|
||||
let gutter_width;
|
||||
if snapshot.mode == EditorMode::Full {
|
||||
gutter_padding = snapshot.em_width(cx.font_cache);
|
||||
match snapshot.max_line_number_width(cx.font_cache, cx.text_layout_cache) {
|
||||
Err(error) => {
|
||||
log::error!("error computing max line number width: {}", error);
|
||||
return (size, None);
|
||||
}
|
||||
Ok(width) => gutter_width = width + gutter_padding * 2.0,
|
||||
}
|
||||
gutter_padding = self.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;
|
||||
gutter_width = 0.0
|
||||
};
|
||||
|
||||
let text_width = size.x() - gutter_width;
|
||||
let text_offset = vec2f(-snapshot.font_descent(cx.font_cache), 0.);
|
||||
let em_width = snapshot.em_width(font_cache);
|
||||
let text_offset = vec2f(-self.style.text.descent(cx.font_cache), 0.);
|
||||
let em_width = self.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| {
|
||||
|
|
@ -488,51 +654,18 @@ impl Element for EditorElement {
|
|||
});
|
||||
|
||||
let line_number_layouts = if snapshot.mode == EditorMode::Full {
|
||||
let settings = self
|
||||
.view
|
||||
.upgrade(cx.app)
|
||||
.unwrap()
|
||||
.read(cx.app)
|
||||
.settings
|
||||
.borrow();
|
||||
match snapshot.layout_line_numbers(
|
||||
start_row..end_row,
|
||||
&active_rows,
|
||||
cx.font_cache,
|
||||
cx.text_layout_cache,
|
||||
&settings.theme,
|
||||
) {
|
||||
Err(error) => {
|
||||
log::error!("error laying out line numbers: {}", error);
|
||||
return (size, None);
|
||||
}
|
||||
Ok(layouts) => layouts,
|
||||
}
|
||||
self.layout_line_numbers(start_row..end_row, &active_rows, &snapshot, cx)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let mut max_visible_line_width = 0.0;
|
||||
let line_layouts = match snapshot.layout_lines(
|
||||
start_row..end_row,
|
||||
&self.style,
|
||||
font_cache,
|
||||
layout_cache,
|
||||
) {
|
||||
Err(error) => {
|
||||
log::error!("error laying out lines: {}", error);
|
||||
return (size, None);
|
||||
let line_layouts = self.layout_lines(start_row..end_row, &mut snapshot, cx);
|
||||
for line in &line_layouts {
|
||||
if line.width() > max_visible_line_width {
|
||||
max_visible_line_width = line.width();
|
||||
}
|
||||
Ok(layouts) => {
|
||||
for line in &layouts {
|
||||
if line.width() > max_visible_line_width {
|
||||
max_visible_line_width = line.width();
|
||||
}
|
||||
}
|
||||
|
||||
layouts
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let mut layout = LayoutState {
|
||||
size,
|
||||
|
|
@ -542,6 +675,7 @@ impl Element for EditorElement {
|
|||
overscroll,
|
||||
text_offset,
|
||||
snapshot,
|
||||
style: self.style.clone(),
|
||||
active_rows,
|
||||
line_layouts,
|
||||
line_number_layouts,
|
||||
|
|
@ -551,15 +685,18 @@ impl Element for EditorElement {
|
|||
max_visible_line_width,
|
||||
};
|
||||
|
||||
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);
|
||||
self.update_view(cx.app, |view, cx| {
|
||||
let clamped = view.clamp_scroll_left(layout.scroll_max(font_cache, layout_cache).x());
|
||||
let clamped = view.clamp_scroll_left(scroll_max);
|
||||
let autoscrolled;
|
||||
if autoscroll_horizontally {
|
||||
autoscrolled = view.autoscroll_horizontally(
|
||||
start_row,
|
||||
layout.text_size.x(),
|
||||
layout.scroll_width(font_cache, layout_cache),
|
||||
layout.snapshot.em_width(font_cache),
|
||||
scroll_width,
|
||||
max_glyph_width,
|
||||
&layout.line_layouts,
|
||||
cx,
|
||||
);
|
||||
|
|
@ -659,6 +796,7 @@ pub struct LayoutState {
|
|||
gutter_size: Vector2F,
|
||||
gutter_padding: f32,
|
||||
text_size: Vector2F,
|
||||
style: EditorStyle,
|
||||
snapshot: Snapshot,
|
||||
active_rows: BTreeMap<u32, bool>,
|
||||
line_layouts: Vec<text_layout::Line>,
|
||||
|
|
@ -672,20 +810,16 @@ pub struct LayoutState {
|
|||
}
|
||||
|
||||
impl LayoutState {
|
||||
fn scroll_width(&self, font_cache: &FontCache, layout_cache: &TextLayoutCache) -> f32 {
|
||||
fn scroll_width(&self, layout_cache: &TextLayoutCache) -> f32 {
|
||||
let row = self.snapshot.longest_row();
|
||||
let longest_line_width = self
|
||||
.snapshot
|
||||
.layout_line(row, font_cache, layout_cache)
|
||||
.unwrap()
|
||||
.width();
|
||||
let longest_line_width = self.layout_line(row, &self.snapshot, layout_cache).width();
|
||||
longest_line_width.max(self.max_visible_line_width) + self.overscroll.x()
|
||||
}
|
||||
|
||||
fn scroll_max(&self, font_cache: &FontCache, layout_cache: &TextLayoutCache) -> Vector2F {
|
||||
let text_width = self.text_size.x();
|
||||
let scroll_width = self.scroll_width(font_cache, layout_cache);
|
||||
let em_width = self.snapshot.em_width(font_cache);
|
||||
let scroll_width = self.scroll_width(layout_cache);
|
||||
let em_width = self.style.text.em_width(font_cache);
|
||||
let max_row = self.snapshot.max_point().row();
|
||||
|
||||
vec2f(
|
||||
|
|
@ -693,6 +827,36 @@ impl LayoutState {
|
|||
max_row.saturating_sub(1) as f32,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn layout_line(
|
||||
&self,
|
||||
row: u32,
|
||||
snapshot: &Snapshot,
|
||||
layout_cache: &TextLayoutCache,
|
||||
) -> text_layout::Line {
|
||||
let mut line = snapshot.line(row);
|
||||
|
||||
if line.len() > MAX_LINE_LEN {
|
||||
let mut len = MAX_LINE_LEN;
|
||||
while !line.is_char_boundary(len) {
|
||||
len -= 1;
|
||||
}
|
||||
line.truncate(len);
|
||||
}
|
||||
|
||||
layout_cache.layout_str(
|
||||
&line,
|
||||
self.style.text.font_size,
|
||||
&[(
|
||||
snapshot.line_len(row) as usize,
|
||||
RunStyle {
|
||||
font_id: self.style.text.font_id,
|
||||
color: Color::black(),
|
||||
underline: false,
|
||||
},
|
||||
)],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PaintState {
|
||||
|
|
@ -864,3 +1028,42 @@ fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 {
|
|||
fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
|
||||
delta.powf(1.2) / 300.0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
editor::{Buffer, Editor, EditorStyle},
|
||||
settings,
|
||||
test::sample_text,
|
||||
};
|
||||
|
||||
#[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 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()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let element = EditorElement::new(editor.downgrade(), style);
|
||||
|
||||
let layouts = editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
let mut presenter = cx.build_presenter(window_id, 30.);
|
||||
let mut layout_cx = presenter.build_layout_context(false, cx);
|
||||
element.layout_line_numbers(0..6, &Default::default(), &snapshot, &mut layout_cx)
|
||||
});
|
||||
assert_eq!(layouts.len(), 6);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,16 +180,21 @@ fn char_kind(c: char) -> CharKind {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
editor::{display_map::DisplayMap, Buffer},
|
||||
test::test_app_state,
|
||||
};
|
||||
use crate::editor::{display_map::DisplayMap, Buffer};
|
||||
|
||||
#[gpui::test]
|
||||
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
|
||||
let settings = test_app_state(cx).settings.clone();
|
||||
let tab_size = 4;
|
||||
let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
|
||||
let font_id = cx
|
||||
.font_cache()
|
||||
.select_font(family_id, &Default::default())
|
||||
.unwrap();
|
||||
let font_size = 14.0;
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ", cx));
|
||||
let display_map = cx.add_model(|cx| DisplayMap::new(buffer, settings, None, cx));
|
||||
let display_map =
|
||||
cx.add_model(|cx| DisplayMap::new(buffer, tab_size, font_id, font_size, None, cx));
|
||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
assert_eq!(
|
||||
prev_word_boundary(&snapshot, DisplayPoint::new(0, 12)).unwrap(),
|
||||
|
|
|
|||
|
|
@ -275,10 +275,14 @@ impl FileFinder {
|
|||
cx.observe(&workspace, Self::workspace_updated).detach();
|
||||
|
||||
let query_editor = cx.add_view(|cx| {
|
||||
Editor::single_line(settings.clone(), cx).with_style({
|
||||
let settings = settings.clone();
|
||||
move |_| settings.borrow().theme.selector.input_editor.as_editor()
|
||||
})
|
||||
Editor::single_line(
|
||||
settings.clone(),
|
||||
{
|
||||
let settings = settings.clone();
|
||||
move |_| settings.borrow().theme.selector.input_editor.as_editor()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.subscribe(&query_editor, Self::on_query_editor_event)
|
||||
.detach();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ mod highlight_map;
|
|||
mod resolution;
|
||||
mod theme_registry;
|
||||
|
||||
use crate::editor::{EditorStyle, SelectionStyle};
|
||||
use anyhow::Result;
|
||||
use gpui::{
|
||||
color::Color,
|
||||
|
|
@ -158,35 +159,16 @@ pub struct ContainedLabel {
|
|||
pub label: LabelStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct EditorStyle {
|
||||
pub text: HighlightStyle,
|
||||
#[serde(default)]
|
||||
pub placeholder_text: HighlightStyle,
|
||||
pub background: Color,
|
||||
pub selection: SelectionStyle,
|
||||
pub gutter_background: Color,
|
||||
pub active_line_background: Color,
|
||||
pub line_number: Color,
|
||||
pub line_number_active: Color,
|
||||
pub guest_selections: Vec<SelectionStyle>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct InputEditorStyle {
|
||||
#[serde(flatten)]
|
||||
pub container: ContainerStyle,
|
||||
pub text: HighlightStyle,
|
||||
pub placeholder_text: HighlightStyle,
|
||||
pub text: TextStyle,
|
||||
#[serde(default)]
|
||||
pub placeholder_text: Option<TextStyle>,
|
||||
pub selection: SelectionStyle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Deserialize)]
|
||||
pub struct SelectionStyle {
|
||||
pub cursor: Color,
|
||||
pub selection: Color,
|
||||
}
|
||||
|
||||
impl SyntaxTheme {
|
||||
pub fn new(highlights: Vec<(String, HighlightStyle)>) -> Self {
|
||||
Self { highlights }
|
||||
|
|
@ -204,30 +186,6 @@ impl SyntaxTheme {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for EditorStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
text: HighlightStyle {
|
||||
color: Color::from_u32(0xff0000ff),
|
||||
font_properties: Default::default(),
|
||||
underline: false,
|
||||
},
|
||||
placeholder_text: HighlightStyle {
|
||||
color: Color::from_u32(0x00ff00ff),
|
||||
font_properties: Default::default(),
|
||||
underline: false,
|
||||
},
|
||||
background: Default::default(),
|
||||
gutter_background: Default::default(),
|
||||
active_line_background: Default::default(),
|
||||
line_number: Default::default(),
|
||||
line_number_active: Default::default(),
|
||||
selection: Default::default(),
|
||||
guest_selections: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputEditorStyle {
|
||||
pub fn as_editor(&self) -> EditorStyle {
|
||||
EditorStyle {
|
||||
|
|
@ -238,7 +196,11 @@ impl InputEditorStyle {
|
|||
.background_color
|
||||
.unwrap_or(Color::transparent_black()),
|
||||
selection: self.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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,10 +58,14 @@ impl ThemeSelector {
|
|||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
let query_editor = cx.add_view(|cx| {
|
||||
Editor::single_line(settings.clone(), cx).with_style({
|
||||
let settings = settings.clone();
|
||||
move |_| settings.borrow().theme.selector.input_editor.as_editor()
|
||||
})
|
||||
Editor::single_line(
|
||||
settings.clone(),
|
||||
{
|
||||
let settings = settings.clone();
|
||||
move |_| settings.borrow().theme.selector.input_editor.as_editor()
|
||||
},
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
cx.subscribe(&query_editor, Self::on_query_editor_event)
|
||||
|
|
|
|||
Loading…
Reference in a new issue