Merge remote-tracking branch 'origin/main' into cache

# Conflicts:
#	crates/gpui/src/elements/div.rs
This commit is contained in:
Antonio Scandurra 2024-01-15 11:37:34 +01:00
commit 4ff514ca7e
48 changed files with 600 additions and 903 deletions

View file

@ -2,16 +2,22 @@ name: "Check formatting"
description: "Checks code formatting use cargo fmt" description: "Checks code formatting use cargo fmt"
runs: runs:
using: "composite" using: "composite"
steps: steps:
- name: cargo fmt - name: cargo fmt
shell: bash -euxo pipefail {0} shell: bash -euxo pipefail {0}
run: cargo fmt --all -- --check run: cargo fmt --all -- --check
- name: cargo clippy - name: cargo clippy
shell: bash -euxo pipefail {0} shell: bash -euxo pipefail {0}
# clippy.toml is not currently supporting specifying allowed lints # clippy.toml is not currently supporting specifying allowed lints
# so specify those here, and disable the rest until Zed's workspace # so specify those here, and disable the rest until Zed's workspace
# will have more fixes & suppression for the standard lint set # will have more fixes & suppression for the standard lint set
run: | run: |
cargo clippy --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo cargo clippy --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
- name: Find modified migrations
shell: bash -euxo pipefail {0}
run: |
export SQUAWK_GITHUB_TOKEN=${{ github.token }}
. ./script/squawk

View file

