mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-15 06:40:17 +00:00
Overhaul handling of font families
* Specify font families in the theme. * Load fonts eagerly when loading themes, instead of loading them lazily when rendering. Co-Authored-By: Antonio Scandurra <me@as-cii.com> Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
ee9ee294ad
commit
3bb5610ad1
23 changed files with 438 additions and 397 deletions
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
font_cache::FamilyId,
|
|
||||||
fonts::{FontId, TextStyle},
|
fonts::{FontId, TextStyle},
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
|
@ -8,8 +7,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
json::{ToJson, Value},
|
json::{ToJson, Value},
|
||||||
text_layout::Line,
|
text_layout::Line,
|
||||||
DebugContext, Element, Event, EventContext, FontCache, LayoutContext, PaintContext,
|
DebugContext, Element, Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
|
||||||
SizeConstraint,
|
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -17,49 +15,41 @@ use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
pub struct Label {
|
pub struct Label {
|
||||||
text: String,
|
text: String,
|
||||||
family_id: FamilyId,
|
|
||||||
font_size: f32,
|
|
||||||
style: LabelStyle,
|
style: LabelStyle,
|
||||||
highlight_indices: Vec<usize>,
|
highlight_indices: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct LabelStyle {
|
pub struct LabelStyle {
|
||||||
pub text: TextStyle,
|
pub text: TextStyle,
|
||||||
pub highlight_text: Option<TextStyle>,
|
pub highlight_text: Option<TextStyle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<TextStyle> for LabelStyle {
|
||||||
|
fn from(text: TextStyle) -> Self {
|
||||||
|
LabelStyle {
|
||||||
|
text,
|
||||||
|
highlight_text: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Label {
|
impl Label {
|
||||||
pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
|
pub fn new(text: String, style: impl Into<LabelStyle>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
text,
|
text,
|
||||||
family_id,
|
|
||||||
font_size,
|
|
||||||
highlight_indices: Default::default(),
|
highlight_indices: Default::default(),
|
||||||
style: Default::default(),
|
style: style.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_style(mut self, style: &LabelStyle) -> Self {
|
|
||||||
self.style = style.clone();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_default_color(mut self, color: Color) -> Self {
|
|
||||||
self.style.text.color = color;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
|
pub fn with_highlights(mut self, indices: Vec<usize>) -> Self {
|
||||||
self.highlight_indices = indices;
|
self.highlight_indices = indices;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_runs(
|
fn compute_runs(&self) -> SmallVec<[(usize, FontId, Color); 8]> {
|
||||||
&self,
|
let font_id = self.style.text.font_id;
|
||||||
font_cache: &FontCache,
|
|
||||||
font_id: FontId,
|
|
||||||
) -> SmallVec<[(usize, FontId, Color); 8]> {
|
|
||||||
if self.highlight_indices.is_empty() {
|
if self.highlight_indices.is_empty() {
|
||||||
return smallvec![(self.text.len(), font_id, self.style.text.color)];
|
return smallvec![(self.text.len(), font_id, self.style.text.color)];
|
||||||
}
|
}
|
||||||
|
@ -68,12 +58,7 @@ impl Label {
|
||||||
.style
|
.style
|
||||||
.highlight_text
|
.highlight_text
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|style| {
|
.map_or(font_id, |style| style.font_id);
|
||||||
font_cache
|
|
||||||
.select_font(self.family_id, &style.font_properties)
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.unwrap_or(font_id);
|
|
||||||
|
|
||||||
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
let mut highlight_indices = self.highlight_indices.iter().copied().peekable();
|
||||||
let mut runs = SmallVec::new();
|
let mut runs = SmallVec::new();
|
||||||
|
@ -123,18 +108,18 @@ impl Element for Label {
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let font_id = cx
|
let runs = self.compute_runs();
|
||||||
.font_cache
|
let line = cx.text_layout_cache.layout_str(
|
||||||
.select_font(self.family_id, &self.style.text.font_properties)
|
self.text.as_str(),
|
||||||
.unwrap();
|
self.style.text.font_size,
|
||||||
let runs = self.compute_runs(&cx.font_cache, font_id);
|
runs.as_slice(),
|
||||||
let line =
|
);
|
||||||
cx.text_layout_cache
|
|
||||||
.layout_str(self.text.as_str(), self.font_size, runs.as_slice());
|
|
||||||
|
|
||||||
let size = vec2f(
|
let size = vec2f(
|
||||||
line.width().max(constraint.min.x()).min(constraint.max.x()),
|
line.width().max(constraint.min.x()).min(constraint.max.x()),
|
||||||
cx.font_cache.line_height(font_id, self.font_size).ceil(),
|
cx.font_cache
|
||||||
|
.line_height(self.style.text.font_id, self.style.text.font_size)
|
||||||
|
.ceil(),
|
||||||
);
|
);
|
||||||
|
|
||||||
(size, line)
|
(size, line)
|
||||||
|
@ -169,15 +154,13 @@ impl Element for Label {
|
||||||
bounds: RectF,
|
bounds: RectF,
|
||||||
_: &Self::LayoutState,
|
_: &Self::LayoutState,
|
||||||
_: &Self::PaintState,
|
_: &Self::PaintState,
|
||||||
cx: &DebugContext,
|
_: &DebugContext,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
json!({
|
json!({
|
||||||
"type": "Label",
|
"type": "Label",
|
||||||
"bounds": bounds.to_json(),
|
"bounds": bounds.to_json(),
|
||||||
"text": &self.text,
|
"text": &self.text,
|
||||||
"highlight_indices": self.highlight_indices,
|
"highlight_indices": self.highlight_indices,
|
||||||
"font_family": cx.font_cache.family_name(self.family_id).unwrap(),
|
|
||||||
"font_size": self.font_size,
|
|
||||||
"style": self.style.to_json(),
|
"style": self.style.to_json(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -201,29 +184,29 @@ mod tests {
|
||||||
|
|
||||||
#[crate::test(self)]
|
#[crate::test(self)]
|
||||||
fn test_layout_label_with_highlights(cx: &mut crate::MutableAppContext) {
|
fn test_layout_label_with_highlights(cx: &mut crate::MutableAppContext) {
|
||||||
let menlo = cx.font_cache().load_family(&["Menlo"]).unwrap();
|
let default_style = TextStyle::new(
|
||||||
let menlo_regular = cx
|
"Menlo",
|
||||||
.font_cache()
|
12.,
|
||||||
.select_font(menlo, &FontProperties::new())
|
Default::default(),
|
||||||
|
Color::black(),
|
||||||
|
cx.font_cache(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let menlo_bold = cx
|
let highlight_style = TextStyle::new(
|
||||||
.font_cache()
|
"Menlo",
|
||||||
.select_font(menlo, FontProperties::new().weight(Weight::BOLD))
|
12.,
|
||||||
|
*FontProperties::new().weight(Weight::BOLD),
|
||||||
|
Color::new(255, 0, 0, 255),
|
||||||
|
cx.font_cache(),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let black = Color::black();
|
let label = Label::new(
|
||||||
let red = Color::new(255, 0, 0, 255);
|
".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(),
|
||||||
|
LabelStyle {
|
||||||
let label = Label::new(".αβγδε.ⓐⓑⓒⓓⓔ.abcde.".to_string(), menlo, 12.0)
|
text: default_style.clone(),
|
||||||
.with_style(&LabelStyle {
|
highlight_text: Some(highlight_style.clone()),
|
||||||
text: TextStyle {
|
|
||||||
color: black,
|
|
||||||
font_properties: Default::default(),
|
|
||||||
},
|
},
|
||||||
highlight_text: Some(TextStyle {
|
)
|
||||||
color: red,
|
|
||||||
font_properties: *FontProperties::new().weight(Weight::BOLD),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.with_highlights(vec![
|
.with_highlights(vec![
|
||||||
".α".len(),
|
".α".len(),
|
||||||
".αβ".len(),
|
".αβ".len(),
|
||||||
|
@ -232,17 +215,21 @@ mod tests {
|
||||||
".αβγδε.ⓐⓑ".len(),
|
".αβγδε.ⓐⓑ".len(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let runs = label.compute_runs(cx.font_cache().as_ref(), menlo_regular);
|
let runs = label.compute_runs();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
runs.as_slice(),
|
runs.as_slice(),
|
||||||
&[
|
&[
|
||||||
(".α".len(), menlo_regular, black),
|
(".α".len(), default_style.font_id, default_style.color),
|
||||||
("βγ".len(), menlo_bold, red),
|
("βγ".len(), highlight_style.font_id, highlight_style.color),
|
||||||
("δ".len(), menlo_regular, black),
|
("δ".len(), default_style.font_id, default_style.color),
|
||||||
("ε".len(), menlo_bold, red),
|
("ε".len(), highlight_style.font_id, highlight_style.color),
|
||||||
(".ⓐ".len(), menlo_regular, black),
|
(".ⓐ".len(), default_style.font_id, default_style.color),
|
||||||
("ⓑⓒ".len(), menlo_bold, red),
|
("ⓑⓒ".len(), highlight_style.font_id, highlight_style.color),
|
||||||
("ⓓⓔ.abcde.".len(), menlo_regular, black),
|
(
|
||||||
|
"ⓓⓔ.abcde.".len(),
|
||||||
|
default_style.font_id,
|
||||||
|
default_style.color
|
||||||
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
font_cache::FamilyId,
|
|
||||||
fonts::TextStyle,
|
fonts::TextStyle,
|
||||||
geometry::{
|
geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
|
@ -14,8 +13,6 @@ use serde_json::json;
|
||||||
|
|
||||||
pub struct Text {
|
pub struct Text {
|
||||||
text: String,
|
text: String,
|
||||||
family_id: FamilyId,
|
|
||||||
font_size: f32,
|
|
||||||
style: TextStyle,
|
style: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,18 +22,8 @@ pub struct LayoutState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Text {
|
impl Text {
|
||||||
pub fn new(text: String, family_id: FamilyId, font_size: f32) -> Self {
|
pub fn new(text: String, style: TextStyle) -> Self {
|
||||||
Self {
|
Self { text, style }
|
||||||
text,
|
|
||||||
family_id,
|
|
||||||
font_size,
|
|
||||||
style: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_style(mut self, style: &TextStyle) -> Self {
|
|
||||||
self.style = style.clone();
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_default_color(mut self, color: Color) -> Self {
|
pub fn with_default_color(mut self, color: Color) -> Self {
|
||||||
|
@ -54,20 +41,17 @@ impl Element for Text {
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
cx: &mut LayoutContext,
|
cx: &mut LayoutContext,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let font_id = cx
|
let font_id = self.style.font_id;
|
||||||
.font_cache
|
let line_height = cx.font_cache.line_height(font_id, self.style.font_size);
|
||||||
.select_font(self.family_id, &self.style.font_properties)
|
|
||||||
.unwrap();
|
|
||||||
let line_height = cx.font_cache.line_height(font_id, self.font_size);
|
|
||||||
|
|
||||||
let mut wrapper = cx.font_cache.line_wrapper(font_id, self.font_size);
|
let mut wrapper = cx.font_cache.line_wrapper(font_id, self.style.font_size);
|
||||||
let mut lines = Vec::new();
|
let mut lines = Vec::new();
|
||||||
let mut line_count = 0;
|
let mut line_count = 0;
|
||||||
let mut max_line_width = 0_f32;
|
let mut max_line_width = 0_f32;
|
||||||
for line in self.text.lines() {
|
for line in self.text.lines() {
|
||||||
let shaped_line = cx.text_layout_cache.layout_str(
|
let shaped_line = cx.text_layout_cache.layout_str(
|
||||||
line,
|
line,
|
||||||
self.font_size,
|
self.style.font_size,
|
||||||
&[(line.len(), font_id, self.style.color)],
|
&[(line.len(), font_id, self.style.color)],
|
||||||
);
|
);
|
||||||
let wrap_boundaries = wrapper
|
let wrap_boundaries = wrapper
|
||||||
|
@ -123,14 +107,12 @@ impl Element for Text {
|
||||||
bounds: RectF,
|
bounds: RectF,
|
||||||
_: &Self::LayoutState,
|
_: &Self::LayoutState,
|
||||||
_: &Self::PaintState,
|
_: &Self::PaintState,
|
||||||
cx: &DebugContext,
|
_: &DebugContext,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
json!({
|
json!({
|
||||||
"type": "Label",
|
"type": "Text",
|
||||||
"bounds": bounds.to_json(),
|
"bounds": bounds.to_json(),
|
||||||
"text": &self.text,
|
"text": &self.text,
|
||||||
"font_family": cx.font_cache.family_name(self.family_id).unwrap(),
|
|
||||||
"font_size": self.font_size,
|
|
||||||
"style": self.style.to_json(),
|
"style": self.style.to_json(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,35 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
json::{json, ToJson},
|
json::{json, ToJson},
|
||||||
|
FontCache,
|
||||||
};
|
};
|
||||||
|
use anyhow::anyhow;
|
||||||
pub use font_kit::{
|
pub use font_kit::{
|
||||||
metrics::Metrics,
|
metrics::Metrics,
|
||||||
properties::{Properties, Stretch, Style, Weight},
|
properties::{Properties, Stretch, Style, Weight},
|
||||||
};
|
};
|
||||||
use serde::{de, Deserialize};
|
use serde::{de, Deserialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
use std::{cell::RefCell, sync::Arc};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct FontId(pub usize);
|
pub struct FontId(pub usize);
|
||||||
|
|
||||||
pub type GlyphId = u32;
|
pub type GlyphId = u32;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TextStyle {
|
pub struct TextStyle {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
pub font_family_name: Arc<str>,
|
||||||
|
pub font_id: FontId,
|
||||||
|
pub font_size: f32,
|
||||||
pub font_properties: Properties,
|
pub font_properties: Properties,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextStyle {
|
#[derive(Clone, Debug, Default)]
|
||||||
fn default() -> Self {
|
pub struct HighlightStyle {
|
||||||
Self {
|
pub color: Color,
|
||||||
color: Color::from_u32(0xff0000ff),
|
pub font_properties: Properties,
|
||||||
font_properties: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
@ -43,24 +46,117 @@ enum WeightJson {
|
||||||
black,
|
black,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static FONT_CACHE: RefCell<Option<Arc<FontCache>>> = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct TextStyleJson {
|
struct TextStyleJson {
|
||||||
|
color: Color,
|
||||||
|
family: String,
|
||||||
|
weight: Option<WeightJson>,
|
||||||
|
#[serde(default)]
|
||||||
|
italic: bool,
|
||||||
|
size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct HighlightStyleJson {
|
||||||
color: Color,
|
color: Color,
|
||||||
weight: Option<WeightJson>,
|
weight: Option<WeightJson>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
italic: bool,
|
italic: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TextStyle {
|
||||||
|
pub fn new(
|
||||||
|
font_family_name: impl Into<Arc<str>>,
|
||||||
|
font_size: f32,
|
||||||
|
font_properties: Properties,
|
||||||
|
color: Color,
|
||||||
|
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)?;
|
||||||
|
Ok(Self {
|
||||||
|
color,
|
||||||
|
font_family_name,
|
||||||
|
font_id,
|
||||||
|
font_size,
|
||||||
|
font_properties,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_json(json: TextStyleJson) -> anyhow::Result<Self> {
|
||||||
|
FONT_CACHE.with(|font_cache| {
|
||||||
|
if let Some(font_cache) = font_cache.borrow().as_ref() {
|
||||||
|
let font_properties = properties_from_json(json.weight, json.italic);
|
||||||
|
Self::new(
|
||||||
|
json.family,
|
||||||
|
json.size,
|
||||||
|
font_properties,
|
||||||
|
json.color,
|
||||||
|
font_cache,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Err(anyhow!(
|
||||||
|
"TextStyle can only be deserialized within a call to with_font_cache"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HighlightStyle {
|
||||||
|
fn from_json(json: HighlightStyleJson) -> Self {
|
||||||
|
let font_properties = properties_from_json(json.weight, json.italic);
|
||||||
|
Self {
|
||||||
|
color: json.color,
|
||||||
|
font_properties,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Color> for HighlightStyle {
|
||||||
|
fn from(color: Color) -> Self {
|
||||||
|
Self {
|
||||||
|
color,
|
||||||
|
font_properties: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for TextStyle {
|
impl<'de> Deserialize<'de> for TextStyle {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let json = Value::deserialize(deserializer)?;
|
Ok(Self::from_json(TextStyleJson::deserialize(deserializer)?)
|
||||||
|
.map_err(|e| de::Error::custom(e))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToJson for TextStyle {
|
||||||
|
fn to_json(&self) -> Value {
|
||||||
|
json!({
|
||||||
|
"color": self.color.to_json(),
|
||||||
|
"font_family": self.font_family_name.as_ref(),
|
||||||
|
"font_properties": self.font_properties.to_json(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for HighlightStyle {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let json = serde_json::Value::deserialize(deserializer)?;
|
||||||
if json.is_object() {
|
if json.is_object() {
|
||||||
let style_json: TextStyleJson =
|
Ok(Self::from_json(
|
||||||
serde_json::from_value(json).map_err(de::Error::custom)?;
|
serde_json::from_value(json).map_err(de::Error::custom)?,
|
||||||
Ok(style_json.into())
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
color: serde_json::from_value(json).map_err(de::Error::custom)?,
|
color: serde_json::from_value(json).map_err(de::Error::custom)?,
|
||||||
|
@ -70,27 +166,8 @@ impl<'de> Deserialize<'de> for TextStyle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for TextStyle {
|
fn properties_from_json(weight: Option<WeightJson>, italic: bool) -> Properties {
|
||||||
fn from(color: Color) -> Self {
|
let weight = match weight.unwrap_or(WeightJson::normal) {
|
||||||
Self {
|
|
||||||
color,
|
|
||||||
font_properties: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToJson for TextStyle {
|
|
||||||
fn to_json(&self) -> Value {
|
|
||||||
json!({
|
|
||||||
"color": self.color.to_json(),
|
|
||||||
"font_properties": self.font_properties.to_json(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<TextStyle> for TextStyleJson {
|
|
||||||
fn into(self) -> TextStyle {
|
|
||||||
let weight = match self.weight.unwrap_or(WeightJson::normal) {
|
|
||||||
WeightJson::thin => Weight::THIN,
|
WeightJson::thin => Weight::THIN,
|
||||||
WeightJson::extra_light => Weight::EXTRA_LIGHT,
|
WeightJson::extra_light => Weight::EXTRA_LIGHT,
|
||||||
WeightJson::light => Weight::LIGHT,
|
WeightJson::light => Weight::LIGHT,
|
||||||
|
@ -101,16 +178,8 @@ impl Into<TextStyle> for TextStyleJson {
|
||||||
WeightJson::extra_bold => Weight::EXTRA_BOLD,
|
WeightJson::extra_bold => Weight::EXTRA_BOLD,
|
||||||
WeightJson::black => Weight::BLACK,
|
WeightJson::black => Weight::BLACK,
|
||||||
};
|
};
|
||||||
let style = if self.italic {
|
let style = if italic { Style::Italic } else { Style::Normal };
|
||||||
Style::Italic
|
*Properties::new().weight(weight).style(style)
|
||||||
} else {
|
|
||||||
Style::Normal
|
|
||||||
};
|
|
||||||
TextStyle {
|
|
||||||
color: self.color,
|
|
||||||
font_properties: *Properties::new().weight(weight).style(style),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToJson for Properties {
|
impl ToJson for Properties {
|
||||||
|
@ -164,3 +233,15 @@ impl ToJson for Stretch {
|
||||||
json!(self.0)
|
json!(self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_font_cache<F, T>(font_cache: Arc<FontCache>, callback: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce() -> T,
|
||||||
|
{
|
||||||
|
FONT_CACHE.with(|cache| {
|
||||||
|
*cache.borrow_mut() = Some(font_cache);
|
||||||
|
let result = callback();
|
||||||
|
cache.borrow_mut().take();
|
||||||
|
result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -943,7 +943,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_share_worktree(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
|
async fn test_share_worktree(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
|
||||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||||
let settings = settings::channel(&cx_b.font_cache()).unwrap().1;
|
let settings = cx_b.read(settings::test).1;
|
||||||
let lang_registry = Arc::new(LanguageRegistry::new());
|
let lang_registry = Arc::new(LanguageRegistry::new());
|
||||||
|
|
||||||
// Connect to a server as 2 clients.
|
// Connect to a server as 2 clients.
|
||||||
|
|
|
@ -4,7 +4,7 @@ background = "$surface.0"
|
||||||
[workspace.tab]
|
[workspace.tab]
|
||||||
text = "$text.2"
|
text = "$text.2"
|
||||||
padding = { left = 10, right = 10 }
|
padding = { left = 10, right = 10 }
|
||||||
icon_close = "$text.0"
|
icon_close = "$text.0.color"
|
||||||
icon_dirty = "$status.info"
|
icon_dirty = "$status.info"
|
||||||
icon_conflict = "$status.warn"
|
icon_conflict = "$status.warn"
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@ text = "$text.0"
|
||||||
padding = { left = 10, right = 10 }
|
padding = { left = 10, right = 10 }
|
||||||
|
|
||||||
[workspace.sidebar_icon]
|
[workspace.sidebar_icon]
|
||||||
color = "$text.2"
|
color = "$text.2.color"
|
||||||
|
|
||||||
[workspace.active_sidebar_icon]
|
[workspace.active_sidebar_icon]
|
||||||
color = "$text.0"
|
color = "$text.0.color"
|
||||||
|
|
||||||
[chat_panel]
|
[chat_panel]
|
||||||
padding = { top = 10.0, bottom = 10.0, left = 10.0, right = 10.0 }
|
padding = { top = 10.0, bottom = 10.0, left = 10.0, right = 10.0 }
|
||||||
|
@ -28,7 +28,7 @@ padding = { top = 10.0, bottom = 10.0, left = 10.0, right = 10.0 }
|
||||||
[chat_panel.message]
|
[chat_panel.message]
|
||||||
body = "$text.0"
|
body = "$text.0"
|
||||||
sender.margin.right = 10.0
|
sender.margin.right = 10.0
|
||||||
sender.text = { color = "$text.0", weight = "bold" }
|
sender.text = { extends = "$text.0", weight = "bold" }
|
||||||
timestamp.text = "$text.2"
|
timestamp.text = "$text.2"
|
||||||
|
|
||||||
[selector]
|
[selector]
|
||||||
|
@ -41,8 +41,8 @@ shadow = { offset = [0.0, 0.0], blur = 12.0, color = "#00000088" }
|
||||||
|
|
||||||
[selector.item]
|
[selector.item]
|
||||||
background = "#424344"
|
background = "#424344"
|
||||||
text = "#cccccc"
|
text = "$text.1"
|
||||||
highlight_text = { color = "#18a3ff", weight = "bold" }
|
highlight_text = { extends = "$text.base", color = "#18a3ff", weight = "bold" }
|
||||||
border = { color = "#000000", width = 1.0 }
|
border = { color = "#000000", width = 1.0 }
|
||||||
padding = { top = 6.0, bottom = 6.0, left = 6.0, right = 6.0 }
|
padding = { top = 6.0, bottom = 6.0, left = 6.0, right = 6.0 }
|
||||||
|
|
||||||
|
@ -54,10 +54,12 @@ background = "#094771"
|
||||||
background = "$surface.1"
|
background = "$surface.1"
|
||||||
gutter_background = "$surface.1"
|
gutter_background = "$surface.1"
|
||||||
active_line_background = "$surface.2"
|
active_line_background = "$surface.2"
|
||||||
line_number = "$text.2"
|
line_number = "$text.2.color"
|
||||||
line_number_active = "$text.0"
|
line_number_active = "$text.0.color"
|
||||||
text = "$text.1"
|
|
||||||
replicas = [
|
replicas = [
|
||||||
{ selection = "#264f78", cursor = "$text.0" },
|
{ selection = "#264f78", cursor = "$text.0.color" },
|
||||||
{ selection = "#504f31", cursor = "#fcf154" },
|
{ selection = "#504f31", cursor = "#fcf154" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[syntax]
|
||||||
|
default = "$text.1.color"
|
||||||
|
|
|
@ -6,9 +6,10 @@ extends = "_base"
|
||||||
2 = "#131415"
|
2 = "#131415"
|
||||||
|
|
||||||
[text]
|
[text]
|
||||||
0 = "#ffffff"
|
base = { family = "Helvetica", size = 12.0 }
|
||||||
1 = "#b3b3b3"
|
0 = { extends = "$text.base", color = "#ffffff" }
|
||||||
2 = "#7b7d80"
|
1 = { extends = "$text.base", color = "#b3b3b3" }
|
||||||
|
2 = { extends = "$text.base", color = "#7b7d80" }
|
||||||
|
|
||||||
[status]
|
[status]
|
||||||
good = "#4fac63"
|
good = "#4fac63"
|
||||||
|
|
|
@ -7,9 +7,10 @@ extends = "_base"
|
||||||
3 = "#3a3b3c"
|
3 = "#3a3b3c"
|
||||||
|
|
||||||
[text]
|
[text]
|
||||||
0 = "#acacac"
|
base = { family = "Inconsolata" }
|
||||||
1 = "#111111"
|
0 = { extends = "$text.base", color = "#acacac" }
|
||||||
2 = "#333333"
|
1 = { extends = "$text.base", color = "#111111" }
|
||||||
|
2 = { extends = "$text.base", color = "#333333" }
|
||||||
|
|
||||||
[status]
|
[status]
|
||||||
good = "#4fac63"
|
good = "#4fac63"
|
||||||
|
|
|
@ -126,10 +126,8 @@ impl ChatPanel {
|
||||||
Container::new(
|
Container::new(
|
||||||
Label::new(
|
Label::new(
|
||||||
message.sender.github_login.clone(),
|
message.sender.github_login.clone(),
|
||||||
settings.ui_font_family,
|
theme.sender.label.clone(),
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
)
|
||||||
.with_style(&theme.sender.label)
|
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_style(&theme.sender.container)
|
.with_style(&theme.sender.container)
|
||||||
|
@ -139,10 +137,8 @@ impl ChatPanel {
|
||||||
Container::new(
|
Container::new(
|
||||||
Label::new(
|
Label::new(
|
||||||
format_timestamp(message.timestamp, now),
|
format_timestamp(message.timestamp, now),
|
||||||
settings.ui_font_family,
|
theme.timestamp.label.clone(),
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
)
|
||||||
.with_style(&theme.timestamp.label)
|
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_style(&theme.timestamp.container)
|
.with_style(&theme.timestamp.container)
|
||||||
|
@ -150,15 +146,7 @@ impl ChatPanel {
|
||||||
)
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(Text::new(message.body.clone(), theme.body.clone()).boxed())
|
||||||
Text::new(
|
|
||||||
message.body.clone(),
|
|
||||||
settings.ui_font_family,
|
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
|
||||||
.with_style(&theme.body)
|
|
||||||
.boxed(),
|
|
||||||
)
|
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2418,7 +2418,7 @@ impl Snapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||||
let style = self.theme.highlight_style(style_ix);
|
let style = self.theme.syntax.highlight_style(style_ix);
|
||||||
// Avoid a lookup if the font properties match the previous ones.
|
// 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 style.font_properties == prev_font_properties {
|
||||||
prev_font_id
|
prev_font_id
|
||||||
|
@ -2632,19 +2632,14 @@ impl workspace::ItemView for Editor {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{editor::Point, language::LanguageRegistry, settings, test::sample_text};
|
||||||
editor::Point,
|
|
||||||
language::LanguageRegistry,
|
|
||||||
settings,
|
|
||||||
test::{build_settings, sample_text},
|
|
||||||
};
|
|
||||||
use buffer::History;
|
use buffer::History;
|
||||||
use unindent::Unindent;
|
use unindent::Unindent;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
|
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 buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -2712,7 +2707,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
|
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 buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -2746,7 +2741,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_cancel(cx: &mut gpui::MutableAppContext) {
|
fn test_cancel(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -2792,7 +2787,7 @@ mod tests {
|
||||||
|
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
||||||
|
|
||||||
let settings = settings::channel(&font_cache).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
let (_, editor) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings.clone(), cx)
|
Editor::for_buffer(buffer.clone(), settings.clone(), cx)
|
||||||
});
|
});
|
||||||
|
@ -2838,7 +2833,7 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -2906,7 +2901,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
|
fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -2983,7 +2978,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
|
fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3041,7 +3036,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
|
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 buffer = cx.add_model(|cx| Buffer::new(0, "ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3072,7 +3067,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
|
fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\n def", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\n def", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3217,7 +3212,7 @@ mod tests {
|
||||||
fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
|
fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer =
|
let buffer =
|
||||||
cx.add_model(|cx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", cx));
|
cx.add_model(|cx| Buffer::new(0, "use std::str::{foo, bar}\n\n {baz.qux()}", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3405,7 +3400,7 @@ mod tests {
|
||||||
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
|
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer =
|
let buffer =
|
||||||
cx.add_model(|cx| Buffer::new(0, "use one::{\n two::three::four::five\n};", cx));
|
cx.add_model(|cx| Buffer::new(0, "use one::{\n two::three::four::five\n};", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3467,7 +3462,7 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3503,7 +3498,7 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3532,7 +3527,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_delete_line(cx: &mut gpui::MutableAppContext) {
|
fn test_delete_line(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3558,7 +3553,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3577,7 +3572,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
|
fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3606,7 +3601,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndef\nghi\n", cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3634,7 +3629,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
|
fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(10, 5), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(10, 5), cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3719,7 +3714,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_clipboard(cx: &mut gpui::MutableAppContext) {
|
fn test_clipboard(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "one two three four five six ", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "one two three four five six ", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let view = cx
|
let view = cx
|
||||||
.add_window(Default::default(), |cx| {
|
.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), settings, cx)
|
Editor::for_buffer(buffer.clone(), settings, cx)
|
||||||
|
@ -3854,7 +3849,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_select_all(cx: &mut gpui::MutableAppContext) {
|
fn test_select_all(cx: &mut gpui::MutableAppContext) {
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\nde\nfgh", cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\nde\nfgh", cx));
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
});
|
});
|
||||||
|
@ -3869,7 +3864,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_select_line(cx: &mut gpui::MutableAppContext) {
|
fn test_select_line(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 5), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 5), cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3917,7 +3912,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
|
fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(9, 5), cx));
|
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(9, 5), cx));
|
||||||
let (_, view) = cx.add_window(Default::default(), |cx| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -3984,7 +3979,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
|
fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = settings::channel(&cx.font_cache()).unwrap().1;
|
let settings = settings::test(&cx).1;
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "abc\ndefghi\n\njk\nlmno\n", 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| {
|
let (_, view) = cx.add_window(Default::default(), |cx| {
|
||||||
Editor::for_buffer(buffer, settings, cx)
|
Editor::for_buffer(buffer, settings, cx)
|
||||||
|
@ -4159,7 +4154,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
|
async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
|
||||||
let settings = cx.read(build_settings);
|
let settings = cx.read(settings::test).1;
|
||||||
let languages = LanguageRegistry::new();
|
let languages = LanguageRegistry::new();
|
||||||
let lang = languages.select_language("z.rs");
|
let lang = languages.select_language("z.rs");
|
||||||
let text = r#"
|
let text = r#"
|
||||||
|
|
|
@ -343,8 +343,8 @@ mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::movement,
|
editor::movement,
|
||||||
language::{Language, LanguageConfig},
|
language::{Language, LanguageConfig},
|
||||||
settings::Theme,
|
|
||||||
test::*,
|
test::*,
|
||||||
|
theme::SyntaxTheme,
|
||||||
util::RandomCharIter,
|
util::RandomCharIter,
|
||||||
};
|
};
|
||||||
use buffer::{History, SelectionGoal};
|
use buffer::{History, SelectionGoal};
|
||||||
|
@ -366,7 +366,7 @@ mod tests {
|
||||||
tab_size: rng.gen_range(1..=4),
|
tab_size: rng.gen_range(1..=4),
|
||||||
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
||||||
buffer_font_size: 14.0,
|
buffer_font_size: 14.0,
|
||||||
..Settings::new(&font_cache).unwrap()
|
..cx.read(Settings::test)
|
||||||
};
|
};
|
||||||
let max_wrap_width = 300.0;
|
let max_wrap_width = 300.0;
|
||||||
let mut wrap_width = if rng.gen_bool(0.1) {
|
let mut wrap_width = if rng.gen_bool(0.1) {
|
||||||
|
@ -535,7 +535,7 @@ mod tests {
|
||||||
buffer_font_size: 12.0,
|
buffer_font_size: 12.0,
|
||||||
ui_font_size: 12.0,
|
ui_font_size: 12.0,
|
||||||
tab_size: 4,
|
tab_size: 4,
|
||||||
theme: Arc::new(Theme::default()),
|
..cx.read(Settings::test)
|
||||||
};
|
};
|
||||||
let wrap_width = Some(64.);
|
let wrap_width = Some(64.);
|
||||||
|
|
||||||
|
@ -606,7 +606,10 @@ mod tests {
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
|
Settings {
|
||||||
|
tab_size: 4,
|
||||||
|
..Settings::test(cx)
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -660,13 +663,13 @@ mod tests {
|
||||||
(function_item name: (identifier) @fn.name)"#,
|
(function_item name: (identifier) @fn.name)"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let theme = Theme {
|
let theme = SyntaxTheme::new(
|
||||||
syntax: vec![
|
Default::default(),
|
||||||
|
vec![
|
||||||
("mod.body".to_string(), Color::from_u32(0xff0000ff).into()),
|
("mod.body".to_string(), Color::from_u32(0xff0000ff).into()),
|
||||||
("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()),
|
("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()),
|
||||||
],
|
],
|
||||||
..Default::default()
|
);
|
||||||
};
|
|
||||||
let lang = Arc::new(Language {
|
let lang = Arc::new(Language {
|
||||||
config: LanguageConfig {
|
config: LanguageConfig {
|
||||||
name: "Test".to_string(),
|
name: "Test".to_string(),
|
||||||
|
@ -688,7 +691,10 @@ mod tests {
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer,
|
buffer,
|
||||||
Settings::new(cx.font_cache()).unwrap().with_tab_size(2),
|
Settings {
|
||||||
|
tab_size: 2,
|
||||||
|
..Settings::test(cx)
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -750,13 +756,13 @@ mod tests {
|
||||||
(function_item name: (identifier) @fn.name)"#,
|
(function_item name: (identifier) @fn.name)"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let theme = Theme {
|
let theme = SyntaxTheme::new(
|
||||||
syntax: vec![
|
Default::default(),
|
||||||
|
vec![
|
||||||
("mod.body".to_string(), Color::from_u32(0xff0000ff).into()),
|
("mod.body".to_string(), Color::from_u32(0xff0000ff).into()),
|
||||||
("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()),
|
("fn.name".to_string(), Color::from_u32(0x00ff00ff).into()),
|
||||||
],
|
],
|
||||||
..Default::default()
|
);
|
||||||
};
|
|
||||||
let lang = Arc::new(Language {
|
let lang = Arc::new(Language {
|
||||||
config: LanguageConfig {
|
config: LanguageConfig {
|
||||||
name: "Test".to_string(),
|
name: "Test".to_string(),
|
||||||
|
@ -780,7 +786,7 @@ mod tests {
|
||||||
tab_size: 4,
|
tab_size: 4,
|
||||||
buffer_font_family: font_cache.load_family(&["Courier"]).unwrap(),
|
buffer_font_family: font_cache.load_family(&["Courier"]).unwrap(),
|
||||||
buffer_font_size: 16.0,
|
buffer_font_size: 16.0,
|
||||||
..Settings::new(&font_cache).unwrap()
|
..cx.read(Settings::test)
|
||||||
};
|
};
|
||||||
let map = cx.add_model(|cx| DisplayMap::new(buffer, settings, Some(40.0), cx));
|
let map = cx.add_model(|cx| DisplayMap::new(buffer, settings, Some(40.0), cx));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -820,7 +826,10 @@ mod tests {
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
|
Settings {
|
||||||
|
tab_size: 4,
|
||||||
|
..Settings::test(cx)
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -861,7 +870,10 @@ mod tests {
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
|
Settings {
|
||||||
|
tab_size: 4,
|
||||||
|
..Settings::test(cx)
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -925,7 +937,10 @@ mod tests {
|
||||||
let map = cx.add_model(|cx| {
|
let map = cx.add_model(|cx| {
|
||||||
DisplayMap::new(
|
DisplayMap::new(
|
||||||
buffer.clone(),
|
buffer.clone(),
|
||||||
Settings::new(cx.font_cache()).unwrap().with_tab_size(4),
|
Settings {
|
||||||
|
tab_size: 4,
|
||||||
|
..Settings::test(cx)
|
||||||
|
},
|
||||||
None,
|
None,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
|
@ -939,7 +954,7 @@ mod tests {
|
||||||
fn highlighted_chunks<'a>(
|
fn highlighted_chunks<'a>(
|
||||||
rows: Range<u32>,
|
rows: Range<u32>,
|
||||||
map: &ModelHandle<DisplayMap>,
|
map: &ModelHandle<DisplayMap>,
|
||||||
theme: &'a Theme,
|
theme: &'a SyntaxTheme,
|
||||||
cx: &mut MutableAppContext,
|
cx: &mut MutableAppContext,
|
||||||
) -> Vec<(String, Option<&'a str>)> {
|
) -> Vec<(String, Option<&'a str>)> {
|
||||||
let mut snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
let mut snapshot = map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
|
@ -921,7 +921,7 @@ mod tests {
|
||||||
tab_size: rng.gen_range(1..=4),
|
tab_size: rng.gen_range(1..=4),
|
||||||
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
|
||||||
buffer_font_size: 14.0,
|
buffer_font_size: 14.0,
|
||||||
..Settings::new(&font_cache).unwrap()
|
..cx.read(Settings::test)
|
||||||
};
|
};
|
||||||
log::info!("Tab size: {}", settings.tab_size);
|
log::info!("Tab size: {}", settings.tab_size);
|
||||||
log::info!("Wrap width: {:?}", wrap_width);
|
log::info!("Wrap width: {:?}", wrap_width);
|
||||||
|
|
|
@ -182,12 +182,12 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::{display_map::DisplayMap, Buffer},
|
editor::{display_map::DisplayMap, Buffer},
|
||||||
test::build_app_state,
|
test::test_app_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
|
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
|
||||||
let settings = build_app_state(cx).settings.borrow().clone();
|
let settings = test_app_state(cx).settings.borrow().clone();
|
||||||
let buffer = cx.add_model(|cx| Buffer::new(0, "a bcΔ defγ", cx));
|
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, settings, None, cx));
|
||||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||||
|
|
|
@ -117,13 +117,7 @@ impl FileFinder {
|
||||||
if self.matches.is_empty() {
|
if self.matches.is_empty() {
|
||||||
let settings = self.settings.borrow();
|
let settings = self.settings.borrow();
|
||||||
return Container::new(
|
return Container::new(
|
||||||
Label::new(
|
Label::new("No matches".into(), settings.theme.selector.label.clone()).boxed(),
|
||||||
"No matches".into(),
|
|
||||||
settings.ui_font_family,
|
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
|
||||||
.with_style(&settings.theme.selector.label)
|
|
||||||
.boxed(),
|
|
||||||
)
|
)
|
||||||
.with_margin_top(6.0)
|
.with_margin_top(6.0)
|
||||||
.named("empty matches");
|
.named("empty matches");
|
||||||
|
@ -184,22 +178,12 @@ impl FileFinder {
|
||||||
1.0,
|
1.0,
|
||||||
Flex::column()
|
Flex::column()
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(
|
Label::new(file_name.to_string(), style.label.clone())
|
||||||
file_name.to_string(),
|
|
||||||
settings.ui_font_family,
|
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
|
||||||
.with_style(&style.label)
|
|
||||||
.with_highlights(file_name_positions)
|
.with_highlights(file_name_positions)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_child(
|
.with_child(
|
||||||
Label::new(
|
Label::new(full_path, style.label.clone())
|
||||||
full_path,
|
|
||||||
settings.ui_font_family,
|
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
|
||||||
.with_style(&style.label)
|
|
||||||
.with_highlights(full_path_positions)
|
.with_highlights(full_path_positions)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
|
@ -438,7 +422,7 @@ mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::{self, Insert},
|
editor::{self, Insert},
|
||||||
fs::FakeFs,
|
fs::FakeFs,
|
||||||
test::{build_app_state, temp_tree},
|
test::{temp_tree, test_app_state},
|
||||||
workspace::Workspace,
|
workspace::Workspace,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -456,7 +440,7 @@ mod tests {
|
||||||
editor::init(cx);
|
editor::init(cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
workspace
|
workspace
|
||||||
.update(&mut cx, |workspace, cx| {
|
.update(&mut cx, |workspace, cx| {
|
||||||
|
@ -516,7 +500,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut app_state = cx.update(build_app_state);
|
let mut app_state = cx.update(test_app_state);
|
||||||
Arc::get_mut(&mut app_state).unwrap().fs = fs;
|
Arc::get_mut(&mut app_state).unwrap().fs = fs;
|
||||||
|
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
|
@ -578,7 +562,7 @@ mod tests {
|
||||||
fs::create_dir(&dir_path).unwrap();
|
fs::create_dir(&dir_path).unwrap();
|
||||||
fs::write(&file_path, "").unwrap();
|
fs::write(&file_path, "").unwrap();
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
workspace
|
workspace
|
||||||
.update(&mut cx, |workspace, cx| {
|
.update(&mut cx, |workspace, cx| {
|
||||||
|
@ -625,7 +609,7 @@ mod tests {
|
||||||
"dir2": { "a.txt": "" }
|
"dir2": { "a.txt": "" }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
|
|
||||||
workspace
|
workspace
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::settings::{HighlightMap, Theme};
|
use crate::{settings::HighlightMap, theme::SyntaxTheme};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -39,7 +39,7 @@ impl Language {
|
||||||
self.highlight_map.lock().clone()
|
self.highlight_map.lock().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_theme(&self, theme: &Theme) {
|
pub fn set_theme(&self, theme: &SyntaxTheme) {
|
||||||
*self.highlight_map.lock() = HighlightMap::new(self.highlight_query.capture_names(), theme);
|
*self.highlight_map.lock() = HighlightMap::new(self.highlight_query.capture_names(), theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ impl LanguageRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_theme(&self, theme: &Theme) {
|
pub fn set_theme(&self, theme: &SyntaxTheme) {
|
||||||
for language in &self.languages {
|
for language in &self.languages {
|
||||||
language.set_theme(theme);
|
language.set_theme(theme);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,10 @@ fn main() {
|
||||||
|
|
||||||
let app = gpui::App::new(assets::Assets).unwrap();
|
let app = gpui::App::new(assets::Assets).unwrap();
|
||||||
|
|
||||||
let themes = settings::ThemeRegistry::new(assets::Assets);
|
let themes = settings::ThemeRegistry::new(assets::Assets, app.font_cache());
|
||||||
let (settings_tx, settings) =
|
let (settings_tx, settings) = settings::channel(&app.font_cache(), &themes).unwrap();
|
||||||
settings::channel_with_themes(&app.font_cache(), &themes).unwrap();
|
|
||||||
let languages = Arc::new(language::LanguageRegistry::new());
|
let languages = Arc::new(language::LanguageRegistry::new());
|
||||||
languages.set_theme(&settings.borrow().theme);
|
languages.set_theme(&settings.borrow().theme.syntax);
|
||||||
|
|
||||||
app.run(move |cx| {
|
app.run(move |cx| {
|
||||||
let rpc = rpc::Client::new();
|
let rpc = rpc::Client::new();
|
||||||
|
|
|
@ -17,11 +17,27 @@ pub struct Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
pub fn new(font_cache: &FontCache) -> Result<Self> {
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
Self::new_with_theme(font_cache, Arc::new(Theme::default()))
|
pub fn test(cx: &gpui::AppContext) -> Self {
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref DEFAULT_THEME: parking_lot::Mutex<Option<Arc<Theme>>> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_theme(font_cache: &FontCache, theme: Arc<Theme>) -> Result<Self> {
|
let mut theme_guard = DEFAULT_THEME.lock();
|
||||||
|
let theme = if let Some(theme) = theme_guard.as_ref() {
|
||||||
|
theme.clone()
|
||||||
|
} else {
|
||||||
|
let theme = ThemeRegistry::new(crate::assets::Assets, cx.font_cache().clone())
|
||||||
|
.get(DEFAULT_THEME_NAME)
|
||||||
|
.expect("failed to load default theme in tests");
|
||||||
|
*theme_guard = Some(theme.clone());
|
||||||
|
theme
|
||||||
|
};
|
||||||
|
|
||||||
|
Self::new(cx.font_cache(), theme).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(font_cache: &FontCache, theme: Arc<Theme>) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
buffer_font_family: font_cache.load_family(&["Fira Code", "Monaco"])?,
|
buffer_font_family: font_cache.load_family(&["Fira Code", "Monaco"])?,
|
||||||
buffer_font_size: 14.0,
|
buffer_font_size: 14.0,
|
||||||
|
@ -38,13 +54,12 @@ impl Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel(
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
font_cache: &FontCache,
|
pub fn test(cx: &gpui::AppContext) -> (watch::Sender<Settings>, watch::Receiver<Settings>) {
|
||||||
) -> Result<(watch::Sender<Settings>, watch::Receiver<Settings>)> {
|
watch::channel_with(Settings::test(cx))
|
||||||
Ok(watch::channel_with(Settings::new(font_cache)?))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_with_themes(
|
pub fn channel(
|
||||||
font_cache: &FontCache,
|
font_cache: &FontCache,
|
||||||
themes: &ThemeRegistry,
|
themes: &ThemeRegistry,
|
||||||
) -> Result<(watch::Sender<Settings>, watch::Receiver<Settings>)> {
|
) -> Result<(watch::Sender<Settings>, watch::Receiver<Settings>)> {
|
||||||
|
@ -54,7 +69,5 @@ pub fn channel_with_themes(
|
||||||
panic!("failed to deserialize default theme: {:?}", err)
|
panic!("failed to deserialize default theme: {:?}", err)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(watch::channel_with(Settings::new_with_theme(
|
Ok(watch::channel_with(Settings::new(font_cache, theme)?))
|
||||||
font_cache, theme,
|
|
||||||
)?))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,10 @@ use crate::{
|
||||||
settings::{self, ThemeRegistry},
|
settings::{self, ThemeRegistry},
|
||||||
time::ReplicaId,
|
time::ReplicaId,
|
||||||
user::UserStore,
|
user::UserStore,
|
||||||
AppState, Settings,
|
AppState,
|
||||||
};
|
};
|
||||||
use gpui::{AppContext, Entity, ModelHandle, MutableAppContext};
|
use gpui::{Entity, ModelHandle, MutableAppContext};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use postage::watch;
|
|
||||||
use smol::channel;
|
use smol::channel;
|
||||||
use std::{
|
use std::{
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
@ -156,14 +155,10 @@ fn write_tree(path: &Path, tree: serde_json::Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_settings(cx: &AppContext) -> watch::Receiver<Settings> {
|
pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
|
||||||
settings::channel(&cx.font_cache()).unwrap().1
|
let (settings_tx, settings) = settings::test(cx);
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
|
|
||||||
let (settings_tx, settings) = settings::channel(&cx.font_cache()).unwrap();
|
|
||||||
let languages = Arc::new(LanguageRegistry::new());
|
let languages = Arc::new(LanguageRegistry::new());
|
||||||
let themes = ThemeRegistry::new(());
|
let themes = ThemeRegistry::new((), cx.font_cache().clone());
|
||||||
let rpc = rpc::Client::new();
|
let rpc = rpc::Client::new();
|
||||||
let user_store = Arc::new(UserStore::new(rpc.clone()));
|
let user_store = Arc::new(UserStore::new(rpc.clone()));
|
||||||
Arc::new(AppState {
|
Arc::new(AppState {
|
||||||
|
|
|
@ -5,9 +5,9 @@ use anyhow::Result;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
elements::{ContainerStyle, LabelStyle},
|
elements::{ContainerStyle, LabelStyle},
|
||||||
fonts::TextStyle,
|
fonts::{HighlightStyle, TextStyle},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{de, Deserialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub use highlight_map::*;
|
pub use highlight_map::*;
|
||||||
|
@ -15,7 +15,7 @@ pub use theme_registry::*;
|
||||||
|
|
||||||
pub const DEFAULT_THEME_NAME: &'static str = "dark";
|
pub const DEFAULT_THEME_NAME: &'static str = "dark";
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -23,11 +23,15 @@ pub struct Theme {
|
||||||
pub chat_panel: ChatPanel,
|
pub chat_panel: ChatPanel,
|
||||||
pub selector: Selector,
|
pub selector: Selector,
|
||||||
pub editor: Editor,
|
pub editor: Editor,
|
||||||
#[serde(deserialize_with = "deserialize_syntax_theme")]
|
pub syntax: SyntaxTheme,
|
||||||
pub syntax: Vec<(String, TextStyle)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
pub struct SyntaxTheme {
|
||||||
|
highlights: Vec<(String, HighlightStyle)>,
|
||||||
|
default_style: HighlightStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
|
@ -37,7 +41,7 @@ pub struct Workspace {
|
||||||
pub active_sidebar_icon: SidebarIcon,
|
pub active_sidebar_icon: SidebarIcon,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -48,26 +52,26 @@ pub struct Tab {
|
||||||
pub icon_conflict: Color,
|
pub icon_conflict: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct SidebarIcon {
|
pub struct SidebarIcon {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct ChatPanel {
|
pub struct ChatPanel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
pub message: ChatMessage,
|
pub message: ChatMessage,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct ChatMessage {
|
pub struct ChatMessage {
|
||||||
pub body: TextStyle,
|
pub body: TextStyle,
|
||||||
pub sender: ContainedLabel,
|
pub sender: ContainedLabel,
|
||||||
pub timestamp: ContainedLabel,
|
pub timestamp: ContainedLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Selector {
|
pub struct Selector {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -78,7 +82,7 @@ pub struct Selector {
|
||||||
pub active_item: ContainedLabel,
|
pub active_item: ContainedLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct ContainedLabel {
|
pub struct ContainedLabel {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub container: ContainerStyle,
|
pub container: ContainerStyle,
|
||||||
|
@ -86,70 +90,69 @@ pub struct ContainedLabel {
|
||||||
pub label: LabelStyle,
|
pub label: LabelStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
pub gutter_background: Color,
|
pub gutter_background: Color,
|
||||||
pub active_line_background: Color,
|
pub active_line_background: Color,
|
||||||
pub line_number: Color,
|
pub line_number: Color,
|
||||||
pub line_number_active: Color,
|
pub line_number_active: Color,
|
||||||
pub text: Color,
|
|
||||||
pub replicas: Vec<Replica>,
|
pub replicas: Vec<Replica>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize)]
|
#[derive(Clone, Copy, Deserialize)]
|
||||||
pub struct Replica {
|
pub struct Replica {
|
||||||
pub cursor: Color,
|
pub cursor: Color,
|
||||||
pub selection: Color,
|
pub selection: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl SyntaxTheme {
|
||||||
pub fn highlight_style(&self, id: HighlightId) -> TextStyle {
|
pub fn new(default_style: HighlightStyle, highlights: Vec<(String, HighlightStyle)>) -> Self {
|
||||||
self.syntax
|
Self {
|
||||||
|
default_style,
|
||||||
|
highlights,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn highlight_style(&self, id: HighlightId) -> HighlightStyle {
|
||||||
|
self.highlights
|
||||||
.get(id.0 as usize)
|
.get(id.0 as usize)
|
||||||
.map(|entry| entry.1.clone())
|
.map(|entry| entry.1.clone())
|
||||||
.unwrap_or_else(|| TextStyle {
|
.unwrap_or_else(|| self.default_style.clone())
|
||||||
color: self.editor.text,
|
|
||||||
font_properties: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
|
pub fn highlight_name(&self, id: HighlightId) -> Option<&str> {
|
||||||
self.syntax.get(id.0 as usize).map(|e| e.0.as_str())
|
self.highlights.get(id.0 as usize).map(|e| e.0.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Editor {
|
impl<'de> Deserialize<'de> for SyntaxTheme {
|
||||||
fn default() -> Self {
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
Self {
|
where
|
||||||
background: Default::default(),
|
D: serde::Deserializer<'de>,
|
||||||
gutter_background: Default::default(),
|
{
|
||||||
active_line_background: Default::default(),
|
let mut syntax_data: HashMap<String, HighlightStyle> =
|
||||||
line_number: Default::default(),
|
Deserialize::deserialize(deserializer)?;
|
||||||
line_number_active: Default::default(),
|
|
||||||
text: Default::default(),
|
|
||||||
replicas: vec![Replica::default()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize_syntax_theme<'de, D>(
|
let mut result = Self {
|
||||||
deserializer: D,
|
highlights: Vec::<(String, HighlightStyle)>::new(),
|
||||||
) -> Result<Vec<(String, TextStyle)>, D::Error>
|
default_style: syntax_data
|
||||||
where
|
.remove("default")
|
||||||
D: Deserializer<'de>,
|
.ok_or_else(|| de::Error::custom("must specify a default color in syntax theme"))?,
|
||||||
{
|
};
|
||||||
let mut result = Vec::<(String, TextStyle)>::new();
|
|
||||||
|
|
||||||
let syntax_data: HashMap<String, TextStyle> = Deserialize::deserialize(deserializer)?;
|
|
||||||
for (key, style) in syntax_data {
|
for (key, style) in syntax_data {
|
||||||
match result.binary_search_by(|(needle, _)| needle.cmp(&key)) {
|
match result
|
||||||
|
.highlights
|
||||||
|
.binary_search_by(|(needle, _)| needle.cmp(&key))
|
||||||
|
{
|
||||||
Ok(i) | Err(i) => {
|
Ok(i) | Err(i) => {
|
||||||
result.insert(i, (key, style));
|
result.highlights.insert(i, (key, style));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::Theme;
|
use super::SyntaxTheme;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -10,7 +10,7 @@ pub struct HighlightId(pub u32);
|
||||||
const DEFAULT_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
|
const DEFAULT_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
|
||||||
|
|
||||||
impl HighlightMap {
|
impl HighlightMap {
|
||||||
pub fn new(capture_names: &[String], theme: &Theme) -> Self {
|
pub fn new(capture_names: &[String], theme: &SyntaxTheme) -> Self {
|
||||||
// For each capture name in the highlight query, find the longest
|
// For each capture name in the highlight query, find the longest
|
||||||
// key in the theme's syntax styles that matches all of the
|
// key in the theme's syntax styles that matches all of the
|
||||||
// dot-separated components of the capture name.
|
// dot-separated components of the capture name.
|
||||||
|
@ -19,7 +19,7 @@ impl HighlightMap {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|capture_name| {
|
.map(|capture_name| {
|
||||||
theme
|
theme
|
||||||
.syntax
|
.highlights
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, (key, _))| {
|
.filter_map(|(i, (key, _))| {
|
||||||
|
@ -68,9 +68,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_highlight_map() {
|
fn test_highlight_map() {
|
||||||
let theme = Theme {
|
let theme = SyntaxTheme::new(
|
||||||
name: "test".into(),
|
Default::default(),
|
||||||
syntax: [
|
[
|
||||||
("function", Color::from_u32(0x100000ff)),
|
("function", Color::from_u32(0x100000ff)),
|
||||||
("function.method", Color::from_u32(0x200000ff)),
|
("function.method", Color::from_u32(0x200000ff)),
|
||||||
("function.async", Color::from_u32(0x300000ff)),
|
("function.async", Color::from_u32(0x300000ff)),
|
||||||
|
@ -81,8 +81,7 @@ mod tests {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, color)| (name.to_string(), (*color).into()))
|
.map(|(name, color)| (name.to_string(), (*color).into()))
|
||||||
.collect(),
|
.collect(),
|
||||||
..Default::default()
|
);
|
||||||
};
|
|
||||||
|
|
||||||
let capture_names = &[
|
let capture_names = &[
|
||||||
"function.special".to_string(),
|
"function.special".to_string(),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use gpui::AssetSource;
|
use gpui::{fonts, AssetSource, FontCache};
|
||||||
use json::{Map, Value};
|
use json::{Map, Value};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
|
@ -11,6 +11,7 @@ pub struct ThemeRegistry {
|
||||||
assets: Box<dyn AssetSource>,
|
assets: Box<dyn AssetSource>,
|
||||||
themes: Mutex<HashMap<String, Arc<Theme>>>,
|
themes: Mutex<HashMap<String, Arc<Theme>>>,
|
||||||
theme_data: Mutex<HashMap<String, Arc<Value>>>,
|
theme_data: Mutex<HashMap<String, Arc<Value>>>,
|
||||||
|
font_cache: Arc<FontCache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -38,11 +39,12 @@ enum Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThemeRegistry {
|
impl ThemeRegistry {
|
||||||
pub fn new(source: impl AssetSource) -> Arc<Self> {
|
pub fn new(source: impl AssetSource, font_cache: Arc<FontCache>) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
assets: Box::new(source),
|
assets: Box::new(source),
|
||||||
themes: Default::default(),
|
themes: Default::default(),
|
||||||
theme_data: Default::default(),
|
theme_data: Default::default(),
|
||||||
|
font_cache,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +71,10 @@ impl ThemeRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
let theme_data = self.load(name, true)?;
|
let theme_data = self.load(name, true)?;
|
||||||
let mut theme = serde_json::from_value::<Theme>(theme_data.as_ref().clone())?;
|
let mut theme = fonts::with_font_cache(self.font_cache.clone(), || {
|
||||||
|
serde_json::from_value::<Theme>(theme_data.as_ref().clone())
|
||||||
|
})?;
|
||||||
|
|
||||||
theme.name = name.into();
|
theme.name = name.into();
|
||||||
let theme = Arc::new(theme);
|
let theme = Arc::new(theme);
|
||||||
self.themes.lock().insert(name.to_string(), theme.clone());
|
self.themes.lock().insert(name.to_string(), theme.clone());
|
||||||
|
@ -512,11 +517,12 @@ fn value_at<'a>(object: &'a mut Map<String, Value>, key_path: &KeyPath) -> Optio
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{assets::Assets, theme::DEFAULT_THEME_NAME};
|
use crate::{assets::Assets, theme::DEFAULT_THEME_NAME};
|
||||||
|
use gpui::MutableAppContext;
|
||||||
use rand::{prelude::StdRng, Rng};
|
use rand::{prelude::StdRng, Rng};
|
||||||
|
|
||||||
#[test]
|
#[gpui::test]
|
||||||
fn test_bundled_themes() {
|
fn test_bundled_themes(cx: &mut MutableAppContext) {
|
||||||
let registry = ThemeRegistry::new(Assets);
|
let registry = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
||||||
let mut has_default_theme = false;
|
let mut has_default_theme = false;
|
||||||
for theme_name in registry.list() {
|
for theme_name in registry.list() {
|
||||||
let theme = registry.get(&theme_name).unwrap();
|
let theme = registry.get(&theme_name).unwrap();
|
||||||
|
@ -528,8 +534,8 @@ mod tests {
|
||||||
assert!(has_default_theme);
|
assert!(has_default_theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[gpui::test]
|
||||||
fn test_theme_extension() {
|
fn test_theme_extension(cx: &mut MutableAppContext) {
|
||||||
let assets = TestAssets(&[
|
let assets = TestAssets(&[
|
||||||
(
|
(
|
||||||
"themes/_base.toml",
|
"themes/_base.toml",
|
||||||
|
@ -568,7 +574,7 @@ mod tests {
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let registry = ThemeRegistry::new(assets);
|
let registry = ThemeRegistry::new(assets, cx.font_cache().clone());
|
||||||
let theme_data = registry.load("light", true).unwrap();
|
let theme_data = registry.load("light", true).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
theme_data.as_ref(),
|
theme_data.as_ref(),
|
||||||
|
|
|
@ -204,13 +204,7 @@ impl ThemeSelector {
|
||||||
if self.matches.is_empty() {
|
if self.matches.is_empty() {
|
||||||
let settings = self.settings.borrow();
|
let settings = self.settings.borrow();
|
||||||
return Container::new(
|
return Container::new(
|
||||||
Label::new(
|
Label::new("No matches".into(), settings.theme.selector.label.clone()).boxed(),
|
||||||
"No matches".into(),
|
|
||||||
settings.ui_font_family,
|
|
||||||
settings.ui_font_size,
|
|
||||||
)
|
|
||||||
.with_style(&settings.theme.selector.label)
|
|
||||||
.boxed(),
|
|
||||||
)
|
)
|
||||||
.with_margin_top(6.0)
|
.with_margin_top(6.0)
|
||||||
.named("empty matches");
|
.named("empty matches");
|
||||||
|
@ -247,14 +241,12 @@ impl ThemeSelector {
|
||||||
let container = Container::new(
|
let container = Container::new(
|
||||||
Label::new(
|
Label::new(
|
||||||
theme_match.string.clone(),
|
theme_match.string.clone(),
|
||||||
settings.ui_font_family,
|
if index == self.selected_index {
|
||||||
settings.ui_font_size,
|
theme.selector.active_item.label.clone()
|
||||||
)
|
|
||||||
.with_style(if index == self.selected_index {
|
|
||||||
&theme.selector.active_item.label
|
|
||||||
} else {
|
} else {
|
||||||
&theme.selector.item.label
|
theme.selector.item.label.clone()
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.with_highlights(theme_match.positions.clone())
|
.with_highlights(theme_match.positions.clone())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1018,7 +1018,7 @@ mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::{Editor, Insert},
|
editor::{Editor, Insert},
|
||||||
fs::FakeFs,
|
fs::FakeFs,
|
||||||
test::{build_app_state, temp_tree},
|
test::{temp_tree, test_app_state},
|
||||||
worktree::WorktreeHandle,
|
worktree::WorktreeHandle,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -1027,7 +1027,7 @@ mod tests {
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_open_paths_action(mut cx: gpui::TestAppContext) {
|
async fn test_open_paths_action(mut cx: gpui::TestAppContext) {
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let dir = temp_tree(json!({
|
let dir = temp_tree(json!({
|
||||||
"a": {
|
"a": {
|
||||||
"aa": null,
|
"aa": null,
|
||||||
|
@ -1100,7 +1100,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
|
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
workspace
|
workspace
|
||||||
|
@ -1204,7 +1204,7 @@ mod tests {
|
||||||
fs.insert_file("/dir1/a.txt", "".into()).await.unwrap();
|
fs.insert_file("/dir1/a.txt", "".into()).await.unwrap();
|
||||||
fs.insert_file("/dir2/b.txt", "".into()).await.unwrap();
|
fs.insert_file("/dir2/b.txt", "".into()).await.unwrap();
|
||||||
|
|
||||||
let mut app_state = cx.update(build_app_state);
|
let mut app_state = cx.update(test_app_state);
|
||||||
Arc::get_mut(&mut app_state).unwrap().fs = Arc::new(fs);
|
Arc::get_mut(&mut app_state).unwrap().fs = Arc::new(fs);
|
||||||
|
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
|
@ -1273,7 +1273,7 @@ mod tests {
|
||||||
"a.txt": "",
|
"a.txt": "",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
workspace
|
workspace
|
||||||
.update(&mut cx, |workspace, cx| {
|
.update(&mut cx, |workspace, cx| {
|
||||||
|
@ -1318,7 +1318,7 @@ mod tests {
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_open_and_save_new_file(mut cx: gpui::TestAppContext) {
|
async fn test_open_and_save_new_file(mut cx: gpui::TestAppContext) {
|
||||||
let dir = TempDir::new("test-new-file").unwrap();
|
let dir = TempDir::new("test-new-file").unwrap();
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
workspace
|
workspace
|
||||||
.update(&mut cx, |workspace, cx| {
|
.update(&mut cx, |workspace, cx| {
|
||||||
|
@ -1417,7 +1417,7 @@ mod tests {
|
||||||
async fn test_new_empty_workspace(mut cx: gpui::TestAppContext) {
|
async fn test_new_empty_workspace(mut cx: gpui::TestAppContext) {
|
||||||
cx.update(init);
|
cx.update(init);
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
cx.dispatch_global_action(OpenNew(app_state));
|
cx.dispatch_global_action(OpenNew(app_state));
|
||||||
let window_id = *cx.window_ids().first().unwrap();
|
let window_id = *cx.window_ids().first().unwrap();
|
||||||
let workspace = cx.root_view::<Workspace>(window_id).unwrap();
|
let workspace = cx.root_view::<Workspace>(window_id).unwrap();
|
||||||
|
@ -1463,7 +1463,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let app_state = cx.update(build_app_state);
|
let app_state = cx.update(test_app_state);
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&app_state, cx));
|
||||||
workspace
|
workspace
|
||||||
.update(&mut cx, |workspace, cx| {
|
.update(&mut cx, |workspace, cx| {
|
||||||
|
|
|
@ -208,14 +208,12 @@ impl Pane {
|
||||||
Align::new(
|
Align::new(
|
||||||
Label::new(
|
Label::new(
|
||||||
title,
|
title,
|
||||||
settings.ui_font_family,
|
if is_active {
|
||||||
settings.ui_font_size,
|
theme.workspace.active_tab.label.clone()
|
||||||
)
|
|
||||||
.with_style(if is_active {
|
|
||||||
&theme.workspace.active_tab.label
|
|
||||||
} else {
|
} else {
|
||||||
&theme.workspace.tab.label
|
theme.workspace.tab.label.clone()
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
|
|
Loading…
Reference in a new issue