Cargo.lock 🔗
@@ -5738,7 +5738,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "zed"
-version = "0.15.1"
+version = "0.15.2"
dependencies = [
"anyhow",
"async-recursion",
Antonio Scandurra created
Cargo.lock | 2
crates/chat_panel/src/chat_panel.rs | 26 +--
crates/contacts_panel/src/contacts_panel.rs | 7
crates/diagnostics/src/items.rs | 2
crates/editor/src/editor.rs | 41 ++----
crates/find/src/find.rs | 29 ++--
crates/gpui/src/app.rs | 118 ++++++++----------
crates/gpui/src/elements/mouse_event_handler.rs | 12 -
crates/gpui/src/presenter.rs | 10 +
crates/gpui/src/views/select.rs | 6
crates/project_panel/src/project_panel.rs | 2
crates/workspace/src/pane.rs | 4
crates/workspace/src/sidebar.rs | 41 ++----
crates/workspace/src/workspace.rs | 4
crates/zed/Cargo.toml | 2
15 files changed, 132 insertions(+), 174 deletions(-)
@@ -5738,7 +5738,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "zed"
-version = "0.15.1"
+version = "0.15.2"
dependencies = [
"anyhow",
"async-recursion",
@@ -325,21 +325,17 @@ impl ChatPanel {
enum SignInPromptLabel {}
Align::new(
- MouseEventHandler::new::<SignInPromptLabel, _, _, _>(
- cx.view_id(),
- cx,
- |mouse_state, _| {
- Label::new(
- "Sign in to use chat".to_string(),
- if mouse_state.hovered {
- theme.chat_panel.hovered_sign_in_prompt.clone()
- } else {
- theme.chat_panel.sign_in_prompt.clone()
- },
- )
- .boxed()
- },
- )
+ MouseEventHandler::new::<SignInPromptLabel, _, _>(0, cx, |mouse_state, _| {
+ Label::new(
+ "Sign in to use chat".to_string(),
+ if mouse_state.hovered {
+ theme.chat_panel.hovered_sign_in_prompt.clone()
+ } else {
+ theme.chat_panel.sign_in_prompt.clone()
+ },
+ )
+ .boxed()
+ })
.with_cursor_style(CursorStyle::PointingHand)
.on_click(move |cx| {
let rpc = rpc.clone();
@@ -27,7 +27,6 @@ impl ContactsPanel {
1000.,
{
let app_state = app_state.clone();
- let view_id = cx.view_id();
move |ix, cx| {
let user_store = app_state.user_store.read(cx);
let contacts = user_store.contacts().clone();
@@ -36,7 +35,6 @@ impl ContactsPanel {
&contacts[ix],
current_user_id,
app_state.clone(),
- view_id,
cx,
)
}
@@ -58,7 +56,6 @@ impl ContactsPanel {
collaborator: &Contact,
current_user_id: Option<u64>,
app_state: Arc<AppState>,
- view_id: usize,
cx: &mut LayoutContext,
) -> ElementBox {
let theme = &app_state.settings.borrow().theme.contacts_panel;
@@ -159,8 +156,8 @@ impl ContactsPanel {
let is_shared = project.is_shared;
let app_state = app_state.clone();
- MouseEventHandler::new::<ContactsPanel, _, _, _>(
- view_id,
+ MouseEventHandler::new::<ContactsPanel, _, _>(
+ project_id as usize,
cx,
|mouse_state, _| {
let style = match (project.is_shared, mouse_state.hovered) {
@@ -57,7 +57,7 @@ impl View for DiagnosticSummary {
let theme = &self.settings.borrow().theme.project_diagnostics;
let in_progress = self.in_progress;
- MouseEventHandler::new::<Tag, _, _, _>(cx.view_id(), cx, |_, _| {
+ MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
if in_progress {
Label::new(
"Checking... ".to_string(),
@@ -530,7 +530,6 @@ impl ContextMenu {
}
struct CompletionsMenu {
- editor_id: usize,
id: CompletionId,
initial_position: Anchor,
buffer: ModelHandle<Buffer>,
@@ -568,7 +567,6 @@ impl CompletionsMenu {
let settings = build_settings(cx);
let completions = self.completions.clone();
let matches = self.matches.clone();
- let editor_id = self.editor_id;
let selected_item = self.selected_item;
UniformList::new(self.list.clone(), matches.len(), move |range, items, cx| {
let settings = build_settings(cx);
@@ -577,8 +575,8 @@ impl CompletionsMenu {
let completion = &completions[mat.candidate_id];
let item_ix = start_ix + ix;
items.push(
- MouseEventHandler::new::<CompletionTag, _, _, _>(
- (editor_id, mat.candidate_id),
+ MouseEventHandler::new::<CompletionTag, _, _>(
+ mat.candidate_id,
cx,
|state, _| {
let item_style = if item_ix == selected_item {
@@ -675,7 +673,6 @@ impl CompletionsMenu {
#[derive(Clone)]
struct CodeActionsMenu {
- editor_id: usize,
actions: Arc<[CodeAction]>,
buffer: ModelHandle<Buffer>,
selected_item: usize,
@@ -712,7 +709,6 @@ impl CodeActionsMenu {
let settings = build_settings(cx);
let actions = self.actions.clone();
- let editor_id = self.editor_id;
let selected_item = self.selected_item;
let element =
UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
@@ -721,28 +717,21 @@ impl CodeActionsMenu {
for (ix, action) in actions[range].iter().enumerate() {
let item_ix = start_ix + ix;
items.push(
- MouseEventHandler::new::<ActionTag, _, _, _>(
- (editor_id, item_ix),
- cx,
- |state, _| {
- let item_style = if item_ix == selected_item {
- settings.style.autocomplete.selected_item
- } else if state.hovered {
- settings.style.autocomplete.hovered_item
- } else {
- settings.style.autocomplete.item
- };
-
- Text::new(
- action.lsp_action.title.clone(),
- settings.style.text.clone(),
- )
+ MouseEventHandler::new::<ActionTag, _, _>(item_ix, cx, |state, _| {
+ let item_style = if item_ix == selected_item {
+ settings.style.autocomplete.selected_item
+ } else if state.hovered {
+ settings.style.autocomplete.hovered_item
+ } else {
+ settings.style.autocomplete.item
+ };
+
+ Text::new(action.lsp_action.title.clone(), settings.style.text.clone())
.with_soft_wrap(false)
.contained()
.with_style(item_style)
.boxed()
- },
- )
+ })
.with_cursor_style(CursorStyle::PointingHand)
.on_mouse_down(move |cx| {
cx.dispatch_action(ConfirmCodeAction(Some(item_ix)));
@@ -1955,7 +1944,6 @@ impl Editor {
}
let mut menu = CompletionsMenu {
- editor_id: this.id(),
id,
initial_position: position,
match_candidates: completions
@@ -2138,7 +2126,6 @@ impl Editor {
if let Some((buffer, actions)) = this.available_code_actions.clone() {
this.show_context_menu(
ContextMenu::CodeActions(CodeActionsMenu {
- editor_id: this.handle.id(),
buffer,
actions,
selected_item: Default::default(),
@@ -2293,7 +2280,7 @@ impl Editor {
enum Tag {}
let style = (self.build_settings)(cx).style;
Some(
- MouseEventHandler::new::<Tag, _, _, _>(cx.view_id(), cx, |_, _| {
+ MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
Svg::new("icons/zap.svg")
.with_color(style.code_actions_indicator)
.boxed()
@@ -227,7 +227,7 @@ impl FindBar {
) -> ElementBox {
let theme = &self.settings.borrow().theme.find;
let is_active = self.is_mode_enabled(mode);
- MouseEventHandler::new::<Self, _, _, _>((cx.view_id(), mode as usize), cx, |state, _| {
+ MouseEventHandler::new::<Self, _, _>(mode as usize, cx, |state, _| {
let style = match (is_active, state.hovered) {
(false, false) => &theme.mode_button,
(false, true) => &theme.hovered_mode_button,
@@ -251,21 +251,18 @@ impl FindBar {
cx: &mut RenderContext<Self>,
) -> ElementBox {
let theme = &self.settings.borrow().theme.find;
- MouseEventHandler::new::<Self, _, _, _>(
- (cx.view_id(), 10 + direction as usize),
- cx,
- |state, _| {
- let style = if state.hovered {
- &theme.hovered_mode_button
- } else {
- &theme.mode_button
- };
- Label::new(icon.to_string(), style.text.clone())
- .contained()
- .with_style(style.container)
- .boxed()
- },
- )
+ enum NavButton {}
+ MouseEventHandler::new::<NavButton, _, _>(direction as usize, cx, |state, _| {
+ let style = if state.hovered {
+ &theme.hovered_mode_button
+ } else {
+ &theme.mode_button
+ };
+ Label::new(icon.to_string(), style.text.clone())
+ .contained()
+ .with_style(style.container)
+ .boxed()
+ })
.on_click(move |cx| cx.dispatch_action(GoToMatch(direction)))
.with_cursor_style(CursorStyle::PointingHand)
.boxed()
@@ -116,6 +116,26 @@ pub trait UpdateView {
T: View;
}
+pub trait ElementStateContext: DerefMut<Target = MutableAppContext> {
+ fn current_view_id(&self) -> usize;
+
+ fn element_state<Tag: 'static, T: 'static + Default>(
+ &mut self,
+ element_id: usize,
+ ) -> ElementStateHandle<T> {
+ let id = ElementStateId {
+ view_id: self.current_view_id(),
+ element_id,
+ tag: TypeId::of::<Tag>(),
+ };
+ self.cx
+ .element_states
+ .entry(id)
+ .or_insert_with(|| Box::new(T::default()));
+ ElementStateHandle::new(id, self.frame_count, &self.cx.ref_counts)
+ }
+}
+
pub trait Action: 'static + AnyAction {
type Argument: 'static + Clone;
}
@@ -1414,23 +1434,6 @@ impl MutableAppContext {
})
}
- pub fn element_state<Tag: 'static, T: 'static + Default>(
- &mut self,
- id: ElementStateId,
- ) -> ElementStateHandle<T> {
- let key = (TypeId::of::<Tag>(), id);
- self.cx
- .element_states
- .entry(key)
- .or_insert_with(|| Box::new(T::default()));
- ElementStateHandle::new(
- TypeId::of::<Tag>(),
- id,
- self.frame_count,
- &self.cx.ref_counts,
- )
- }
-
fn remove_dropped_entities(&mut self) {
loop {
let (dropped_models, dropped_views, dropped_element_states) =
@@ -1850,7 +1853,7 @@ pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>,
windows: HashMap<usize, Window>,
- element_states: HashMap<(TypeId, ElementStateId), Box<dyn Any>>,
+ element_states: HashMap<ElementStateId, Box<dyn Any>>,
background: Arc<executor::Background>,
ref_counts: Arc<Mutex<RefCounts>>,
font_cache: Arc<FontCache>,
@@ -2607,6 +2610,12 @@ impl<V: View> ReadView for RenderContext<'_, V> {
}
}
+impl<V: View> ElementStateContext for RenderContext<'_, V> {
+ fn current_view_id(&self) -> usize {
+ self.view_id
+ }
+}
+
impl<M> AsRef<AppContext> for ViewContext<'_, M> {
fn as_ref(&self) -> &AppContext {
&self.app.cx
@@ -2687,6 +2696,12 @@ impl<V: View> UpdateView for ViewContext<'_, V> {
}
}
+impl<V: View> ElementStateContext for ViewContext<'_, V> {
+ fn current_view_id(&self) -> usize {
+ self.view_id
+ }
+}
+
pub trait Handle<T> {
type Weak: 'static;
fn id(&self) -> usize;
@@ -3430,41 +3445,24 @@ impl<T> Hash for WeakViewHandle<T> {
}
}
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct ElementStateId(usize, usize);
-
-impl From<usize> for ElementStateId {
- fn from(id: usize) -> Self {
- Self(id, 0)
- }
-}
-
-impl From<(usize, usize)> for ElementStateId {
- fn from(id: (usize, usize)) -> Self {
- Self(id.0, id.1)
- }
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub struct ElementStateId {
+ view_id: usize,
+ element_id: usize,
+ tag: TypeId,
}
pub struct ElementStateHandle<T> {
value_type: PhantomData<T>,
- tag_type_id: TypeId,
id: ElementStateId,
ref_counts: Weak<Mutex<RefCounts>>,
}
impl<T: 'static> ElementStateHandle<T> {
- fn new(
- tag_type_id: TypeId,
- id: ElementStateId,
- frame_id: usize,
- ref_counts: &Arc<Mutex<RefCounts>>,
- ) -> Self {
- ref_counts
- .lock()
- .inc_element_state(tag_type_id, id, frame_id);
+ fn new(id: ElementStateId, frame_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
+ ref_counts.lock().inc_element_state(id, frame_id);
Self {
value_type: PhantomData,
- tag_type_id,
id,
ref_counts: Arc::downgrade(ref_counts),
}
@@ -3472,7 +3470,7 @@ impl<T: 'static> ElementStateHandle<T> {
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
cx.element_states
- .get(&(self.tag_type_id, self.id))
+ .get(&self.id)
.unwrap()
.downcast_ref()
.unwrap()
@@ -3482,17 +3480,12 @@ impl<T: 'static> ElementStateHandle<T> {
where
C: DerefMut<Target = MutableAppContext>,
{
- let mut element_state = cx
- .deref_mut()
- .cx
- .element_states
- .remove(&(self.tag_type_id, self.id))
- .unwrap();
+ let mut element_state = cx.deref_mut().cx.element_states.remove(&self.id).unwrap();
let result = f(element_state.downcast_mut().unwrap(), cx);
cx.deref_mut()
.cx
.element_states
- .insert((self.tag_type_id, self.id), element_state);
+ .insert(self.id, element_state);
result
}
}
@@ -3500,9 +3493,7 @@ impl<T: 'static> ElementStateHandle<T> {
impl<T> Drop for ElementStateHandle<T> {
fn drop(&mut self) {
if let Some(ref_counts) = self.ref_counts.upgrade() {
- ref_counts
- .lock()
- .dec_element_state(self.tag_type_id, self.id);
+ ref_counts.lock().dec_element_state(self.id);
}
}
}
@@ -3600,10 +3591,10 @@ impl Drop for Subscription {
#[derive(Default)]
struct RefCounts {
entity_counts: HashMap<usize, usize>,
- element_state_counts: HashMap<(TypeId, ElementStateId), ElementStateRefCount>,
+ element_state_counts: HashMap<ElementStateId, ElementStateRefCount>,
dropped_models: HashSet<usize>,
dropped_views: HashSet<(usize, usize)>,
- dropped_element_states: HashSet<(TypeId, ElementStateId)>,
+ dropped_element_states: HashSet<ElementStateId>,
}
struct ElementStateRefCount {
@@ -3634,8 +3625,8 @@ impl RefCounts {
}
}
- fn inc_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId, frame_id: usize) {
- match self.element_state_counts.entry((tag_type_id, id)) {
+ fn inc_element_state(&mut self, id: ElementStateId, frame_id: usize) {
+ match self.element_state_counts.entry(id) {
Entry::Occupied(mut entry) => {
let entry = entry.get_mut();
if entry.frame_id == frame_id || entry.ref_count >= 2 {
@@ -3649,7 +3640,7 @@ impl RefCounts {
ref_count: 1,
frame_id,
});
- self.dropped_element_states.remove(&(tag_type_id, id));
+ self.dropped_element_states.remove(&id);
}
}
}
@@ -3672,13 +3663,12 @@ impl RefCounts {
}
}
- fn dec_element_state(&mut self, tag_type_id: TypeId, id: ElementStateId) {
- let key = (tag_type_id, id);
- let entry = self.element_state_counts.get_mut(&key).unwrap();
+ fn dec_element_state(&mut self, id: ElementStateId) {
+ let entry = self.element_state_counts.get_mut(&id).unwrap();
entry.ref_count -= 1;
if entry.ref_count == 0 {
- self.element_state_counts.remove(&key);
- self.dropped_element_states.insert(key);
+ self.element_state_counts.remove(&id);
+ self.dropped_element_states.insert(id);
}
}
@@ -3691,7 +3681,7 @@ impl RefCounts {
) -> (
HashSet<usize>,
HashSet<(usize, usize)>,
- HashSet<(TypeId, ElementStateId)>,
+ HashSet<ElementStateId>,
) {
(
std::mem::take(&mut self.dropped_models),
@@ -5,11 +5,10 @@ use crate::{
vector::{vec2f, Vector2F},
},
platform::CursorStyle,
- CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateHandle, ElementStateId,
- Event, EventContext, LayoutContext, MutableAppContext, PaintContext, SizeConstraint,
+ CursorStyleHandle, DebugContext, Element, ElementBox, ElementStateContext, ElementStateHandle,
+ Event, EventContext, LayoutContext, PaintContext, SizeConstraint,
};
use serde_json::json;
-use std::ops::DerefMut;
pub struct MouseEventHandler {
state: ElementStateHandle<MouseState>,
@@ -30,14 +29,13 @@ pub struct MouseState {
}
impl MouseEventHandler {
- pub fn new<Tag, F, C, Id>(id: Id, cx: &mut C, render_child: F) -> Self
+ pub fn new<Tag, C, F>(id: usize, cx: &mut C, render_child: F) -> Self
where
Tag: 'static,
+ C: ElementStateContext,
F: FnOnce(&MouseState, &mut C) -> ElementBox,
- C: DerefMut<Target = MutableAppContext>,
- Id: Into<ElementStateId>,
{
- let state_handle = cx.element_state::<Tag, _>(id.into());
+ let state_handle = cx.element_state::<Tag, _>(id);
let child = state_handle.update(cx, |state, cx| render_child(state, cx));
Self {
state: state_handle,
@@ -7,8 +7,8 @@ use crate::{
platform::Event,
text_layout::TextLayoutCache,
Action, AnyAction, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox,
- Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene, UpgradeModelHandle,
- UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
+ ElementStateContext, Entity, FontSystem, ModelHandle, ReadModel, ReadView, Scene,
+ UpgradeModelHandle, UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use serde_json::json;
@@ -292,6 +292,12 @@ impl<'a> UpgradeViewHandle for LayoutContext<'a> {
}
}
+impl<'a> ElementStateContext for LayoutContext<'a> {
+ fn current_view_id(&self) -> usize {
+ *self.view_stack.last().unwrap()
+ }
+}
+
pub struct PaintContext<'a> {
rendered_views: &'a mut HashMap<usize, ElementBox>,
pub scene: &'a mut Scene,
@@ -104,7 +104,7 @@ impl View for Select {
Default::default()
};
let mut result = Flex::column().with_child(
- MouseEventHandler::new::<Header, _, _, _>(self.handle.id(), cx, |mouse_state, cx| {
+ MouseEventHandler::new::<Header, _, _>(self.handle.id(), cx, |mouse_state, cx| {
Container::new((self.render_item)(
self.selected_item_ix,
ItemType::Header,
@@ -132,8 +132,8 @@ impl View for Select {
let selected_item_ix = this.selected_item_ix;
range.end = range.end.min(this.item_count);
items.extend(range.map(|ix| {
- MouseEventHandler::new::<Item, _, _, _>(
- (handle.id(), ix),
+ MouseEventHandler::new::<Item, _, _>(
+ ix,
cx,
|mouse_state, cx| {
(handle.read(cx).render_item)(
@@ -476,7 +476,7 @@ impl ProjectPanel {
cx: &mut ViewContext<Self>,
) -> ElementBox {
let is_dir = details.is_dir;
- MouseEventHandler::new::<Self, _, _, _>((cx.view_id(), entry.entry_id), cx, |state, _| {
+ MouseEventHandler::new::<Self, _, _>(entry.entry_id, cx, |state, _| {
let style = match (details.is_selected, state.hovered) {
(false, false) => &theme.entry,
(false, true) => &theme.hovered_entry,
@@ -466,7 +466,7 @@ impl Pane {
let theme = &settings.theme;
enum Tabs {}
- let tabs = MouseEventHandler::new::<Tabs, _, _, _>(cx.view_id(), cx, |mouse_state, cx| {
+ let tabs = MouseEventHandler::new::<Tabs, _, _>(0, cx, |mouse_state, cx| {
let mut row = Flex::row();
for (ix, (_, item_view)) in self.item_views.iter().enumerate() {
let is_active = ix == self.active_item_index;
@@ -543,7 +543,7 @@ impl Pane {
let item_id = item_view.id();
enum TabCloseButton {}
let icon = Svg::new("icons/x.svg");
- MouseEventHandler::new::<TabCloseButton, _, _, _>(
+ MouseEventHandler::new::<TabCloseButton, _, _>(
item_id,
cx,
|mouse_state, _| {
@@ -83,26 +83,22 @@ impl Sidebar {
&theme.item
};
enum SidebarButton {}
- MouseEventHandler::new::<SidebarButton, _, _, _>(
- item.view.id(),
- cx,
- |_, _| {
- ConstrainedBox::new(
- Align::new(
- ConstrainedBox::new(
- Svg::new(item.icon_path)
- .with_color(theme.icon_color)
- .boxed(),
- )
- .with_height(theme.icon_size)
- .boxed(),
+ MouseEventHandler::new::<SidebarButton, _, _>(item.view.id(), cx, |_, _| {
+ ConstrainedBox::new(
+ Align::new(
+ ConstrainedBox::new(
+ Svg::new(item.icon_path)
+ .with_color(theme.icon_color)
+ .boxed(),
)
+ .with_height(theme.icon_size)
.boxed(),
)
- .with_height(theme.height)
- .boxed()
- },
- )
+ .boxed(),
+ )
+ .with_height(theme.height)
+ .boxed()
+ })
.with_cursor_style(CursorStyle::PointingHand)
.on_mouse_down(move |cx| {
cx.dispatch_action(ToggleSidebarItem(SidebarItemId {
@@ -161,7 +157,7 @@ impl Sidebar {
) -> ElementBox {
let width = self.width.clone();
let side = self.side;
- MouseEventHandler::new::<Self, _, _, _>((cx.view_id(), self.side.id()), cx, |_, _| {
+ MouseEventHandler::new::<Self, _, _>(side as usize, cx, |_, _| {
Container::new(Empty::new().boxed())
.with_style(self.theme(settings).resize_handle)
.boxed()
@@ -184,12 +180,3 @@ impl Sidebar {
.boxed()
}
}
-
-impl Side {
- fn id(self) -> usize {
- match self {
- Side::Left => 0,
- Side::Right => 1,
- }
- }
-}
@@ -1191,7 +1191,7 @@ impl Workspace {
if let Some(avatar) = user.and_then(|user| user.avatar.clone()) {
self.render_avatar(avatar, replica_id, theme)
} else {
- MouseEventHandler::new::<Authenticate, _, _, _>(cx.view_id(), cx, |state, _| {
+ MouseEventHandler::new::<Authenticate, _, _>(0, cx, |state, _| {
let style = if state.hovered {
&theme.workspace.titlebar.hovered_sign_in_prompt
} else {
@@ -1252,7 +1252,7 @@ impl Workspace {
theme.workspace.titlebar.share_icon_color
};
Some(
- MouseEventHandler::new::<Share, _, _, _>(cx.view_id(), cx, |_, _| {
+ MouseEventHandler::new::<Share, _, _>(0, cx, |_, _| {
Align::new(
ConstrainedBox::new(
Svg::new("icons/broadcast-24.svg").with_color(color).boxed(),
@@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
-version = "0.15.1"
+version = "0.15.2"
[lib]
name = "zed"