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

@ -15,3 +15,9 @@ runs:
# will have more fixes & suppression for the standard lint set
run: |
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",
// 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
"ui_font_features": {
// Disable ligatures:

View file

@ -40,7 +40,7 @@ use language::{language_settings::SoftWrap, Buffer, LanguageRegistry, ToOffset a
use project::Project;
use search::{buffer_search::DivRegistrar, BufferSearchBar};
use semantic_index::{SemanticIndex, SemanticIndexStatus};
use settings::{Settings, SettingsStore};
use settings::Settings;
use std::{
cell::Cell,
cmp,
@ -165,7 +165,7 @@ impl AssistantPanel {
cx.on_focus_in(&focus_handle, Self::focus_in).detach();
cx.on_focus_out(&focus_handle, Self::focus_out).detach();
let mut this = Self {
Self {
workspace: workspace_handle,
active_editor_index: Default::default(),
prev_active_editor_index: Default::default(),
@ -190,20 +190,7 @@ impl AssistantPanel {
_watch_saved_conversations,
semantic_index,
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 ai::test::FakeCompletionProvider;
use gpui::AppContext;
use settings::SettingsStore;
#[gpui::test]
fn test_inserting_and_removing_messages(cx: &mut AppContext) {

View file

@ -442,6 +442,8 @@ impl ActiveCall {
.location
.as_ref()
.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))
}
} else {

View file

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

View file

@ -144,7 +144,7 @@ impl ChannelChat {
message: MessageParams,
cx: &mut ModelContext<Self>,
) -> Result<Task<Result<u64>>> {
if message.text.is_empty() {
if message.text.trim().is_empty() {
Err(anyhow!("message body can't be empty"))?;
}
@ -174,6 +174,8 @@ impl ChannelChat {
let user_store = self.user_store.clone();
let rpc = self.rpc.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 {
let outgoing_message_guard = outgoing_messages_lock.lock().await;
let request = rpc.request(proto::SendChannelMessage {

View file

@ -14,6 +14,8 @@ use sysinfo::{
};
use tempfile::NamedTempFile;
use util::http::HttpClient;
#[cfg(not(debug_assertions))]
use util::ResultExt;
use util::{channel::ReleaseChannel, TryFutureExt};
use self::event_coalescer::EventCoalescer;
@ -167,6 +169,20 @@ impl Telemetry {
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>({
let state = state.clone();

View file

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

View file

@ -1418,8 +1418,6 @@ async fn test_channel_moving(
) {
let mut server = TestServer::start(executor.clone()).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
.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 call::ActiveCall;
use call::{room, ActiveCall};
use channel::{ChannelChat, ChannelChatEvent, ChannelMessageId, ChannelStore};
use client::Client;
use collections::HashMap;
use db::kvp::KEY_VALUE_STORE;
use editor::Editor;
use gpui::{
actions, div, list, prelude::*, px, AnyElement, AppContext, AsyncWindowContext, ClickEvent,
ElementId, EventEmitter, FocusableView, ListOffset, ListScrollEvent, ListState, Model, Render,
Subscription, Task, View, ViewContext, VisualContext, WeakView,
actions, div, list, prelude::*, px, Action, AppContext, AsyncWindowContext, DismissEvent,
ElementId, EventEmitter, FocusHandle, FocusableView, FontWeight, ListOffset, ListScrollEvent,
ListState, Model, Render, Subscription, Task, View, ViewContext, VisualContext, WeakView,
};
use language::LanguageRegistry;
use menu::Confirm;
@ -17,10 +17,13 @@ use message_editor::MessageEditor;
use project::Fs;
use rich_text::RichText;
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use settings::Settings;
use std::sync::Arc;
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 workspace::{
dock::{DockPosition, Panel, PanelEvent},
@ -54,9 +57,10 @@ pub struct ChatPanel {
active: bool,
pending_serialization: Task<Option<()>>,
subscriptions: Vec<gpui::Subscription>,
workspace: WeakView<Workspace>,
is_scrolled_to_bottom: bool,
markdown_data: HashMap<ChannelMessageId, RichText>,
focus_handle: FocusHandle,
open_context_menu: Option<(u64, Subscription)>,
}
#[derive(Serialize, Deserialize)]
@ -64,13 +68,6 @@ struct SerializedChatPanel {
width: Option<Pixels>,
}
#[derive(Debug)]
pub enum Event {
DockPositionChanged,
Focus,
Dismissed,
}
actions!(chat_panel, [ToggleFocus]);
impl ChatPanel {
@ -89,8 +86,6 @@ impl ChatPanel {
)
});
let workspace_handle = workspace.weak_handle();
cx.new_view(|cx: &mut ViewContext<Self>| {
let view = cx.view().downgrade();
let message_list =
@ -108,7 +103,7 @@ impl ChatPanel {
if event.visible_range.start < MESSAGE_LOADING_THRESHOLD {
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 {
@ -122,22 +117,37 @@ impl ChatPanel {
message_editor: input_editor,
local_timezone: cx.local_timezone(),
subscriptions: Vec::new(),
workspace: workspace_handle,
is_scrolled_to_bottom: true,
active: false,
width: None,
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.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(Event::DockPositionChanged);
this.subscriptions.push(cx.subscribe(
&ActiveCall::global(cx),
move |this: &mut Self, call, event: &room::Event, cx| match event {
room::Event::RoomJoined { channel_id } => {
if let Some(channel_id) = channel_id {
this.select_channel(*channel_id, None, cx)
.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 {
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 {
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| {
let is_admin = self
.channel_store
@ -314,13 +286,9 @@ impl ChatPanel {
let last_message = active_chat.message(ix.saturating_sub(1));
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
&& 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 this_message
@ -332,12 +300,7 @@ impl ChatPanel {
}
}
(
this_message,
is_continuation_from_previous,
is_continuation_to_next,
is_admin,
)
(this_message, is_continuation_from_previous, is_admin)
});
let _is_pending = message.is_pending();
@ -360,50 +323,100 @@ impl ChatPanel {
ChannelMessageId::Saved(id) => ("saved-message", id).into(),
ChannelMessageId::Pending(id) => ("pending-message", id).into(),
};
let this = cx.view().clone();
v_stack()
.w_full()
.id(element_id)
.relative()
.overflow_hidden()
.group("")
.when(!is_continuation_from_previous, |this| {
this.child(
this.pt_3().child(
h_stack()
.gap_2()
.child(Avatar::new(message.sender.avatar_uri.clone()))
.child(Label::new(message.sender.github_login.clone()))
.child(
div().absolute().child(
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(
Label::new(format_timestamp(
message.timestamp,
now,
self.local_timezone,
))
.size(LabelSize::Small)
.color(Color::Muted),
),
)
})
.when(!is_continuation_to_next, |this|
// HACK: This should really be a margin, but margins seem to get collapsed.
this.pb_2())
.when(is_continuation_from_previous, |this| this.pt_1())
.child(
v_stack()
.w_full()
.text_ui_sm()
.id(element_id)
.group("")
.child(text.element("body".into(), cx))
.child(
div()
.absolute()
.top_1()
.right_2()
.w_8()
.visible_on_hover("")
.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| {
IconButton::new(("remove", message_id), IconName::XCircle).on_click(
cx.listener(move |this, _, cx| {
this.remove_message(message_id, cx);
}),
)
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(
language_registry: &Arc<LanguageRegistry>,
current_user_id: u64,
@ -421,44 +434,6 @@ impl ChatPanel {
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>) {
if let Some((chat, _)) = self.active_chat.as_ref() {
let message = self
@ -535,50 +510,93 @@ impl ChatPanel {
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 {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
v_stack()
.size_full()
.map(|this| match (self.client.user_id(), self.active_chat()) {
(Some(_), Some(_)) => this.child(self.render_channel(cx)),
(Some(_), None) => this.child(
div().p_4().child(
.track_focus(&self.focus_handle)
.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("Chat".to_string()),
)),
),
),
)
.child(div().flex_grow().px_2().pt_1().map(|this| {
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(),
)
}),
),
),
(None, _) => this.child(self.render_sign_in_prompt(cx)),
)
}
}))
.child(
h_stack()
.when(!self.is_scrolled_to_bottom, |el| {
el.border_t_1().border_color(cx.theme().colors().border)
})
.min_w(px(150.))
.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 {
fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
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 {
self.acknowledge_last_message(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 rpc::proto::{self, PeerId};
use serde_derive::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use settings::Settings;
use smallvec::SmallVec;
use std::{mem, sync::Arc};
use theme::{ActiveTheme, ThemeSettings};
@ -254,19 +254,6 @@ impl CollabPanel {
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);
this.subscriptions
.push(cx.observe(&this.user_store, |this, _, cx| {
@ -1426,14 +1413,6 @@ impl CollabPanel {
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>) {
match self.collapsed_channels.binary_search(&channel_id) {
Ok(ix) => {
@ -1910,7 +1889,6 @@ impl CollabPanel {
let mut channel_link = None;
let mut channel_tooltip_text = None;
let mut channel_icon = None;
// let mut is_dragged_over = false;
let text = match section {
Section::ActiveCall => {
@ -2052,7 +2030,7 @@ impl CollabPanel {
}),
)
.start_slot(
// todo!() handle contacts with no avatar
// todo handle contacts with no avatar
Avatar::new(contact.user.avatar_uri.clone())
.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);
notification_panel::init(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) {
@ -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 {
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 window in windows {
window

View file

@ -1955,17 +1955,21 @@ impl Editor {
}
}
// pub fn language_at<'a, T: ToOffset>(
// &self,
// point: T,
// cx: &'a AppContext,
// ) -> Option<Arc<Language>> {
// self.buffer.read(cx).language_at(point, cx)
// }
pub fn language_at<'a, T: ToOffset>(
&self,
point: T,
cx: &'a AppContext,
) -> Option<Arc<Language>> {
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>> {
// self.buffer.read(cx).read(cx).file_at(point).cloned()
// }
pub fn file_at<'a, T: ToOffset>(
&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(
&self,
@ -1976,15 +1980,6 @@ impl Editor {
.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 {
self.mode
}
@ -5443,6 +5438,10 @@ impl Editor {
}
pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
if self.read_only(cx) {
return;
}
self.transact(cx, |this, cx| {
if let Some(item) = cx.read_from_clipboard() {
let clipboard_text = Cow::Borrowed(item.text());
@ -5515,6 +5514,10 @@ impl Editor {
}
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((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
self.change_selections(None, cx, |s| {
@ -5529,6 +5532,10 @@ impl Editor {
}
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((_, 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 {
fn from(color: Rgba) -> Self {
let r = color.r;

View file

@ -978,12 +978,31 @@ impl Interactivity {
f: impl FnOnce(&Style, Point<Pixels>, &mut WindowContext),
) {
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 {
cx.with_z_index(z_index, |cx| paint_hover_group_handler(cx));
return;
}
let z_index = style.z_index.unwrap_or(0);
cx.with_z_index(z_index, |cx| {
style.paint(bounds, cx, |cx| {
cx.with_text_style(style.text_style().cloned(), |cx| {
@ -1166,21 +1185,7 @@ impl Interactivity {
})
}
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();
}
});
}
paint_hover_group_handler(cx);
if self.hover_style.is_some()
|| self.base_style.mouse_cursor.is_some()

View file

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

View file

@ -18,7 +18,7 @@ use smallvec::SmallVec;
use std::{ffi::c_void, mem, ptr, sync::Arc};
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 {
layer: metal::MetalLayer,
@ -204,7 +204,11 @@ impl MetalRenderer {
let command_buffer = command_queue.new_command_buffer();
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 color_attachment = render_pass_descriptor
@ -228,67 +232,67 @@ impl MetalRenderer {
zfar: 1.0,
});
for batch in scene.batches() {
match batch {
PrimitiveBatch::Shadows(shadows) => {
self.draw_shadows(
let ok = match batch {
PrimitiveBatch::Shadows(shadows) => self.draw_shadows(
shadows,
&mut instance_offset,
viewport_size,
command_encoder,
);
}
),
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(
PrimitiveBatch::Paths(paths) => self.draw_paths(
paths,
&path_tiles,
&mut instance_offset,
viewport_size,
command_encoder,
);
}
PrimitiveBatch::Underlines(underlines) => {
self.draw_underlines(
),
PrimitiveBatch::Underlines(underlines) => self.draw_underlines(
underlines,
&mut instance_offset,
viewport_size,
command_encoder,
);
}
),
PrimitiveBatch::MonochromeSprites {
texture_id,
sprites,
} => {
self.draw_monochrome_sprites(
} => self.draw_monochrome_sprites(
texture_id,
sprites,
&mut instance_offset,
viewport_size,
command_encoder,
);
}
),
PrimitiveBatch::PolychromeSprites {
texture_id,
sprites,
} => {
self.draw_polychrome_sprites(
} => self.draw_polychrome_sprites(
texture_id,
sprites,
&mut instance_offset,
viewport_size,
command_encoder,
);
}
PrimitiveBatch::Surfaces(surfaces) => {
self.draw_surfaces(
),
PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(
surfaces,
&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>],
offset: &mut usize,
command_buffer: &metal::CommandBufferRef,
) -> HashMap<PathId, AtlasTile> {
) -> Option<HashMap<PathId, AtlasTile>> {
let mut tiles = HashMap::default();
let mut vertices_by_texture_id = HashMap::default();
for path in paths {
@ -337,10 +341,9 @@ impl MetalRenderer {
for (texture_id, vertices) in vertices_by_texture_id {
align_offset(offset);
let next_offset = *offset + vertices.len() * mem::size_of::<PathVertex<ScaledPixels>>();
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
if next_offset > INSTANCE_BUFFER_SIZE {
return None;
}
let render_pass_descriptor = metal::RenderPassDescriptor::new();
let color_attachment = render_pass_descriptor
@ -389,7 +392,7 @@ impl MetalRenderer {
*offset = next_offset;
}
tiles
Some(tiles)
}
fn draw_shadows(
@ -398,9 +401,9 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
if shadows.is_empty() {
return;
return true;
}
align_offset(offset);
@ -429,6 +432,12 @@ impl MetalRenderer {
let shadow_bytes_len = std::mem::size_of_val(shadows);
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 {
ptr::copy_nonoverlapping(
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(
metal::MTLPrimitiveType::Triangle,
0,
@ -450,6 +453,7 @@ impl MetalRenderer {
shadows.len() as u64,
);
*offset = next_offset;
true
}
fn draw_quads(
@ -458,9 +462,9 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
if quads.is_empty() {
return;
return true;
}
align_offset(offset);
@ -489,16 +493,16 @@ impl MetalRenderer {
let quad_bytes_len = std::mem::size_of_val(quads);
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 {
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(
metal::MTLPrimitiveType::Triangle,
0,
@ -506,6 +510,7 @@ impl MetalRenderer {
quads.len() as u64,
);
*offset = next_offset;
true
}
fn draw_paths(
@ -515,9 +520,9 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
if paths.is_empty() {
return;
return true;
}
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));
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 =
unsafe { (self.instances.contents() as *mut u8).add(*offset) };
unsafe {
ptr::copy_nonoverlapping(
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(
metal::MTLPrimitiveType::Triangle,
0,
@ -613,6 +618,7 @@ impl MetalRenderer {
sprites.clear();
}
}
true
}
fn draw_underlines(
@ -621,9 +627,9 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
if underlines.is_empty() {
return;
return true;
}
align_offset(offset);
@ -661,10 +667,9 @@ impl MetalRenderer {
}
let next_offset = *offset + quad_bytes_len;
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::Triangle,
@ -673,6 +678,7 @@ impl MetalRenderer {
underlines.len() as u64,
);
*offset = next_offset;
true
}
fn draw_monochrome_sprites(
@ -682,9 +688,9 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
if sprites.is_empty() {
return;
return true;
}
align_offset(offset);
@ -723,6 +729,12 @@ impl MetalRenderer {
let sprite_bytes_len = std::mem::size_of_val(sprites);
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 {
ptr::copy_nonoverlapping(
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(
metal::MTLPrimitiveType::Triangle,
0,
@ -744,6 +750,7 @@ impl MetalRenderer {
sprites.len() as u64,
);
*offset = next_offset;
true
}
fn draw_polychrome_sprites(
@ -753,9 +760,9 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
if sprites.is_empty() {
return;
return true;
}
align_offset(offset);
@ -794,6 +801,12 @@ impl MetalRenderer {
let sprite_bytes_len = std::mem::size_of_val(sprites);
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 {
ptr::copy_nonoverlapping(
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(
metal::MTLPrimitiveType::Triangle,
0,
@ -815,6 +822,7 @@ impl MetalRenderer {
sprites.len() as u64,
);
*offset = next_offset;
true
}
fn draw_surfaces(
@ -823,7 +831,7 @@ impl MetalRenderer {
offset: &mut usize,
viewport_size: Size<DevicePixels>,
command_encoder: &metal::RenderCommandEncoderRef,
) {
) -> bool {
command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state);
command_encoder.set_vertex_buffer(
SurfaceInputIndex::Vertices as u64,
@ -874,10 +882,9 @@ impl MetalRenderer {
align_offset(offset);
let next_offset = *offset + mem::size_of::<Surface>();
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
if next_offset > INSTANCE_BUFFER_SIZE {
return false;
}
command_encoder.set_vertex_buffer(
SurfaceInputIndex::Surfaces as u64,
@ -913,6 +920,7 @@ impl MetalRenderer {
command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
*offset = next_offset;
}
true
}
}

View file

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

View file

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

View file

@ -1,7 +1,7 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position,
SharedString, StyleRefinement, Visibility, WhiteSpace,
DefiniteLength, Display, Fill, FlexDirection, FontWeight, Hsla, JustifyContent, Length,
Position, SharedString, StyleRefinement, Visibility, WhiteSpace,
};
use crate::{BoxShadow, TextStyleRefinement};
use smallvec::{smallvec, SmallVec};
@ -494,6 +494,13 @@ pub trait Styled: Sized {
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 {
self.text_style()
.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 {
fn to_taffy(&self, rem_size: Pixels) -> taffy::prelude::LengthPercentageAuto {
match self {

View file

@ -59,7 +59,7 @@ impl TextSystem {
fallback_font_stack: smallvec![
// TODO: This is currently Zed-specific.
// We should allow GPUI users to provide their own fallback font stack.
font("Zed Mono"),
font("Zed Sans"),
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).
///
/// 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_macro2::Ident;
use quote::{format_ident, quote};

View file

@ -99,20 +99,6 @@ pub trait Item {
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 {
worktrees: Vec<WorktreeHandle>,
active_entry: Option<ProjectEntryId>,

View file

@ -1,6 +1,6 @@
pub mod file_associations;
mod project_panel_settings;
use settings::{Settings, SettingsStore};
use settings::Settings;
use db::kvp::KEY_VALUE_STORE;
use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor};
@ -221,10 +221,10 @@ impl ProjectPanel {
})
.detach();
// cx.observe_global::<FileAssociations, _>(|_, cx| {
// cx.notify();
// })
// .detach();
cx.observe_global::<FileAssociations>(|_, cx| {
cx.notify();
})
.detach();
let mut this = Self {
project: project.clone(),
@ -246,18 +246,6 @@ impl ProjectPanel {
};
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
});
@ -292,16 +280,16 @@ impl ProjectPanel {
}
&Event::SplitEntry { entry_id } => {
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) {
// workspace
// .split_path(
// ProjectPath {
// worktree_id: worktree.read(cx).id(),
// path: entry.path.clone(),
// },
// cx,
// )
// .detach_and_log_err(cx);
if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
workspace
.split_path(
ProjectPath {
worktree_id: worktree.read(cx).id(),
path: entry.path.clone(),
},
cx,
)
.detach_and_log_err(cx);
}
}
}
@ -788,10 +776,6 @@ impl ProjectPanel {
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),
}));
// 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
},
})
@ -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
.iter()
.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
/// for e.g. mentions.
#[derive(Debug)]
pub struct Mention {
pub range: Range<usize>,
pub is_self_mention: bool,
@ -85,31 +86,6 @@ impl RichText {
})
.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(
@ -138,8 +114,10 @@ pub fn render_markdown_mut(
if let Some(language) = &current_language {
render_code(text, highlights, t.as_ref(), language);
} else {
if let Some(mention) = mentions.first() {
if source_range.contains_inclusive(&mention.range) {
while let Some(mention) = mentions.first() {
if !source_range.contains_inclusive(&mention.range) {
break;
}
mentions = &mentions[1..];
let range = (prev_len + mention.range.start - source_range.start)
..(prev_len + mention.range.end - source_range.start);
@ -152,7 +130,6 @@ pub fn render_markdown_mut(
},
));
}
}
text.push_str(t.as_ref());
let mut style = HighlightStyle::default();
@ -272,13 +249,6 @@ pub fn render_markdown(
language_registry: &Arc<LanguageRegistry>,
language: Option<&Arc<Language>>,
) -> 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 highlights = Vec::new();
let mut link_ranges = Vec::new();

View file

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

View file

@ -1648,7 +1648,6 @@ mod tests {
#[gpui::test]
async fn test_search_query_history(cx: &mut TestAppContext) {
//crate::project_search::tests::init_test(cx);
init_globals(cx);
let buffer_text = r#"
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]
fn test_subtract_ranges() {
// collapsed_ranges: Vec<Range<usize>>, keep_ranges: Vec<Range<usize>>
assert_eq!(
subtract_ranges(&[0..5, 10..21], &[0..1, 4..5]),
vec![1..4, 10..21]

View file

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

View file

@ -11,7 +11,7 @@ use itertools::Itertools;
use project::{Fs, ProjectEntryId};
use search::{buffer_search::DivRegistrar, BufferSearchBar};
use serde::{Deserialize, Serialize};
use settings::{Settings, SettingsStore};
use settings::Settings;
use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
use ui::{h_stack, ButtonCommon, Clickable, IconButton, IconSize, Selectable, Tooltip};
use util::{ResultExt, TryFutureExt};
@ -159,15 +159,6 @@ impl TerminalPanel {
height: None,
_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
}

View file

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

View file

@ -8,6 +8,11 @@ pub(crate) fn neutral() -> ColorScaleSet {
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 {
pub fn light() -> Self {
let system = SystemColors::default();
@ -23,12 +28,12 @@ impl ThemeColors {
surface_background: neutral().light().step_2(),
background: neutral().light().step_1(),
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_selected: neutral().light_alpha().step_5(),
element_disabled: neutral().light_alpha().step_3(), // todo!("pick the right colors")
drop_target_background: blue().light_alpha().step_2(), // todo!("pick the right colors")
ghost_element_background: system.transparent, // todo!("pick the right colors")
element_disabled: neutral().light_alpha().step_3(),
drop_target_background: blue().light_alpha().step_2(),
ghost_element_background: system.transparent,
ghost_element_hover: neutral().light_alpha().step_3(),
ghost_element_active: neutral().light_alpha().step_4(),
ghost_element_selected: neutral().light_alpha().step_5(),
@ -59,7 +64,7 @@ impl ThemeColors {
scrollbar_track_background: gpui::transparent_black(),
scrollbar_track_border: neutral().light().step_5(),
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_subheader_background: neutral().light().step_2(),
editor_active_line_background: neutral().light_alpha().step_3(),
@ -106,17 +111,17 @@ impl ThemeColors {
surface_background: neutral().dark().step_2(),
background: neutral().dark().step_1(),
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_selected: neutral().dark_alpha().step_5(), // todo!("pick the right colors")
element_disabled: neutral().dark_alpha().step_3(), // todo!("pick the right colors")
element_selected: neutral().dark_alpha().step_5(),
element_disabled: neutral().dark_alpha().step_3(),
drop_target_background: blue().dark_alpha().step_2(),
ghost_element_background: system.transparent,
ghost_element_hover: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
ghost_element_active: neutral().dark_alpha().step_5(), // todo!("pick the right colors")
ghost_element_hover: neutral().dark_alpha().step_4(),
ghost_element_active: neutral().dark_alpha().step_5(),
ghost_element_selected: neutral().dark_alpha().step_5(),
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_placeholder: neutral().dark().step_10(),
text_disabled: neutral().dark().step_9(),
@ -140,7 +145,7 @@ impl ThemeColors {
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
scrollbar_thumb_border: 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_background: neutral().dark().step_1(),
editor_gutter_background: neutral().dark().step_1(),

View file

@ -7,6 +7,10 @@ use crate::{
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 {
ThemeFamily {
id: "one".to_string(),
@ -75,7 +79,7 @@ pub(crate) fn one_dark() -> Theme {
tab_bar_background: bg,
tab_inactive_background: bg,
tab_active_background: editor,
search_match_background: bg, // todo!(this was inserted by Mikayla)
search_match_background: bg,
editor_background: editor,
editor_gutter_background: editor,

View file

@ -26,6 +26,7 @@ pub enum AvatarShape {
#[derive(IntoElement)]
pub struct Avatar {
image: Img,
size: Option<Pixels>,
border_color: Option<Hsla>,
is_available: Option<bool>,
}
@ -36,7 +37,7 @@ impl RenderOnce for Avatar {
self = self.shape(AvatarShape::Circle);
}
let size = cx.rem_size();
let size = self.size.unwrap_or_else(|| cx.rem_size());
div()
.size(size + px(2.))
@ -78,6 +79,7 @@ impl Avatar {
image: img(src),
is_available: None,
border_color: None,
size: None,
}
}
@ -124,4 +126,10 @@ impl Avatar {
self.is_available = is_available.into();
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)])
.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::Placeholder => cx.theme().colors().text_placeholder,
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::Selected => cx.theme().colors().text_accent,
Color::Success => cx.theme().status().success,

View file

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

View file

@ -809,27 +809,6 @@ pub mod test {
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 {
pub fn new(id: u64, path: &str, cx: &mut AppContext) -> Model<Self> {
let entry_id = Some(ProjectEntryId::from_proto(id));

View file

@ -60,24 +60,6 @@ pub enum SaveIntent {
#[derive(Clone, Deserialize, PartialEq, Debug)]
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)]
#[serde(rename_all = "camelCase")]
pub struct CloseActiveItem {
@ -1226,125 +1208,6 @@ impl Pane {
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> {
&self.toolbar
}

View file

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

View file

@ -3324,36 +3324,6 @@ impl 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>(
&mut self,
callback: impl Fn(&mut Self, &A, &mut ViewContext<Self>) + 'static,

View file

@ -184,7 +184,7 @@ mod tests {
#[gpui::test]
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 =
crate::languages::language("python", tree_sitter_python::language(), None).await;
cx.update(|cx| {

View file

@ -113,12 +113,6 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
})
.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 diagnostic_summary =
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