@ -36,7 +36,7 @@
// }, // },
"buffer_line_height": "comfortable", "buffer_line_height": "comfortable",
// The name of a font to use for rendering text in the UI // The name of a font to use for rendering text in the UI
"ui_font_family": "Zed Mono", "ui_font_family": "Zed Sans",
// The OpenType features to enable for text in the UI // The OpenType features to enable for text in the UI
"ui_font_features": { "ui_font_features": {
// Disable ligatures: // Disable ligatures:

View file

@ -40,7 +40,7 @@ use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset a
use project::Project; use project::Project;
use search::{buffer_search::DivRegistrar, BufferSearchBar}; use search::{buffer_search::DivRegistrar, BufferSearchBar};
use semantic_index::{SemanticIndex, SemanticIndexStatus}; use semantic_index::{SemanticIndex, SemanticIndexStatus};
use settings::{Settings, SettingsStore}; use settings::Settings;
use std::{ use std::{
cell::Cell, cell::Cell,
cmp, cmp,
@ -165,7 +165,7 @@ impl AssistantPanel {
cx.on_focus_in(&focus_handle, Self::focus_in).detach(); cx.on_focus_in(&focus_handle, Self::focus_in).detach();
cx.on_focus_out(&focus_handle, Self::focus_out).detach(); cx.on_focus_out(&focus_handle, Self::focus_out).detach();
let mut this = Self { Self {
workspace: workspace_handle, workspace: workspace_handle,
active_editor_index: Default::default(), active_editor_index: Default::default(),
prev_active_editor_index: Default::default(), prev_active_editor_index: Default::default(),
@ -190,20 +190,7 @@ impl AssistantPanel {
_watch_saved_conversations, _watch_saved_conversations,
semantic_index, semantic_index,
retrieve_context_in_next_inline_assist: false, retrieve_context_in_next_inline_assist: false,
}; }
let mut old_dock_position = this.position(cx);
this.subscriptions =
vec![cx.observe_global::<SettingsStore>(move |this, cx| {
let new_dock_position = this.position(cx);
if new_dock_position != old_dock_position {
old_dock_position = new_dock_position;
cx.emit(PanelEvent::ChangePosition);
}
cx.notify();
})];
this
}) })
}) })
}) })
@ -3133,6 +3120,7 @@ mod tests {
use crate::MessageId; use crate::MessageId;
use ai::test::FakeCompletionProvider; use ai::test::FakeCompletionProvider;
use gpui::AppContext; use gpui::AppContext;
use settings::SettingsStore;
#[gpui::test] #[gpui::test]
fn test_inserting_and_removing_messages(cx: &mut AppContext) { fn test_inserting_and_removing_messages(cx: &mut AppContext) {

View file

@ -442,6 +442,8 @@ impl ActiveCall {
.location .location
.as_ref() .as_ref()
.and_then(|location| location.upgrade()); .and_then(|location| location.upgrade());
let channel_id = room.read(cx).channel_id();
cx.emit(Event::RoomJoined { channel_id });
room.update(cx, |room, cx| room.set_location(location.as_ref(), cx)) room.update(cx, |room, cx| room.set_location(location.as_ref(), cx))
} }
} else { } else {

View file

@ -26,6 +26,9 @@ pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event { pub enum Event {
RoomJoined {
channel_id: Option<u64>,
},
ParticipantLocationChanged { ParticipantLocationChanged {
participant_id: proto::PeerId, participant_id: proto::PeerId,
}, },
@ -49,7 +52,9 @@ pub enum Event {
RemoteProjectInvitationDiscarded { RemoteProjectInvitationDiscarded {
project_id: u64, project_id: u64,
}, },
Left, Left {
channel_id: Option<u64>,
},
} }
pub struct Room { pub struct Room {
@ -357,7 +362,9 @@ impl Room {
pub(crate) fn leave(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> { pub(crate) fn leave(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
cx.notify(); cx.notify();
cx.emit(Event::Left); cx.emit(Event::Left {
channel_id: self.channel_id(),
});
self.leave_internal(cx) self.leave_internal(cx)
} }
@ -598,6 +605,14 @@ impl Room {
.map(|participant| participant.role) .map(|participant| participant.role)
} }
pub fn contains_guests(&self) -> bool {
self.local_participant.role == proto::ChannelRole::Guest
|| self
.remote_participants
.values()
.any(|p| p.role == proto::ChannelRole::Guest)
}
pub fn local_participant_is_admin(&self) -> bool { pub fn local_participant_is_admin(&self) -> bool {
self.local_participant.role == proto::ChannelRole::Admin self.local_participant.role == proto::ChannelRole::Admin
} }

View file

@ -144,7 +144,7 @@ impl ChannelChat {
message: MessageParams, message: MessageParams,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> Result<Task<Result<u64>>> { ) -> Result<Task<Result<u64>>> {
if message.text.is_empty() { if message.text.trim().is_empty() {
Err(anyhow!("message body can't be empty"))?; Err(anyhow!("message body can't be empty"))?;
} }
@ -174,6 +174,8 @@ impl ChannelChat {
let user_store = self.user_store.clone(); let user_store = self.user_store.clone();
let rpc = self.rpc.clone(); let rpc = self.rpc.clone();
let outgoing_messages_lock = self.outgoing_messages_lock.clone(); let outgoing_messages_lock = self.outgoing_messages_lock.clone();
// todo - handle messages that fail to send (e.g. >1024 chars)
Ok(cx.spawn(move |this, mut cx| async move { Ok(cx.spawn(move |this, mut cx| async move {
let outgoing_message_guard = outgoing_messages_lock.lock().await; let outgoing_message_guard = outgoing_messages_lock.lock().await;
let request = rpc.request(proto::SendChannelMessage { let request = rpc.request(proto::SendChannelMessage {

View file

@ -14,6 +14,8 @@ use sysinfo::{
}; };
use tempfile::NamedTempFile; use tempfile::NamedTempFile;
use util::http::HttpClient; use util::http::HttpClient;
#[cfg(not(debug_assertions))]
use util::ResultExt;
use util::{channel::ReleaseChannel, TryFutureExt}; use util::{channel::ReleaseChannel, TryFutureExt};
use self::event_coalescer::EventCoalescer; use self::event_coalescer::EventCoalescer;
@ -167,6 +169,20 @@ impl Telemetry {
event_coalescer: EventCoalescer::new(), event_coalescer: EventCoalescer::new(),
})); }));
#[cfg(not(debug_assertions))]
cx.background_executor()
.spawn({
let state = state.clone();
async move {
if let Some(tempfile) =
NamedTempFile::new_in(util::paths::CONFIG_DIR.as_path()).log_err()
{
state.lock().log_file = Some(tempfile);
}
}
})
.detach();
cx.observe_global::<SettingsStore>({ cx.observe_global::<SettingsStore>({
let state = state.clone(); let state = state.clone();

View file

@ -256,6 +256,7 @@ impl Database {
message_id = result.last_insert_id; message_id = result.last_insert_id;
let mentioned_user_ids = let mentioned_user_ids =
mentions.iter().map(|m| m.user_id).collect::<HashSet<_>>(); mentions.iter().map(|m| m.user_id).collect::<HashSet<_>>();
let mentions = mentions let mentions = mentions
.iter() .iter()
.filter_map(|mention| { .filter_map(|mention| {

View file

@ -1418,8 +1418,6 @@ async fn test_channel_moving(
) { ) {
let mut server = TestServer::start(executor.clone()).await; let mut server = TestServer::start(executor.clone()).await;
let client_a = server.create_client(cx_a, "user_a").await; let client_a = server.create_client(cx_a, "user_a").await;
// let client_b = server.create_client(cx_b, "user_b").await;
// let client_c = server.create_client(cx_c, "user_c").await;
let channels = server let channels = server
.make_channel_tree( .make_channel_tree(

View file

@ -1,15 +1,15 @@
use crate::{channel_view::ChannelView, is_channels_feature_enabled, ChatPanelSettings}; use crate::{collab_panel, is_channels_feature_enabled, ChatPanelSettings};
use anyhow::Result; use anyhow::Result;
use call::ActiveCall; use call::{room, ActiveCall};
use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore}; use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore};
use client::Client; use client::Client;
use collections::HashMap; use collections::HashMap;
use db::kvp::KEY_VALUE_STORE; use db::kvp::KEY_VALUE_STORE;
use editor::Editor; use editor::Editor;
use gpui::{ use gpui::{
actions, div, list, prelude::*, px, AnyElement, AppContext, AsyncWindowContext, ClickEvent, actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, DismissEvent,
ElementId, EventEmitter, FocusableView, ListOffset, ListScrollEvent, ListState, Model, Render, ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, ListOffset, ListScrollEvent,
Subscription, Task, View, ViewContext, VisualContext, WeakView, ListState, Model, Render, Subscription, Task, View, ViewContext, VisualContext, WeakView,
}; };
use language::LanguageRegistry; use language::LanguageRegistry;
use menu::Confirm; use menu::Confirm;
@ -17,10 +17,13 @@ use message_editor::MessageEditor;
use project::Fs; use project::Fs;
use rich_text::RichText; use rich_text::RichText;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore}; use settings::Settings;
use std::sync::Arc; use std::sync::Arc;
use time::{OffsetDateTime, UtcOffset}; use time::{OffsetDateTime, UtcOffset};
use ui::{prelude::*, Avatar, Button, IconButton, IconName, Label, TabBar, Tooltip}; use ui::{
popover_menu, prelude::*, Avatar, Button, ContextMenu, IconButton, IconName, KeyBinding, Label,
TabBar,
};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
use workspace::{ use workspace::{
dock::{DockPosition, Panel, PanelEvent}, dock::{DockPosition, Panel, PanelEvent},
@ -54,9 +57,10 @@ pub struct ChatPanel {
active: bool, active: bool,
pending_serialization: Task<Option<()>>, pending_serialization: Task<Option<()>>,
subscriptions: Vec<gpui::Subscription>, subscriptions: Vec<gpui::Subscription>,
workspace: WeakView<Workspace>,
is_scrolled_to_bottom: bool, is_scrolled_to_bottom: bool,
markdown_data: HashMap<ChannelMessageId, RichText>, markdown_data: HashMap<ChannelMessageId, RichText>,
focus_handle: FocusHandle,
open_context_menu: Option<(u64, Subscription)>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -64,13 +68,6 @@ struct SerializedChatPanel {
width: Option<Pixels>, width: Option<Pixels>,
} }
#[derive(Debug)]
pub enum Event {
DockPositionChanged,
Focus,
Dismissed,
}
actions!(chat_panel, [ToggleFocus]); actions!(chat_panel, [ToggleFocus]);
impl ChatPanel { impl ChatPanel {
@ -89,8 +86,6 @@ impl ChatPanel {
) )
}); });
let workspace_handle = workspace.weak_handle();
cx.new_view(|cx: &mut ViewContext<Self>| { cx.new_view(|cx: &mut ViewContext<Self>| {
let view = cx.view().downgrade(); let view = cx.view().downgrade();
let message_list = let message_list =
@ -108,7 +103,7 @@ impl ChatPanel {
if event.visible_range.start < MESSAGE_LOADING_THRESHOLD { if event.visible_range.start < MESSAGE_LOADING_THRESHOLD {
this.load_more_messages(cx); this.load_more_messages(cx);
} }
this.is_scrolled_to_bottom = event.visible_range.end == event.count; this.is_scrolled_to_bottom = !event.is_scrolled;
})); }));
let mut this = Self { let mut this = Self {
@ -122,22 +117,37 @@ impl ChatPanel {
message_editor: input_editor, message_editor: input_editor,
local_timezone: cx.local_timezone(), local_timezone: cx.local_timezone(),
subscriptions: Vec::new(), subscriptions: Vec::new(),
workspace: workspace_handle,
is_scrolled_to_bottom: true, is_scrolled_to_bottom: true,
active: false, active: false,
width: None, width: None,
markdown_data: Default::default(), markdown_data: Default::default(),
focus_handle: cx.focus_handle(),
open_context_menu: None,
}; };
let mut old_dock_position = this.position(cx); this.subscriptions.push(cx.subscribe(
this.subscriptions.push(cx.observe_global::<SettingsStore>( &ActiveCall::global(cx),
move |this: &mut Self, cx| { move |this: &mut Self, call, event: &room::Event, cx| match event {
let new_dock_position = this.position(cx); room::Event::RoomJoined { channel_id } => {
if new_dock_position != old_dock_position { if let Some(channel_id) = channel_id {
old_dock_position = new_dock_position; this.select_channel(*channel_id, None, cx)
cx.emit(Event::DockPositionChanged); .detach_and_log_err(cx);
if call
.read(cx)
.room()
.is_some_and(|room| room.read(cx).contains_guests())
{
cx.emit(PanelEvent::Activate)
}
}
} }
cx.notify(); room::Event::Left { channel_id } => {
if channel_id == &this.channel_id(cx) {
cx.emit(PanelEvent::Close)
}
}
_ => {}
}, },
)); ));
@ -145,6 +155,12 @@ impl ChatPanel {
}) })
} }
pub fn channel_id(&self, cx: &AppContext) -> Option<u64> {
self.active_chat
.as_ref()
.map(|(chat, _)| chat.read(cx).channel_id)
}
pub fn is_scrolled_to_bottom(&self) -> bool { pub fn is_scrolled_to_bottom(&self) -> bool {
self.is_scrolled_to_bottom self.is_scrolled_to_bottom
} }
@ -259,53 +275,9 @@ impl ChatPanel {
} }
} }
fn render_channel(&self, cx: &mut ViewContext<Self>) -> AnyElement {
v_stack()
.full()
.on_action(cx.listener(Self::send))
.child(
h_stack().z_index(1).child(
TabBar::new("chat_header")
.child(
h_stack()
.w_full()
.h(rems(ui::Tab::HEIGHT_IN_REMS))
.px_2()
.child(Label::new(
self.active_chat
.as_ref()
.and_then(|c| {
Some(format!("#{}", c.0.read(cx).channel(cx)?.name))
})
.unwrap_or_default(),
)),
)
.end_child(
IconButton::new("notes", IconName::File)
.on_click(cx.listener(Self::open_notes))
.tooltip(|cx| Tooltip::text("Open notes", cx)),
)
.end_child(
IconButton::new("call", IconName::AudioOn)
.on_click(cx.listener(Self::join_call))
.tooltip(|cx| Tooltip::text("Join call", cx)),
),
),
)
.child(div().flex_grow().px_2().py_1().map(|this| {
if self.active_chat.is_some() {
this.child(list(self.message_list.clone()).full())
} else {
this
}
}))
.child(h_stack().p_2().child(self.message_editor.clone()))
.into_any()
}
fn render_message(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render_message(&mut self, ix: usize, cx: &mut ViewContext<Self>) -> impl IntoElement {
let active_chat = &self.active_chat.as_ref().unwrap().0; let active_chat = &self.active_chat.as_ref().unwrap().0;
let (message, is_continuation_from_previous, is_continuation_to_next, is_admin) = let (message, is_continuation_from_previous, is_admin) =
active_chat.update(cx, |active_chat, cx| { active_chat.update(cx, |active_chat, cx| {
let is_admin = self let is_admin = self
.channel_store .channel_store
@ -314,13 +286,9 @@ impl ChatPanel {
let last_message = active_chat.message(ix.saturating_sub(1)); let last_message = active_chat.message(ix.saturating_sub(1));
let this_message = active_chat.message(ix).clone(); let this_message = active_chat.message(ix).clone();
let next_message =
active_chat.message(ix.saturating_add(1).min(active_chat.message_count() - 1));
let is_continuation_from_previous = last_message.id != this_message.id let is_continuation_from_previous = last_message.id != this_message.id
&& last_message.sender.id == this_message.sender.id; && last_message.sender.id == this_message.sender.id;
let is_continuation_to_next = this_message.id != next_message.id
&& this_message.sender.id == next_message.sender.id;
if let ChannelMessageId::Saved(id) = this_message.id { if let ChannelMessageId::Saved(id) = this_message.id {
if this_message if this_message
@ -332,12 +300,7 @@ impl ChatPanel {
} }
} }
( (this_message, is_continuation_from_previous, is_admin)
this_message,
is_continuation_from_previous,
is_continuation_to_next,
is_admin,
)
}); });
let _is_pending = message.is_pending(); let _is_pending = message.is_pending();
@ -360,50 +323,100 @@ impl ChatPanel {
ChannelMessageId::Saved(id) => ("saved-message", id).into(), ChannelMessageId::Saved(id) => ("saved-message", id).into(),
ChannelMessageId::Pending(id) => ("pending-message", id).into(), ChannelMessageId::Pending(id) => ("pending-message", id).into(),
}; };
let this = cx.view().clone();
v_stack() v_stack()
.w_full() .w_full()
.id(element_id)
.relative() .relative()
.overflow_hidden() .overflow_hidden()
.group("")
.when(!is_continuation_from_previous, |this| { .when(!is_continuation_from_previous, |this| {
this.child( this.pt_3().child(
h_stack() h_stack()
.gap_2() .child(
.child(Avatar::new(message.sender.avatar_uri.clone())) div().absolute().child(
.child(Label::new(message.sender.github_login.clone())) Avatar::new(message.sender.avatar_uri.clone())
.size(cx.rem_size() * 1.5),
),
)
.child(
div()
.pl(cx.rem_size() * 1.5 + px(6.0))
.pr(px(8.0))
.font_weight(FontWeight::BOLD)
.child(Label::new(message.sender.github_login.clone())),
)
.child( .child(
Label::new(format_timestamp( Label::new(format_timestamp(
message.timestamp, message.timestamp,
now, now,
self.local_timezone, self.local_timezone,
)) ))
.size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
), ),
) )
}) })
.when(!is_continuation_to_next, |this| .when(is_continuation_from_previous, |this| this.pt_1())
// HACK: This should really be a margin, but margins seem to get collapsed.
this.pb_2())
.child(text.element("body".into(), cx))
.child( .child(
div() v_stack()
.absolute() .w_full()
.top_1() .text_ui_sm()
.right_2() .id(element_id)
.w_8() .group("")
.visible_on_hover("") .child(text.element("body".into(), cx))
.children(message_id_to_remove.map(|message_id| { .child(
IconButton::new(("remove", message_id), IconName::XCircle).on_click( div()
cx.listener(move |this, _, cx| { .absolute()
this.remove_message(message_id, cx); .z_index(1)
}), .right_0()
) .w_6()
})), .bg(cx.theme().colors().panel_background)
.when(!self.has_open_menu(message_id_to_remove), |el| {
el.visible_on_hover("")
})
.children(message_id_to_remove.map(|message_id| {
popover_menu(("menu", message_id))
.trigger(IconButton::new(
("trigger", message_id),
IconName::Ellipsis,
))
.menu(move |cx| {
Some(Self::render_message_menu(&this, message_id, cx))
})
})),
),
) )
} }
fn has_open_menu(&self, message_id: Option<u64>) -> bool {
match self.open_context_menu.as_ref() {
Some((id, _)) => Some(*id) == message_id,
None => false,
}
}
fn render_message_menu(
this: &View<Self>,
message_id: u64,
cx: &mut WindowContext,
) -> View<ContextMenu> {
let menu = {
let this = this.clone();
ContextMenu::build(cx, move |menu, _| {
menu.entry("Delete message", None, move |cx| {
this.update(cx, |this, cx| this.remove_message(message_id, cx))
})
})
};
this.update(cx, |this, cx| {
let subscription = cx.subscribe(&menu, |this: &mut Self, _, _: &DismissEvent, _| {
this.open_context_menu = None;
});
this.open_context_menu = Some((message_id, subscription));
});
menu
}
fn render_markdown_with_mentions( fn render_markdown_with_mentions(
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
current_user_id: u64, current_user_id: u64,
@ -421,44 +434,6 @@ impl ChatPanel {
rich_text::render_markdown(message.body.clone(), &mentions, language_registry, None) rich_text::render_markdown(message.body.clone(), &mentions, language_registry, None)
} }
fn render_sign_in_prompt(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_stack()
.gap_2()
.p_4()
.child(
Button::new("sign-in", "Sign in")
.style(ButtonStyle::Filled)
.icon_color(Color::Muted)
.icon(IconName::Github)
.icon_position(IconPosition::Start)
.full_width()
.on_click(cx.listener(move |this, _, cx| {
let client = this.client.clone();
cx.spawn(|this, mut cx| async move {
if client
.authenticate_and_connect(true, &cx)
.log_err()
.await
.is_some()
{
this.update(&mut cx, |_, cx| {
cx.focus_self();
})
.ok();
}
})
.detach();
})),
)
.child(
div().flex().w_full().items_center().child(
Label::new("Sign in to chat.")
.color(Color::Muted)
.size(LabelSize::Small),
),
)
}
fn send(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) { fn send(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if let Some((chat, _)) = self.active_chat.as_ref() { if let Some((chat, _)) = self.active_chat.as_ref() {
let message = self let message = self
@ -535,50 +510,93 @@ impl ChatPanel {
Ok(()) Ok(())
}) })
} }
fn open_notes(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
if let Some((chat, _)) = &self.active_chat {
let channel_id = chat.read(cx).channel_id;
if let Some(workspace) = self.workspace.upgrade() {
ChannelView::open(channel_id, workspace, cx).detach();
}
}
}
fn join_call(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
if let Some((chat, _)) = &self.active_chat {
let channel_id = chat.read(cx).channel_id;
ActiveCall::global(cx)
.update(cx, |call, cx| call.join_channel(channel_id, cx))
.detach_and_log_err(cx);
}
}
} }
impl EventEmitter<Event> for ChatPanel {}
impl Render for ChatPanel { impl Render for ChatPanel {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_stack() v_stack()
.size_full() .track_focus(&self.focus_handle)
.map(|this| match (self.client.user_id(), self.active_chat()) { .full()
(Some(_), Some(_)) => this.child(self.render_channel(cx)), .on_action(cx.listener(Self::send))
(Some(_), None) => this.child( .child(
div().p_4().child( h_stack().z_index(1).child(
Label::new("Select a channel to chat in.") TabBar::new("chat_header").child(
.size(LabelSize::Small) h_stack()
.color(Color::Muted), .w_full()
.h(rems(ui::Tab::HEIGHT_IN_REMS))
.px_2()
.child(Label::new(
self.active_chat
.as_ref()
.and_then(|c| {
Some(format!("#{}", c.0.read(cx).channel(cx)?.name))
})
.unwrap_or("Chat".to_string()),
)),
), ),
), ),
(None, _) => this.child(self.render_sign_in_prompt(cx)), )
}) .child(div().flex_grow().px_2().pt_1().map(|this| {
.min_w(px(150.)) if self.active_chat.is_some() {
this.child(list(self.message_list.clone()).full())
} else {
this.child(
div()
.p_4()
.child(
Label::new("Select a channel to chat in.")
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(
div().pt_1().w_full().items_center().child(
Button::new("toggle-collab", "Open")
.full_width()
.key_binding(KeyBinding::for_action(
&collab_panel::ToggleFocus,
cx,
))
.on_click(|_, cx| {
cx.dispatch_action(
collab_panel::ToggleFocus.boxed_clone(),
)
}),
),
),
)
}
}))
.child(
h_stack()
.when(!self.is_scrolled_to_bottom, |el| {
el.border_t_1().border_color(cx.theme().colors().border)
})
.p_2()
.map(|el| {
if self.active_chat.is_some() {
el.child(self.message_editor.clone())
} else {
el.child(
div()
.rounded_md()
.h_7()
.w_full()
.bg(cx.theme().colors().editor_background),
)
}
}),
)
.into_any()
} }
} }
impl FocusableView for ChatPanel { impl FocusableView for ChatPanel {
fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
self.message_editor.read(cx).focus_handle(cx) if self.active_chat.is_some() {
self.message_editor.read(cx).focus_handle(cx)
} else {
self.focus_handle.clone()
}
} }
} }
@ -613,7 +631,7 @@ impl Panel for ChatPanel {
if active { if active {
self.acknowledge_last_message(cx); self.acknowledge_last_message(cx);
if !is_channels_feature_enabled(cx) { if !is_channels_feature_enabled(cx) {
cx.emit(Event::Dismissed); cx.emit(PanelEvent::Close);
} }
} }
} }

View file

@ -26,7 +26,7 @@ use menu::{Cancel, Confirm, SelectNext, SelectPrev};
use project::{Fs, Project}; use project::{Fs, Project};
use rpc::proto::{self, PeerId}; use rpc::proto::{self, PeerId};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsStore}; use settings::Settings;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{mem, sync::Arc}; use std::{mem, sync::Arc};
use theme::{ActiveTheme, ThemeSettings}; use theme::{ActiveTheme, ThemeSettings};
@ -254,19 +254,6 @@ impl CollabPanel {
this.update_entries(false, cx); this.update_entries(false, cx);
// Update the dock position when the setting changes.
let mut old_dock_position = this.position(cx);
this.subscriptions.push(cx.observe_global::<SettingsStore>(
move |this: &mut Self, cx| {
let new_dock_position = this.position(cx);
if new_dock_position != old_dock_position {
old_dock_position = new_dock_position;
cx.emit(PanelEvent::ChangePosition);
}
cx.notify();
},
));
let active_call = ActiveCall::global(cx); let active_call = ActiveCall::global(cx);
this.subscriptions this.subscriptions
.push(cx.observe(&this.user_store, |this, _, cx| { .push(cx.observe(&this.user_store, |this, _, cx| {
@ -1426,14 +1413,6 @@ impl CollabPanel {
self.toggle_channel_collapsed(id, cx) self.toggle_channel_collapsed(id, cx)
} }
// fn toggle_channel_collapsed_action(
// &mut self,
// action: &ToggleCollapse,
// cx: &mut ViewContext<Self>,
// ) {
// self.toggle_channel_collapsed(action.location, cx);
// }
fn toggle_channel_collapsed<'a>(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) { fn toggle_channel_collapsed<'a>(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
match self.collapsed_channels.binary_search(&channel_id) { match self.collapsed_channels.binary_search(&channel_id) {
Ok(ix) => { Ok(ix) => {
@ -1910,7 +1889,6 @@ impl CollabPanel {
let mut channel_link = None; let mut channel_link = None;
let mut channel_tooltip_text = None; let mut channel_tooltip_text = None;
let mut channel_icon = None; let mut channel_icon = None;
// let mut is_dragged_over = false;
let text = match section { let text = match section {
Section::ActiveCall => { Section::ActiveCall => {
@ -2052,7 +2030,7 @@ impl CollabPanel {
}), }),
) )
.start_slot( .start_slot(
// todo!() handle contacts with no avatar // todo handle contacts with no avatar
Avatar::new(contact.user.avatar_uri.clone()) Avatar::new(contact.user.avatar_uri.clone())
.availability_indicator(if online { Some(!busy) } else { None }), .availability_indicator(if online { Some(!busy) } else { None }),
) )

View file

@ -41,10 +41,6 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
chat_panel::init(cx); chat_panel::init(cx);
notification_panel::init(cx); notification_panel::init(cx);
notifications::init(&app_state, cx); notifications::init(&app_state, cx);
// cx.add_global_action(toggle_screen_sharing);
// cx.add_global_action(toggle_mute);
// cx.add_global_action(toggle_deafen);
} }
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) { pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
@ -131,34 +127,6 @@ fn notification_window_options(
} }
} }
// fn render_avatar<T: 'static>(
// avatar: Option<Arc<ImageData>>,
// avatar_style: &AvatarStyle,
// container: ContainerStyle,
// ) -> AnyElement<T> {
// avatar
// .map(|avatar| {
// Image::from_data(avatar)
// .with_style(avatar_style.image)
// .aligned()
// .contained()
// .with_corner_radius(avatar_style.outer_corner_radius)
// .constrained()
// .with_width(avatar_style.outer_width)
// .with_height(avatar_style.outer_width)
// .into_any()
// })
// .unwrap_or_else(|| {
// Empty::new()
// .constrained()
// .with_width(avatar_style.outer_width)
// .into_any()
// })
// .contained()
// .with_style(container)
// .into_any()
// }
fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool { fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
cx.is_staff() || cx.has_flag::<ChannelsAlpha>() cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
} }

View file

@ -58,7 +58,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
} }
} }
room::Event::Left => { room::Event::Left { .. } => {
for (_, windows) in notification_windows.drain() { for (_, windows) in notification_windows.drain() {
for window in windows { for window in windows {
window window

View file

@ -1955,17 +1955,21 @@ impl Editor {
} }
} }
// pub fn language_at<'a, T: ToOffset>( pub fn language_at<'a, T: ToOffset>(
// &self, &self,
// point: T, point: T,
// cx: &'a AppContext, cx: &'a AppContext,
// ) -> Option<Arc<Language>> { ) -> Option<Arc<Language>> {
// self.buffer.read(cx).language_at(point, cx) self.buffer.read(cx).language_at(point, cx)
// } }
// pub fn file_at<'a, T: ToOffset>(&self, point: T, cx: &'a AppContext) -> Option<Arc<dyn File>> { pub fn file_at<'a, T: ToOffset>(
// self.buffer.read(cx).read(cx).file_at(point).cloned() &self,
// } point: T,
cx: &'a AppContext,
) -> Option<Arc<dyn language::File>> {
self.buffer.read(cx).read(cx).file_at(point).cloned()
}
pub fn active_excerpt( pub fn active_excerpt(
&self, &self,
@ -1976,15 +1980,6 @@ impl Editor {
.excerpt_containing(self.selections.newest_anchor().head(), cx) .excerpt_containing(self.selections.newest_anchor().head(), cx)
} }
// pub fn style(&self, cx: &AppContext) -> EditorStyle {
// build_style(
// settings::get::<ThemeSettings>(cx),
// self.get_field_editor_theme.as_deref(),
// self.override_text_style.as_deref(),
// cx,
// )
// }
pub fn mode(&self) -> EditorMode { pub fn mode(&self) -> EditorMode {
self.mode self.mode
} }
@ -5443,6 +5438,10 @@ impl Editor {
} }
pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) { pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
if self.read_only(cx) {
return;
}
self.transact(cx, |this, cx| { self.transact(cx, |this, cx| {
if let Some(item) = cx.read_from_clipboard() { if let Some(item) = cx.read_from_clipboard() {
let clipboard_text = Cow::Borrowed(item.text()); let clipboard_text = Cow::Borrowed(item.text());
@ -5515,6 +5514,10 @@ impl Editor {
} }
pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) { pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
if self.read_only(cx) {
return;
}
if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() { if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
self.change_selections(None, cx, |s| { self.change_selections(None, cx, |s| {
@ -5529,6 +5532,10 @@ impl Editor {
} }
pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) { pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
if self.read_only(cx) {
return;
}
if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) { if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned() if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
{ {

View file

@ -355,16 +355,6 @@ impl Hsla {
} }
} }
// impl From<Hsla> for Rgba {
// fn from(value: Hsla) -> Self {
// let h = value.h;
// let s = value.s;
// let l = value.l;
// let c = (1 - |2L - 1|) X s
// }
// }
impl From<Rgba> for Hsla { impl From<Rgba> for Hsla {
fn from(color: Rgba) -> Self { fn from(color: Rgba) -> Self {
let r = color.r; let r = color.r;

View file

@ -978,12 +978,31 @@ impl Interactivity {
f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext), f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext),
) { ) {
let style = self.compute_style(Some(bounds), element_state, cx); let style = self.compute_style(Some(bounds), element_state, cx);
let z_index = style.z_index.unwrap_or(0);
let paint_hover_group_handler = |cx: &mut WindowContext| {
let hover_group_bounds = self
.group_hover_style
.as_ref()
.and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
if let Some(group_bounds) = hover_group_bounds {
let hovered = group_bounds.contains(&cx.mouse_position());
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture
&& group_bounds.contains(&event.position) != hovered
{
cx.refresh();
}
});
}
};
if style.visibility == Visibility::Hidden { if style.visibility == Visibility::Hidden {
cx.with_z_index(z_index, |cx| paint_hover_group_handler(cx));
return; return;
} }
let z_index = style.z_index.unwrap_or(0);
cx.with_z_index(z_index, |cx| { cx.with_z_index(z_index, |cx| {
style.paint(bounds, cx, |cx| { style.paint(bounds, cx, |cx| {
cx.with_text_style(style.text_style().cloned(), |cx| { cx.with_text_style(style.text_style().cloned(), |cx| {
@ -1166,21 +1185,7 @@ impl Interactivity {
}) })
} }
let hover_group_bounds = self paint_hover_group_handler(cx);
.group_hover_style
.as_ref()
.and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
if let Some(group_bounds) = hover_group_bounds {
let hovered = group_bounds.contains(&cx.mouse_position());
cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
if phase == DispatchPhase::Capture
&& group_bounds.contains(&event.position) != hovered
{
cx.refresh();
}
});
}
if self.hover_style.is_some() if self.hover_style.is_some()
|| self.base_style.mouse_cursor.is_some() || self.base_style.mouse_cursor.is_some()

View file

@ -43,6 +43,7 @@ pub enum ListAlignment {
pub struct ListScrollEvent { pub struct ListScrollEvent {
pub visible_range: Range<usize>, pub visible_range: Range<usize>,
pub count: usize, pub count: usize,
pub is_scrolled: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -253,6 +254,7 @@ impl StateInner {
&ListScrollEvent { &ListScrollEvent {
visible_range, visible_range,
count: self.items.summary().count, count: self.items.summary().count,
is_scrolled: self.logical_scroll_top.is_some(),
}, },
cx, cx,
); );

View file

@ -18,7 +18,7 @@ use smallvec::SmallVec;
use std::{ffi::c_void, mem, ptr, sync::Arc}; use std::{ffi::c_void, mem, ptr, sync::Arc};
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib")); const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value. const INSTANCE_BUFFER_SIZE: usize = 32 * 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value (maybe even we could adjust dynamically...)
pub(crate) struct MetalRenderer { pub(crate) struct MetalRenderer {
layer: metal::MetalLayer, layer: metal::MetalLayer,
@ -204,7 +204,11 @@ impl MetalRenderer {
let command_buffer = command_queue.new_command_buffer(); let command_buffer = command_queue.new_command_buffer();
let mut instance_offset = 0; let mut instance_offset = 0;
let path_tiles = self.rasterize_paths(scene.paths(), &mut instance_offset, command_buffer); let Some(path_tiles) =
self.rasterize_paths(scene.paths(), &mut instance_offset, command_buffer)
else {
panic!("failed to rasterize {} paths", scene.paths().len());
};
let render_pass_descriptor = metal::RenderPassDescriptor::new(); let render_pass_descriptor = metal::RenderPassDescriptor::new();
let color_attachment = render_pass_descriptor let color_attachment = render_pass_descriptor
@ -228,67 +232,67 @@ impl MetalRenderer {
zfar: 1.0, zfar: 1.0,
}); });
for batch in scene.batches() { for batch in scene.batches() {
match batch { let ok = match batch {
PrimitiveBatch::Shadows(shadows) => { PrimitiveBatch::Shadows(shadows) => self.draw_shadows(
self.draw_shadows( shadows,
shadows, &mut instance_offset,
&mut instance_offset, viewport_size,
viewport_size, command_encoder,
command_encoder, ),
);
}
PrimitiveBatch::Quads(quads) => { PrimitiveBatch::Quads(quads) => {
self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder); self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder)
}
PrimitiveBatch::Paths(paths) => {
self.draw_paths(
paths,
&path_tiles,
&mut instance_offset,
viewport_size,
command_encoder,
);
}
PrimitiveBatch::Underlines(underlines) => {
self.draw_underlines(
underlines,
&mut instance_offset,
viewport_size,
command_encoder,
);
} }
PrimitiveBatch::Paths(paths) => self.draw_paths(
paths,
&path_tiles,
&mut instance_offset,
viewport_size,
command_encoder,
),
PrimitiveBatch::Underlines(underlines) => self.draw_underlines(
underlines,
&mut instance_offset,
viewport_size,
command_encoder,
),
PrimitiveBatch::MonochromeSprites { PrimitiveBatch::MonochromeSprites {
texture_id, texture_id,
sprites, sprites,
} => { } => self.draw_monochrome_sprites(
self.draw_monochrome_sprites( texture_id,
texture_id, sprites,
sprites, &mut instance_offset,
&mut instance_offset, viewport_size,
viewport_size, command_encoder,
command_encoder, ),
);
}
PrimitiveBatch::PolychromeSprites { PrimitiveBatch::PolychromeSprites {
texture_id, texture_id,
sprites, sprites,
} => { } => self.draw_polychrome_sprites(
self.draw_polychrome_sprites( texture_id,
texture_id, sprites,
sprites, &mut instance_offset,
&mut instance_offset, viewport_size,
viewport_size, command_encoder,
command_encoder, ),
); PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(
} surfaces,
PrimitiveBatch::Surfaces(surfaces) => { &mut instance_offset,
self.draw_surfaces( viewport_size,
surfaces, command_encoder,
&mut instance_offset, ),
viewport_size, };
command_encoder,
); if !ok {
} panic!("scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
scene.paths.len(),
scene.shadows.len(),
scene.quads.len(),
scene.underlines.len(),
scene.monochrome_sprites.len(),
scene.polychrome_sprites.len(),
scene.surfaces.len(),
)
} }
} }
@ -311,7 +315,7 @@ impl MetalRenderer {
paths: &[Path<ScaledPixels>], paths: &[Path<ScaledPixels>],
offset: &mut usize, offset: &mut usize,
command_buffer: &metal::CommandBufferRef, command_buffer: &metal::CommandBufferRef,
) -> HashMap<PathId, AtlasTile> { ) -> Option<HashMap<PathId, AtlasTile>> {
let mut tiles = HashMap::default(); let mut tiles = HashMap::default();
let mut vertices_by_texture_id = HashMap::default(); let mut vertices_by_texture_id = HashMap::default();
for path in paths { for path in paths {
@ -337,10 +341,9 @@ impl MetalRenderer {
for (texture_id, vertices) in vertices_by_texture_id { for (texture_id, vertices) in vertices_by_texture_id {
align_offset(offset); align_offset(offset);
let next_offset = *offset + vertices.len() * mem::size_of::<PathVertex<ScaledPixels>>(); let next_offset = *offset + vertices.len() * mem::size_of::<PathVertex<ScaledPixels>>();
assert!( if next_offset > INSTANCE_BUFFER_SIZE {
next_offset <= INSTANCE_BUFFER_SIZE, return None;
"instance buffer exhausted" }
);
let render_pass_descriptor = metal::RenderPassDescriptor::new(); let render_pass_descriptor = metal::RenderPassDescriptor::new();
let color_attachment = render_pass_descriptor let color_attachment = render_pass_descriptor
@ -389,7 +392,7 @@ impl MetalRenderer {
*offset = next_offset; *offset = next_offset;
} }
tiles Some(tiles)
} }
fn draw_shadows( fn draw_shadows(
@ -398,9 +401,9 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
if shadows.is_empty() { if shadows.is_empty() {
return; return true;
} }
align_offset(offset); align_offset(offset);
@ -429,6 +432,12 @@ impl MetalRenderer {
let shadow_bytes_len = std::mem::size_of_val(shadows); let shadow_bytes_len = std::mem::size_of_val(shadows);
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) }; let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
let next_offset = *offset + shadow_bytes_len;
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
unsafe { unsafe {
ptr::copy_nonoverlapping( ptr::copy_nonoverlapping(
shadows.as_ptr() as *const u8, shadows.as_ptr() as *const u8,
@ -437,12 +446,6 @@ impl MetalRenderer {
); );
} }
let next_offset = *offset + shadow_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced( command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle, metal::MTLPrimitiveType::Triangle,
0, 0,
@ -450,6 +453,7 @@ impl MetalRenderer {
shadows.len() as u64, shadows.len() as u64,
); );
*offset = next_offset; *offset = next_offset;
true
} }
fn draw_quads( fn draw_quads(
@ -458,9 +462,9 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
if quads.is_empty() { if quads.is_empty() {
return; return true;
} }
align_offset(offset); align_offset(offset);
@ -489,16 +493,16 @@ impl MetalRenderer {
let quad_bytes_len = std::mem::size_of_val(quads); let quad_bytes_len = std::mem::size_of_val(quads);
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) }; let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
let next_offset = *offset + quad_bytes_len;
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
unsafe { unsafe {
ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len); ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
} }
let next_offset = *offset + quad_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced( command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle, metal::MTLPrimitiveType::Triangle,
0, 0,
@ -506,6 +510,7 @@ impl MetalRenderer {
quads.len() as u64, quads.len() as u64,
); );
*offset = next_offset; *offset = next_offset;
true
} }
fn draw_paths( fn draw_paths(
@ -515,9 +520,9 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
if paths.is_empty() { if paths.is_empty() {
return; return true;
} }
command_encoder.set_render_pipeline_state(&self.path_sprites_pipeline_state); command_encoder.set_render_pipeline_state(&self.path_sprites_pipeline_state);
@ -587,8 +592,14 @@ impl MetalRenderer {
.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture)); .set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len(); let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
let next_offset = *offset + sprite_bytes_len;
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
let buffer_contents = let buffer_contents =
unsafe { (self.instances.contents() as *mut u8).add(*offset) }; unsafe { (self.instances.contents() as *mut u8).add(*offset) };
unsafe { unsafe {
ptr::copy_nonoverlapping( ptr::copy_nonoverlapping(
sprites.as_ptr() as *const u8, sprites.as_ptr() as *const u8,
@ -597,12 +608,6 @@ impl MetalRenderer {
); );
} }
let next_offset = *offset + sprite_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced( command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle, metal::MTLPrimitiveType::Triangle,
0, 0,
@ -613,6 +618,7 @@ impl MetalRenderer {
sprites.clear(); sprites.clear();
} }
} }
true
} }
fn draw_underlines( fn draw_underlines(
@ -621,9 +627,9 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
if underlines.is_empty() { if underlines.is_empty() {
return; return true;
} }
align_offset(offset); align_offset(offset);
@ -661,10 +667,9 @@ impl MetalRenderer {
} }
let next_offset = *offset + quad_bytes_len; let next_offset = *offset + quad_bytes_len;
assert!( if next_offset > INSTANCE_BUFFER_SIZE {
next_offset <= INSTANCE_BUFFER_SIZE, return false;
"instance buffer exhausted" }
);
command_encoder.draw_primitives_instanced( command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle, metal::MTLPrimitiveType::Triangle,
@ -673,6 +678,7 @@ impl MetalRenderer {
underlines.len() as u64, underlines.len() as u64,
); );
*offset = next_offset; *offset = next_offset;
true
} }
fn draw_monochrome_sprites( fn draw_monochrome_sprites(
@ -682,9 +688,9 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
if sprites.is_empty() { if sprites.is_empty() {
return; return true;
} }
align_offset(offset); align_offset(offset);
@ -723,6 +729,12 @@ impl MetalRenderer {
let sprite_bytes_len = std::mem::size_of_val(sprites); let sprite_bytes_len = std::mem::size_of_val(sprites);
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) }; let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
let next_offset = *offset + sprite_bytes_len;
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
unsafe { unsafe {
ptr::copy_nonoverlapping( ptr::copy_nonoverlapping(
sprites.as_ptr() as *const u8, sprites.as_ptr() as *const u8,
@ -731,12 +743,6 @@ impl MetalRenderer {
); );
} }
let next_offset = *offset + sprite_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced( command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle, metal::MTLPrimitiveType::Triangle,
0, 0,
@ -744,6 +750,7 @@ impl MetalRenderer {
sprites.len() as u64, sprites.len() as u64,
); );
*offset = next_offset; *offset = next_offset;
true
} }
fn draw_polychrome_sprites( fn draw_polychrome_sprites(
@ -753,9 +760,9 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
if sprites.is_empty() { if sprites.is_empty() {
return; return true;
} }
align_offset(offset); align_offset(offset);
@ -794,6 +801,12 @@ impl MetalRenderer {
let sprite_bytes_len = std::mem::size_of_val(sprites); let sprite_bytes_len = std::mem::size_of_val(sprites);
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) }; let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
let next_offset = *offset + sprite_bytes_len;
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
unsafe { unsafe {
ptr::copy_nonoverlapping( ptr::copy_nonoverlapping(
sprites.as_ptr() as *const u8, sprites.as_ptr() as *const u8,
@ -802,12 +815,6 @@ impl MetalRenderer {
); );
} }
let next_offset = *offset + sprite_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced( command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle, metal::MTLPrimitiveType::Triangle,
0, 0,
@ -815,6 +822,7 @@ impl MetalRenderer {
sprites.len() as u64, sprites.len() as u64,
); );
*offset = next_offset; *offset = next_offset;
true
} }
fn draw_surfaces( fn draw_surfaces(
@ -823,7 +831,7 @@ impl MetalRenderer {
offset: &mut usize, offset: &mut usize,
viewport_size: Size<DevicePixels>, viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef, command_encoder: &metal::RenderCommandEncoderRef,
) { ) -> bool {
command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state); command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state);
command_encoder.set_vertex_buffer( command_encoder.set_vertex_buffer(
SurfaceInputIndex::Vertices as u64, SurfaceInputIndex::Vertices as u64,
@ -874,10 +882,9 @@ impl MetalRenderer {
align_offset(offset); align_offset(offset);
let next_offset = *offset + mem::size_of::<Surface>(); let next_offset = *offset + mem::size_of::<Surface>();
assert!( if next_offset > INSTANCE_BUFFER_SIZE {
next_offset <= INSTANCE_BUFFER_SIZE, return false;
"instance buffer exhausted" }
);
command_encoder.set_vertex_buffer( command_encoder.set_vertex_buffer(
SurfaceInputIndex::Surfaces as u64, SurfaceInputIndex::Surfaces as u64,
@ -913,6 +920,7 @@ impl MetalRenderer {
command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6); command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
*offset = next_offset; *offset = next_offset;
} }
true
} }
} }

View file

@ -41,13 +41,13 @@ impl From<ViewId> for EntityId {
pub struct Scene { pub struct Scene {
layers_by_order: BTreeMap<StackingOrder, LayerId>, layers_by_order: BTreeMap<StackingOrder, LayerId>,
orders_by_layer: BTreeMap<LayerId, StackingOrder>, orders_by_layer: BTreeMap<LayerId, StackingOrder>,
shadows: Vec<Shadow>, pub(crate) shadows: Vec<Shadow>,
quads: Vec<Quad>, pub(crate) quads: Vec<Quad>,
paths: Vec<Path<ScaledPixels>>, pub(crate) paths: Vec<Path<ScaledPixels>>,
underlines: Vec<Underline>, pub(crate) underlines: Vec<Underline>,
monochrome_sprites: Vec<MonochromeSprite>, pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
polychrome_sprites: Vec<PolychromeSprite>, pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
surfaces: Vec<Surface>, pub(crate) surfaces: Vec<Surface>,
} }
impl Scene { impl Scene {

View file

@ -1,10 +1,10 @@
use std::{iter, mem, ops::Range}; use std::{iter, mem, ops::Range};
use crate::{ use crate::{
black, phi, point, quad, rems, AbsoluteLength, BorrowWindow, Bounds, ContentMask, Corners, black, phi, point, quad, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds,
CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, ContentMask, Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement,
FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba, SharedString, Size, Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rgba,
SizeRefinement, Styled, TextRun, WindowContext, SharedString, Size, SizeRefinement, Styled, TextRun, WindowContext,
}; };
use collections::HashSet; use collections::HashSet;
use refineable::{Cascade, Refineable}; use refineable::{Cascade, Refineable};
@ -308,54 +308,54 @@ impl Style {
} }
} }
// pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R pub fn apply_text_style<C, F, R>(&self, cx: &mut C, f: F) -> R
// where where
// C: BorrowAppContext, C: BorrowAppContext,
// F: FnOnce(&mut C) -> R, F: FnOnce(&mut C) -> R,
// { {
// if self.text.is_some() { if self.text.is_some() {
// cx.with_text_style(Some(self.text.clone()), f) cx.with_text_style(Some(self.text.clone()), f)
// } else { } else {
// f(cx) f(cx)
// } }
// } }
// /// Apply overflow to content mask /// Apply overflow to content mask
// pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R pub fn apply_overflow<C, F, R>(&self, bounds: Bounds<Pixels>, cx: &mut C, f: F) -> R
// where where
// C: BorrowWindow, C: BorrowWindow,
// F: FnOnce(&mut C) -> R, F: FnOnce(&mut C) -> R,
// { {
// let current_mask = cx.content_mask(); let current_mask = cx.content_mask();
// let min = current_mask.bounds.origin; let min = current_mask.bounds.origin;
// let max = current_mask.bounds.lower_right(); let max = current_mask.bounds.lower_right();
// let mask_bounds = match ( let mask_bounds = match (
// self.overflow.x == Overflow::Visible, self.overflow.x == Overflow::Visible,
// self.overflow.y == Overflow::Visible, self.overflow.y == Overflow::Visible,
// ) { ) {
// // x and y both visible // x and y both visible
// (true, true) => return f(cx), (true, true) => return f(cx),
// // x visible, y hidden // x visible, y hidden
// (true, false) => Bounds::from_corners( (true, false) => Bounds::from_corners(
// point(min.x, bounds.origin.y), point(min.x, bounds.origin.y),
// point(max.x, bounds.lower_right().y), point(max.x, bounds.lower_right().y),
// ), ),
// // x hidden, y visible // x hidden, y visible
// (false, true) => Bounds::from_corners( (false, true) => Bounds::from_corners(
// point(bounds.origin.x, min.y), point(bounds.origin.x, min.y),
// point(bounds.lower_right().x, max.y), point(bounds.lower_right().x, max.y),
// ), ),
// // both hidden // both hidden
// (false, false) => bounds, (false, false) => bounds,
// }; };
// let mask = ContentMask { let mask = ContentMask {
// bounds: mask_bounds, bounds: mask_bounds,
// }; };
// cx.with_content_mask(Some(mask), f) cx.with_content_mask(Some(mask), f)
// } }
/// Paints the background of an element styled with this style. /// Paints the background of an element styled with this style.
pub fn paint( pub fn paint(

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle, self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, DefiniteLength, Display, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length,
SharedString, StyleRefinement, Visibility, WhiteSpace, Position, SharedString, StyleRefinement, Visibility, WhiteSpace,
}; };
use crate::{BoxShadow, TextStyleRefinement}; use crate::{BoxShadow, TextStyleRefinement};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
@ -494,6 +494,13 @@ pub trait Styled: Sized {
self self
} }
fn font_weight(mut self, weight: FontWeight) -> Self {
self.text_style()
.get_or_insert_with(Default::default)
.font_weight = Some(weight);
self
}
fn text_bg(mut self, bg: impl Into<Hsla>) -> Self { fn text_bg(mut self, bg: impl Into<Hsla>) -> Self {
self.text_style() self.text_style()
.get_or_insert_with(Default::default) .get_or_insert_with(Default::default)

View file

@ -275,20 +275,6 @@ impl ToTaffy<taffy::style::Style> for Style {
} }
} }
// impl ToTaffy for Bounds<Length> {
// type Output = taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto>;
// fn to_taffy(
// &self,
// rem_size: Pixels,
// ) -> taffy::prelude::Bounds<taffy::prelude::LengthPercentageAuto> {
// taffy::prelude::Bounds {
// origin: self.origin.to_taffy(rem_size),
// size: self.size.to_taffy(rem_size),
// }
// }
// }
impl ToTaffy<taffy::style::LengthPercentageAuto> for Length { impl ToTaffy<taffy::style::LengthPercentageAuto> for Length {
fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto { fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto {
match self { match self {

View file

@ -59,7 +59,7 @@ impl TextSystem {
fallback_font_stack: smallvec![ fallback_font_stack: smallvec![
// TODO: This is currently Zed-specific. // TODO: This is currently Zed-specific.
// We should allow GPUI users to provide their own fallback font stack. // We should allow GPUI users to provide their own fallback font stack.
font("Zed Mono"), font("Zed Sans"),
font("Helvetica") font("Helvetica")
], ],
} }

View file

@ -3319,13 +3319,6 @@ impl AnyWindowHandle {
} }
} }
// #[cfg(any(test, feature = "test-support"))]
// impl From<SmallVec<[u32; 16]>> for StackingOrder {
// fn from(small_vec: SmallVec<[u32; 16]>) -> Self {
// StackingOrder(small_vec)
// }
// }
/// An identifier for an [`Element`](crate::Element). /// An identifier for an [`Element`](crate::Element).
/// ///
/// Can be constructed with a string, a number, or both, as well /// Can be constructed with a string, a number, or both, as well

View file

@ -1,16 +1,3 @@
// Input:
//
// struct FooBar {}
// Output:
//
// struct FooBar {}
//
// #[allow(non_snake_case)]
// #[gpui2::ctor]
// fn register_foobar_builder() {
// gpui2::register_action_builder::<Foo>()
// }
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Ident; use proc_macro2::Ident;
use quote::{format_ident, quote}; use quote::{format_ident, quote};

View file

@ -99,20 +99,6 @@ pub trait Item {
fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>; fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
} }
// Language server state is stored across 3 collections:
// language_servers =>
// a mapping from unique server id to LanguageServerState which can either be a task for a
// server in the process of starting, or a running server with adapter and language server arcs
// language_server_ids => a mapping from worktreeId and server name to the unique server id
// language_server_statuses => a mapping from unique server id to the current server status
//
// Multiple worktrees can map to the same language server for example when you jump to the definition
// of a file in the standard library. So language_server_ids is used to look up which server is active
// for a given worktree and language server name
//
// When starting a language server, first the id map is checked to make sure a server isn't already available
// for that worktree. If there is one, it finishes early. Otherwise, a new id is allocated and and
// the Starting variant of LanguageServerState is stored in the language_servers map.
pub struct Project { pub struct Project {
worktrees: Vec<WorktreeHandle>, worktrees: Vec<WorktreeHandle>,
active_entry: Option<ProjectEntryId>, active_entry: Option<ProjectEntryId>,

View file

@ -1,6 +1,6 @@
pub mod file_associations; pub mod file_associations;
mod project_panel_settings; mod project_panel_settings;
use settings::{Settings, SettingsStore}; use settings::Settings;
use db::kvp::KEY_VALUE_STORE; use db::kvp::KEY_VALUE_STORE;
use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor}; use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor};
@ -221,10 +221,10 @@ impl ProjectPanel {
}) })
.detach(); .detach();
// cx.observe_global::<FileAssociations, _>(|_, cx| { cx.observe_global::<FileAssociations>(|_, cx| {
// cx.notify(); cx.notify();
// }) })
// .detach(); .detach();
let mut this = Self { let mut this = Self {
project: project.clone(), project: project.clone(),
@ -246,18 +246,6 @@ impl ProjectPanel {
}; };
this.update_visible_entries(None, cx); this.update_visible_entries(None, cx);
// Update the dock position when the setting changes.
let mut old_dock_position = this.position(cx);
ProjectPanelSettings::register(cx);
cx.observe_global::<SettingsStore>(move |this, cx| {
let new_dock_position = this.position(cx);
if new_dock_position != old_dock_position {
old_dock_position = new_dock_position;
cx.emit(PanelEvent::ChangePosition);
}
})
.detach();
this this
}); });
@ -292,16 +280,16 @@ impl ProjectPanel {
} }
&Event::SplitEntry { entry_id } => { &Event::SplitEntry { entry_id } => {
if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) { if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) {
if let Some(_entry) = worktree.read(cx).entry_for_id(entry_id) { if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
// workspace workspace
// .split_path( .split_path(
// ProjectPath { ProjectPath {
// worktree_id: worktree.read(cx).id(), worktree_id: worktree.read(cx).id(),
// path: entry.path.clone(), path: entry.path.clone(),
// }, },
// cx, cx,
// ) )
// .detach_and_log_err(cx); .detach_and_log_err(cx);
} }
} }
} }
@ -788,10 +776,6 @@ impl ProjectPanel {
cx.notify(); cx.notify();
} }
} }
// cx.update_global(|drag_and_drop: &mut DragAndDrop<Workspace>, cx| {
// drag_and_drop.cancel_dragging::<ProjectEntryId>(cx);
// })
} }
} }

View file

@ -69,13 +69,6 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
path: parse_quote!(Clone), path: parse_quote!(Clone),
})); }));
// punctuated.push_punct(syn::token::Add::default());
// punctuated.push_value(TypeParamBound::Trait(TraitBound {
// paren_token: None,
// modifier: syn::TraitBoundModifier::None,
// lifetimes: None,
// path: parse_quote!(Default),
// }));
punctuated punctuated
}, },
}) })
@ -94,10 +87,6 @@ pub fn derive_refineable(input: TokenStream) -> TokenStream {
}, },
}; };
// refinable_refine_assignments
// refinable_refined_assignments
// refinement_refine_assignments
let refineable_refine_assignments: Vec<TokenStream2> = fields let refineable_refine_assignments: Vec<TokenStream2> = fields
.iter() .iter()
.map(|field| { .map(|field| {

View file

@ -39,6 +39,7 @@ pub struct RichText {
/// Allows one to specify extra links to the rendered markdown, which can be used /// Allows one to specify extra links to the rendered markdown, which can be used
/// for e.g. mentions. /// for e.g. mentions.
#[derive(Debug)]
pub struct Mention { pub struct Mention {
pub range: Range<usize>, pub range: Range<usize>,
pub is_self_mention: bool, pub is_self_mention: bool,
@ -85,31 +86,6 @@ impl RichText {
}) })
.into_any_element() .into_any_element()
} }
// pub fn add_mention(
// &mut self,
// range: Range<usize>,
// is_current_user: bool,
// mention_style: HighlightStyle,
// ) -> anyhow::Result<()> {
// if range.end > self.text.len() {
// bail!(
// "Mention in range {range:?} is outside of bounds for a message of length {}",
// self.text.len()
// );
// }
// if is_current_user {
// self.region_ranges.push(range.clone());
// self.regions.push(RenderedRegion {
// background_kind: Some(BackgroundKind::Mention),
// link_url: None,
// });
// }
// self.highlights
// .push((range, Highlight::Highlight(mention_style)));
// Ok(())
// }
} }
pub fn render_markdown_mut( pub fn render_markdown_mut(
@ -138,20 +114,21 @@ pub fn render_markdown_mut(
if let Some(language) = &current_language { if let Some(language) = &current_language {
render_code(text, highlights, t.as_ref(), language); render_code(text, highlights, t.as_ref(), language);
} else { } else {
if let Some(mention) = mentions.first() { while let Some(mention) = mentions.first() {
if source_range.contains_inclusive(&mention.range) { if !source_range.contains_inclusive(&mention.range) {
mentions = &mentions[1..]; break;
let range = (prev_len + mention.range.start - source_range.start)
..(prev_len + mention.range.end - source_range.start);
highlights.push((
range.clone(),
if mention.is_self_mention {
Highlight::SelfMention
} else {
Highlight::Mention
},
));
} }
mentions = &mentions[1..];
let range = (prev_len + mention.range.start - source_range.start)
..(prev_len + mention.range.end - source_range.start);
highlights.push((
range.clone(),
if mention.is_self_mention {
Highlight::SelfMention
} else {
Highlight::Mention
},
));
} }
text.push_str(t.as_ref()); text.push_str(t.as_ref());
@ -272,13 +249,6 @@ pub fn render_markdown(
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
language: Option<&Arc<Language>>, language: Option<&Arc<Language>>,
) -> RichText { ) -> RichText {
// let mut data = RichText {
// text: Default::default(),
// highlights: Default::default(),
// region_ranges: Default::default(),
// regions: Default::default(),
// };
let mut text = String::new(); let mut text = String::new();
let mut highlights = Vec::new(); let mut highlights = Vec::new();
let mut link_ranges = Vec::new(); let mut link_ranges = Vec::new();

View file

@ -1,6 +1,5 @@
fn main() { fn main() {
let mut build = prost_build::Config::new(); let mut build = prost_build::Config::new();
// build.protoc_arg("--experimental_allow_proto3_optional");
build build
.type_attribute(".", "#[derive(serde::Serialize)]") .type_attribute(".", "#[derive(serde::Serialize)]")
.compile_protos(&["proto/zed.proto"], &["proto"]) .compile_protos(&["proto/zed.proto"], &["proto"])

View file

@ -1648,7 +1648,6 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_search_query_history(cx: &mut TestAppContext) { async fn test_search_query_history(cx: &mut TestAppContext) {
//crate::project_search::tests::init_test(cx);
init_globals(cx); init_globals(cx);
let buffer_text = r#" let buffer_text = r#"
A regular expression (shortened as regex or regexp;[1] also referred to as A regular expression (shortened as regex or regexp;[1] also referred to as

View file

@ -1677,8 +1677,6 @@ fn elixir_lang() -> Arc<Language> {
#[gpui::test] #[gpui::test]
fn test_subtract_ranges() { fn test_subtract_ranges() {
// collapsed_ranges: Vec<Range<usize>>, keep_ranges: Vec<Range<usize>>
assert_eq!( assert_eq!(
subtract_ranges(&[0..5, 10..21], &[0..1, 4..5]), subtract_ranges(&[0..5, 10..21], &[0..1, 4..5]),
vec![1..4, 10..21] vec![1..4, 10..21]

View file

@ -21,11 +21,6 @@ use crate::assets::Assets;
use crate::story_selector::{ComponentStory, StorySelector}; use crate::story_selector::{ComponentStory, StorySelector};
pub use indoc::indoc; pub use indoc::indoc;
// gpui::actions! {
// storybook,
// [ToggleInspector]
// }
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {

View file

@ -11,7 +11,7 @@ use itertools::Itertools;
use project::{Fs, ProjectEntryId}; use project::{Fs, ProjectEntryId};
use search::{buffer_search::DivRegistrar, BufferSearchBar}; use search::{buffer_search::DivRegistrar, BufferSearchBar};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore}; use settings::Settings;
use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings}; use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
use ui::{h_stack, ButtonCommon, Clickable, IconButton, IconSize, Selectable, Tooltip}; use ui::{h_stack, ButtonCommon, Clickable, IconButton, IconSize, Selectable, Tooltip};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
@ -159,15 +159,6 @@ impl TerminalPanel {
height: None, height: None,
_subscriptions: subscriptions, _subscriptions: subscriptions,
}; };
let mut old_dock_position = this.position(cx);
cx.observe_global::<SettingsStore>(move |this, cx| {
let new_dock_position = this.position(cx);
if new_dock_position != old_dock_position {
old_dock_position = new_dock_position;
cx.emit(PanelEvent::ChangePosition);
}
})
.detach();
this this
} }

View file

@ -600,6 +600,9 @@ fn possible_open_targets(
pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option<RegexSearch> { pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option<RegexSearch> {
let query = query.as_str(); let query = query.as_str();
if query == "." {
return None;
}
let searcher = RegexSearch::new(&query); let searcher = RegexSearch::new(&query);
searcher.ok() searcher.ok()
} }

View file

@ -8,6 +8,11 @@ pub(crate) fn neutral() -> ColorScaleSet {
sand() sand()
} }
// Note: We aren't currently making use of the default colors, as all of the
// themes have a value set for each color.
//
// We'll need to revisit these once we're ready to launch user themes, which may
// not specify a value for each color (and thus should fall back to the defaults).
impl ThemeColors { impl ThemeColors {
pub fn light() -> Self { pub fn light() -> Self {
let system = SystemColors::default(); let system = SystemColors::default();
@ -23,12 +28,12 @@ impl ThemeColors {
surface_background: neutral().light().step_2(), surface_background: neutral().light().step_2(),
background: neutral().light().step_1(), background: neutral().light().step_1(),
element_background: neutral().light().step_3(), element_background: neutral().light().step_3(),
element_hover: neutral().light_alpha().step_4(), // todo!("pick the right colors") element_hover: neutral().light_alpha().step_4(),
element_active: neutral().light_alpha().step_5(), element_active: neutral().light_alpha().step_5(),
element_selected: neutral().light_alpha().step_5(), element_selected: neutral().light_alpha().step_5(),
element_disabled: neutral().light_alpha().step_3(), // todo!("pick the right colors") element_disabled: neutral().light_alpha().step_3(),
drop_target_background: blue().light_alpha().step_2(), // todo!("pick the right colors") drop_target_background: blue().light_alpha().step_2(),
ghost_element_background: system.transparent, // todo!("pick the right colors") ghost_element_background: system.transparent,
ghost_element_hover: neutral().light_alpha().step_3(), ghost_element_hover: neutral().light_alpha().step_3(),
ghost_element_active: neutral().light_alpha().step_4(), ghost_element_active: neutral().light_alpha().step_4(),
ghost_element_selected: neutral().light_alpha().step_5(), ghost_element_selected: neutral().light_alpha().step_5(),
@ -59,7 +64,7 @@ impl ThemeColors {
scrollbar_track_background: gpui::transparent_black(), scrollbar_track_background: gpui::transparent_black(),
scrollbar_track_border: neutral().light().step_5(), scrollbar_track_border: neutral().light().step_5(),
editor_foreground: neutral().light().step_12(), editor_foreground: neutral().light().step_12(),
editor_background: neutral().light().step_1(), // todo!(this was inserted by Mikayla) editor_background: neutral().light().step_1(),
editor_gutter_background: neutral().light().step_1(), editor_gutter_background: neutral().light().step_1(),
editor_subheader_background: neutral().light().step_2(), editor_subheader_background: neutral().light().step_2(),
editor_active_line_background: neutral().light_alpha().step_3(), editor_active_line_background: neutral().light_alpha().step_3(),
@ -106,17 +111,17 @@ impl ThemeColors {
surface_background: neutral().dark().step_2(), surface_background: neutral().dark().step_2(),
background: neutral().dark().step_1(), background: neutral().dark().step_1(),
element_background: neutral().dark().step_3(), element_background: neutral().dark().step_3(),
element_hover: neutral().dark_alpha().step_4(), // todo!("pick the right colors") element_hover: neutral().dark_alpha().step_4(),
element_active: neutral().dark_alpha().step_5(), element_active: neutral().dark_alpha().step_5(),
element_selected: neutral().dark_alpha().step_5(), // todo!("pick the right colors") element_selected: neutral().dark_alpha().step_5(),
element_disabled: neutral().dark_alpha().step_3(), // todo!("pick the right colors") element_disabled: neutral().dark_alpha().step_3(),
drop_target_background: blue().dark_alpha().step_2(), drop_target_background: blue().dark_alpha().step_2(),
ghost_element_background: system.transparent, ghost_element_background: system.transparent,
ghost_element_hover: neutral().dark_alpha().step_4(), // todo!("pick the right colors") ghost_element_hover: neutral().dark_alpha().step_4(),
ghost_element_active: neutral().dark_alpha().step_5(), // todo!("pick the right colors") ghost_element_active: neutral().dark_alpha().step_5(),
ghost_element_selected: neutral().dark_alpha().step_5(), ghost_element_selected: neutral().dark_alpha().step_5(),
ghost_element_disabled: neutral().dark_alpha().step_3(), ghost_element_disabled: neutral().dark_alpha().step_3(),
text: neutral().dark().step_12(), // todo!("pick the right colors") text: neutral().dark().step_12(),
text_muted: neutral().dark().step_11(), text_muted: neutral().dark().step_11(),
text_placeholder: neutral().dark().step_10(), text_placeholder: neutral().dark().step_10(),
text_disabled: neutral().dark().step_9(), text_disabled: neutral().dark().step_9(),
@ -140,7 +145,7 @@ impl ThemeColors {
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(), scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
scrollbar_thumb_border: gpui::transparent_black(), scrollbar_thumb_border: gpui::transparent_black(),
scrollbar_track_background: gpui::transparent_black(), scrollbar_track_background: gpui::transparent_black(),
scrollbar_track_border: neutral().dark().step_5(), // todo!(this was inserted by Mikayla) scrollbar_track_border: neutral().dark().step_5(),
editor_foreground: neutral().dark().step_12(), editor_foreground: neutral().dark().step_12(),
editor_background: neutral().dark().step_1(), editor_background: neutral().dark().step_1(),
editor_gutter_background: neutral().dark().step_1(), editor_gutter_background: neutral().dark().step_1(),

View file

@ -7,6 +7,10 @@ use crate::{
ThemeColors, ThemeFamily, ThemeStyles, ThemeColors, ThemeFamily, ThemeStyles,
}; };
// Note: This theme family is not the one you see in Zed at the moment.
// This is a from-scratch rebuild that Nate started work on. We currently
// only use this in the tests, and the One family from the `themes/` directory
// is what gets loaded into Zed when running it.
pub fn one_family() -> ThemeFamily { pub fn one_family() -> ThemeFamily {
ThemeFamily { ThemeFamily {
id: "one".to_string(), id: "one".to_string(),
@ -75,7 +79,7 @@ pub(crate) fn one_dark() -> Theme {
tab_bar_background: bg, tab_bar_background: bg,
tab_inactive_background: bg, tab_inactive_background: bg,
tab_active_background: editor, tab_active_background: editor,
search_match_background: bg, // todo!(this was inserted by Mikayla) search_match_background: bg,
editor_background: editor, editor_background: editor,
editor_gutter_background: editor, editor_gutter_background: editor,

View file

@ -26,6 +26,7 @@ pub enum AvatarShape {
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct Avatar { pub struct Avatar {
image: Img, image: Img,
size: Option<Pixels>,
border_color: Option<Hsla>, border_color: Option<Hsla>,
is_available: Option<bool>, is_available: Option<bool>,
} }
@ -36,7 +37,7 @@ impl RenderOnce for Avatar {
self = self.shape(AvatarShape::Circle); self = self.shape(AvatarShape::Circle);
} }
let size = cx.rem_size(); let size = self.size.unwrap_or_else(|| cx.rem_size());
div() div()
.size(size + px(2.)) .size(size + px(2.))
@ -78,6 +79,7 @@ impl Avatar {
image: img(src), image: img(src),
is_available: None, is_available: None,
border_color: None, border_color: None,
size: None,
} }
} }
@ -124,4 +126,10 @@ impl Avatar {
self.is_available = is_available.into(); self.is_available = is_available.into();
self self
} }
/// Size overrides the avatar size. By default they are 1rem.
pub fn size(mut self, size: impl Into<Option<Pixels>>) -> Self {
self.size = size.into();
self
}
} }

View file

@ -117,55 +117,5 @@ impl Render for IconButtonStory {
) )
.children(vec![StorySection::new().children(buttons)]) .children(vec![StorySection::new().children(buttons)])
.into_element() .into_element()
// Story::container()
// .child(Story::title_for::<IconButton>())
// .child(Story::label("Default"))
// .child(div().w_8().child(IconButton::new("icon_a", Icon::Hash)))
// .child(Story::label("Selected"))
// .child(
// div()
// .w_8()
// .child(IconButton::new("icon_a", Icon::Hash).selected(true)),
// )
// .child(Story::label("Selected with `selected_icon`"))
// .child(
// div().w_8().child(
// IconButton::new("icon_a", Icon::AudioOn)
// .selected(true)
// .selected_icon(Icon::AudioOff),
// ),
// )
// .child(Story::label("Disabled"))
// .child(
// div()
// .w_8()
// .child(IconButton::new("icon_a", Icon::Hash).disabled(true)),
// )
// .child(Story::label("With `on_click`"))
// .child(
// div()
// .w_8()
// .child(
// IconButton::new("with_on_click", Icon::Ai).on_click(|_event, _cx| {
// println!("Clicked!");
// }),
// ),
// )
// .child(Story::label("With `tooltip`"))
// .child(
// div().w_8().child(
// IconButton::new("with_tooltip", Icon::MessageBubbles)
// .tooltip(|cx| Tooltip::text("Open messages", cx)),
// ),
// )
// .child(Story::label("Selected with `tooltip`"))
// .child(
// div().w_8().child(
// IconButton::new("selected_with_tooltip", Icon::InlayHint)
// .selected(true)
// .tooltip(|cx| Tooltip::text("Toggle inlay hints", cx)),
// ),
// )
} }
} }

View file

@ -37,7 +37,7 @@ impl Color {
Color::Info => cx.theme().status().info, Color::Info => cx.theme().status().info,
Color::Placeholder => cx.theme().colors().text_placeholder, Color::Placeholder => cx.theme().colors().text_placeholder,
Color::Accent => cx.theme().colors().text_accent, Color::Accent => cx.theme().colors().text_accent,
Color::Player(i) => cx.theme().styles.player.0[i.clone() as usize].cursor, Color::Player(i) => cx.theme().styles.player.color_for_participant(*i).cursor,
Color::Error => cx.theme().status().error, Color::Error => cx.theme().status().error,
Color::Selected => cx.theme().colors().text_accent, Color::Selected => cx.theme().colors().text_accent,
Color::Success => cx.theme().status().success, Color::Success => cx.theme().status().success,

View file

@ -7,6 +7,7 @@ use gpui::{
}; };
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::SettingsStore;
use std::sync::Arc; use std::sync::Arc;
use ui::{h_stack, ContextMenu, IconButton, Tooltip}; use ui::{h_stack, ContextMenu, IconButton, Tooltip};
use ui::{prelude::*, right_click_menu}; use ui::{prelude::*, right_click_menu};
@ -14,7 +15,6 @@ use ui::{prelude::*, right_click_menu};
const RESIZE_HANDLE_SIZE: Pixels = Pixels(6.); const RESIZE_HANDLE_SIZE: Pixels = Pixels(6.);
pub enum PanelEvent { pub enum PanelEvent {
ChangePosition,
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
Activate, Activate,
@ -177,7 +177,7 @@ impl DockPosition {
struct PanelEntry { struct PanelEntry {
panel: Arc<dyn PanelHandle>, panel: Arc<dyn PanelHandle>,
_subscriptions: [Subscription; 2], _subscriptions: [Subscription; 3],
} }
pub struct PanelButtons { pub struct PanelButtons {
@ -321,9 +321,15 @@ impl Dock {
) { ) {
let subscriptions = [ let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()), cx.observe(&panel, |_, _, cx| cx.notify()),
cx.subscribe(&panel, move |this, panel, event, cx| match event { cx.observe_global::<SettingsStore>({
PanelEvent::ChangePosition => { let workspace = workspace.clone();
let panel = panel.clone();
move |this, cx| {
let new_position = panel.read(cx).position(cx); let new_position = panel.read(cx).position(cx);
if new_position == this.position {
return;
}
let Ok(new_dock) = workspace.update(cx, |workspace, cx| { let Ok(new_dock) = workspace.update(cx, |workspace, cx| {
if panel.is_zoomed(cx) { if panel.is_zoomed(cx) {
@ -354,6 +360,8 @@ impl Dock {
} }
}); });
} }
}),
cx.subscribe(&panel, move |this, panel, event, cx| match event {
PanelEvent::ZoomIn => { PanelEvent::ZoomIn => {
this.set_panel_zoomed(&panel.to_any(), true, cx); this.set_panel_zoomed(&panel.to_any(), true, cx);
if !panel.focus_handle(cx).contains_focused(cx) { if !panel.focus_handle(cx).contains_focused(cx) {
@ -737,7 +745,7 @@ pub mod test {
fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) { fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext<Self>) {
self.position = position; self.position = position;
cx.emit(PanelEvent::ChangePosition); cx.update_global::<SettingsStore, _>(|_, _| {});
} }
fn size(&self, _: &WindowContext) -> Pixels { fn size(&self, _: &WindowContext) -> Pixels {

View file

@ -809,27 +809,6 @@ pub mod test {
Edit, Edit,
} }
// impl Clone for TestItem {
// fn clone(&self) -> Self {
// Self {
// state: self.state.clone(),
// label: self.label.clone(),
// save_count: self.save_count,
// save_as_count: self.save_as_count,
// reload_count: self.reload_count,
// is_dirty: self.is_dirty,
// is_singleton: self.is_singleton,
// has_conflict: self.has_conflict,
// project_items: self.project_items.clone(),
// nav_history: None,
// tab_descriptions: None,
// tab_detail: Default::default(),
// workspace_id: self.workspace_id,
// focus_handle: self.focus_handle.clone(),
// }
// }
// }
impl TestProjectItem { impl TestProjectItem {
pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Model<Self> { pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Model<Self> {
let entry_id = Some(ProjectEntryId::from_proto(id)); let entry_id = Some(ProjectEntryId::from_proto(id));

View file

@ -60,24 +60,6 @@ pub enum SaveIntent {
#[derive(Clone, Deserialize, PartialEq, Debug)] #[derive(Clone, Deserialize, PartialEq, Debug)]
pub struct ActivateItem(pub usize); pub struct ActivateItem(pub usize);
// #[derive(Clone, PartialEq)]
// pub struct CloseItemById {
// pub item_id: usize,
// pub pane: WeakView<Pane>,
// }
// #[derive(Clone, PartialEq)]
// pub struct CloseItemsToTheLeftById {
// pub item_id: usize,
// pub pane: WeakView<Pane>,
// }
// #[derive(Clone, PartialEq)]
// pub struct CloseItemsToTheRightById {
// pub item_id: usize,
// pub pane: WeakView<Pane>,
// }
#[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CloseActiveItem { pub struct CloseActiveItem {
@ -1226,125 +1208,6 @@ impl Pane {
cx.emit(Event::Split(direction)); cx.emit(Event::Split(direction));
} }
// fn deploy_split_menu(&mut self, cx: &mut ViewContext<Self>) {
// self.tab_bar_context_menu.handle.update(cx, |menu, cx| {
// menu.toggle(
// Default::default(),
// AnchorCorner::TopRight,
// vec![
// ContextMenuItem::action("Split Right", SplitRight),
// ContextMenuItem::action("Split Left", SplitLeft),
// ContextMenuItem::action("Split Up", SplitUp),
// ContextMenuItem::action("Split Down", SplitDown),
// ],
// cx,
// );
// });
// self.tab_bar_context_menu.kind = TabBarContextMenuKind::Split;
// }
// fn deploy_new_menu(&mut self, cx: &mut ViewContext<Self>) {
// self.tab_bar_context_menu.handle.update(cx, |menu, cx| {
// menu.toggle(
// Default::default(),
// AnchorCorner::TopRight,
// vec![
// ContextMenuItem::action("New File", NewFile),
// ContextMenuItem::action("New Terminal", NewCenterTerminal),
// ContextMenuItem::action("New Search", NewSearch),
// ],
// cx,
// );
// });
// self.tab_bar_context_menu.kind = TabBarContextMenuKind::New;
// }
// fn deploy_tab_context_menu(
// &mut self,
// position: Vector2F,
// target_item_id: usize,
// cx: &mut ViewContext<Self>,
// ) {
// let active_item_id = self.items[self.active_item_index].id();
// let is_active_item = target_item_id == active_item_id;
// let target_pane = cx.weak_handle();
// // The `CloseInactiveItems` action should really be called "CloseOthers" and the behaviour should be dynamically based on the tab the action is ran on. Currently, this is a weird action because you can run it on a non-active tab and it will close everything by the actual active tab
// self.tab_context_menu.update(cx, |menu, cx| {
// menu.show(
// position,
// AnchorCorner::TopLeft,
// if is_active_item {
// vec![
// ContextMenuItem::action(
// "Close Active Item",
// CloseActiveItem { save_intent: None },
// ),
// ContextMenuItem::action("Close Inactive Items", CloseInactiveItems),
// ContextMenuItem::action("Close Clean Items", CloseCleanItems),
// ContextMenuItem::action("Close Items To The Left", CloseItemsToTheLeft),
// ContextMenuItem::action("Close Items To The Right", CloseItemsToTheRight),
// ContextMenuItem::action(
// "Close All Items",
// CloseAllItems { save_intent: None },
// ),
// ]
// } else {
// // In the case of the user right clicking on a non-active tab, for some item-closing commands, we need to provide the id of the tab, for the others, we can reuse the existing command.
// vec![
// ContextMenuItem::handler("Close Inactive Item", {
// let pane = target_pane.clone();
// move |cx| {
// if let Some(pane) = pane.upgrade(cx) {
// pane.update(cx, |pane, cx| {
// pane.close_item_by_id(
// target_item_id,
// SaveIntent::Close,
// cx,
// )
// .detach_and_log_err(cx);
// })
// }
// }
// }),
// ContextMenuItem::action("Close Inactive Items", CloseInactiveItems),
// ContextMenuItem::action("Close Clean Items", CloseCleanItems),
// ContextMenuItem::handler("Close Items To The Left", {
// let pane = target_pane.clone();
// move |cx| {
// if let Some(pane) = pane.upgrade(cx) {
// pane.update(cx, |pane, cx| {
// pane.close_items_to_the_left_by_id(target_item_id, cx)
// .detach_and_log_err(cx);
// })
// }
// }
// }),
// ContextMenuItem::handler("Close Items To The Right", {
// let pane = target_pane.clone();
// move |cx| {
// if let Some(pane) = pane.upgrade(cx) {
// pane.update(cx, |pane, cx| {
// pane.close_items_to_the_right_by_id(target_item_id, cx)
// .detach_and_log_err(cx);
// })
// }
// }
// }),
// ContextMenuItem::action(
// "Close All Items",
// CloseAllItems { save_intent: None },
// ),
// ]
// },
// cx,
// );
// });
// }
pub fn toolbar(&self) -> &View<Toolbar> { pub fn toolbar(&self) -> &View<Toolbar> {
&self.toolbar &self.toolbar
} }

View file

@ -1,5 +1,3 @@
//#![allow(dead_code)]
pub mod model; pub mod model;
use std::path::Path; use std::path::Path;

View file

@ -3324,36 +3324,6 @@ impl Workspace {
workspace workspace
} }
// fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
// let dock = match position {
// DockPosition::Left => &self.left_dock,
// DockPosition::Right => &self.right_dock,
// DockPosition::Bottom => &self.bottom_dock,
// };
// let active_panel = dock.read(cx).visible_panel()?;
// let element = if Some(active_panel.id()) == self.zoomed.as_ref().map(|zoomed| zoomed.id()) {
// dock.read(cx).render_placeholder(cx)
// } else {
// ChildView::new(dock, cx).into_any()
// };
// Some(
// element
// .constrained()
// .dynamically(move |constraint, _, cx| match position {
// DockPosition::Left | DockPosition::Right => SizeConstraint::new(
// Vector2F::new(20., constraint.min.y()),
// Vector2F::new(cx.window_size().x() * 0.8, constraint.max.y()),
// ),
// DockPosition::Bottom => SizeConstraint::new(
// Vector2F::new(constraint.min.x(), 20.),
// Vector2F::new(constraint.max.x(), cx.window_size().y() * 0.8),
// ),
// })
// .into_any(),
// )
// }
// }
pub fn register_action<A: Action>( pub fn register_action<A: Action>(
&mut self, &mut self,
callback: impl Fn(&mut Self, &A, &mut ViewContext<Self>) + 'static, callback: impl Fn(&mut Self, &A, &mut ViewContext<Self>) + 'static,

View file

@ -184,7 +184,7 @@ mod tests {
#[gpui::test] #[gpui::test]
async fn test_python_autoindent(cx: &mut TestAppContext) { async fn test_python_autoindent(cx: &mut TestAppContext) {
// cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX); cx.executor().set_block_on_ticks(usize::MAX..=usize::MAX);
let language = let language =
crate::languages::language("python", tree_sitter_python::language(), None).await; crate::languages::language("python", tree_sitter_python::language(), None).await;
cx.update(|cx| { cx.update(|cx| {

View file

@ -113,12 +113,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
}) })
.detach(); .detach();
// cx.emit(workspace::Event::PaneAdded(workspace.active_pane().clone()));
// let collab_titlebar_item =
// cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx));
// workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx);
let copilot = cx.new_view(|cx| copilot_ui::CopilotButton::new(app_state.fs.clone(), cx)); let copilot = cx.new_view(|cx| copilot_ui::CopilotButton::new(app_state.fs.clone(), cx));
let diagnostic_summary = let diagnostic_summary =
cx.new_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx)); cx.new_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));

27
script/squawk Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Squawk is a linter for database migrations. It helps identify dangerous patterns, and suggests alternatives.
# Squawk flagging an error does not mean that you need to take a different approach, but it does indicate you need to think about what you're doing.
# See also: https://squawkhq.com
set -e
SQUAWK_VERSION=0.26.0
SQUAWK_BIN="./target/squawk-$SQUAWK_VERSION"
SQUAWK_ARGS="--assume-in-transaction"
if [ ! -f "$SQUAWK_BIN" ]; then
curl -L -o "$SQUAWK_BIN" "https://github.com/sbdchd/squawk/releases/download/v$SQUAWK_VERSION/squawk-darwin-x86_64"
chmod +x "$SQUAWK_BIN"
fi
if [ -n "$SQUAWK_GITHUB_TOKEN" ]; then
export SQUAWK_GITHUB_REPO_OWNER=$(echo $GITHUB_REPOSITORY | awk -F/ '{print $1}')
export SQUAWK_GITHUB_REPO_NAME=$(echo $GITHUB_REPOSITORY | awk -F/ '{print $2}')
export SQUAWK_GITHUB_PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
$SQUAWK_BIN $SQUAWK_ARGS upload-to-github $(git diff --name-only origin/$GITHUB_BASE_REF...origin/$GITHUB_HEAD_REF 'crates/collab/migrations/*.sql')
else
$SQUAWK_BIN $SQUAWK_ARGS $(git ls-files --others crates/collab/migrations/*.sql) $(git diff --name-only main crates/collab/migrations/*.sql)
fi