Merge branch 'main' into vim2
This commit is contained in:
commit
b66b4915cf
126 changed files with 15861 additions and 11529 deletions
28
Cargo.lock
generated
28
Cargo.lock
generated
|
@ -1154,6 +1154,7 @@ dependencies = [
|
|||
"gpui2",
|
||||
"itertools 0.10.5",
|
||||
"language2",
|
||||
"outline2",
|
||||
"project2",
|
||||
"search2",
|
||||
"settings2",
|
||||
|
@ -1387,11 +1388,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
version = "1.0.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -1753,7 +1753,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.30.0"
|
||||
version = "0.30.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -4670,15 +4670,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "journal"
|
||||
version = "0.1.0"
|
||||
|
@ -5054,18 +5045,18 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
|||
|
||||
[[package]]
|
||||
name = "linkme"
|
||||
version = "0.3.18"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1e6b0bb9ca88d3c5ae88240beb9683821f903b824ee8381ef9ab4e8522fbfa9"
|
||||
checksum = "91ed2ee9464ff9707af8e9ad834cffa4802f072caad90639c583dd3c62e6e608"
|
||||
dependencies = [
|
||||
"linkme-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linkme-impl"
|
||||
version = "0.3.18"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3b3f61e557a617ec6ba36c79431e1f3b5e100d67cfbdb61ed6ef384298af016"
|
||||
checksum = "ba125974b109d512fccbc6c0244e7580143e460895dfd6ea7f8bbb692fd94396"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -8299,6 +8290,7 @@ dependencies = [
|
|||
"menu2",
|
||||
"postage",
|
||||
"project2",
|
||||
"semantic_index2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -10511,7 +10503,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "tree-sitter-vue"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/zed-industries/tree-sitter-vue?rev=9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58#9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58"
|
||||
source = "git+https://github.com/zed-industries/tree-sitter-vue?rev=6608d9d60c386f19d80af7d8132322fa11199c42#6608d9d60c386f19d80af7d8132322fa11199c42"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"tree-sitter",
|
||||
|
|
|
@ -205,7 +205,7 @@ tree-sitter-yaml = { git = "https://github.com/zed-industries/tree-sitter-yaml",
|
|||
tree-sitter-lua = "0.0.14"
|
||||
tree-sitter-nix = { git = "https://github.com/nix-community/tree-sitter-nix", rev = "66e3e9ce9180ae08fc57372061006ef83f0abde7" }
|
||||
tree-sitter-nu = { git = "https://github.com/nushell/tree-sitter-nu", rev = "786689b0562b9799ce53e824cb45a1a2a04dc673"}
|
||||
tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "9b6cb221ccb8d0b956fcb17e9a1efac2feefeb58"}
|
||||
tree-sitter-vue = {git = "https://github.com/zed-industries/tree-sitter-vue", rev = "6608d9d60c386f19d80af7d8132322fa11199c42"}
|
||||
tree-sitter-uiua = {git = "https://github.com/shnarazk/tree-sitter-uiua", rev = "9260f11be5900beda4ee6d1a24ab8ddfaf5a19b2"}
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@ use ui::h_stack;
|
|||
use util::ResultExt;
|
||||
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
||||
|
||||
actions!(ShowErrorMessage);
|
||||
actions!(activity_indicator, [ShowErrorMessage]);
|
||||
|
||||
const DOWNLOAD_ICON: &str = "icons/download.svg";
|
||||
const WARNING_ICON: &str = "icons/warning.svg";
|
||||
|
|
|
@ -12,23 +12,26 @@ use chrono::{DateTime, Local};
|
|||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
use futures::StreamExt;
|
||||
use gpui::{actions, AppContext};
|
||||
use gpui::{actions, AppContext, SharedString};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cmp::Reverse, ffi::OsStr, path::PathBuf, sync::Arc};
|
||||
use util::paths::CONVERSATIONS_DIR;
|
||||
|
||||
actions!(
|
||||
NewConversation,
|
||||
Assist,
|
||||
Split,
|
||||
CycleMessageRole,
|
||||
QuoteSelection,
|
||||
ToggleFocus,
|
||||
ResetKey,
|
||||
InlineAssist,
|
||||
ToggleIncludeConversation,
|
||||
ToggleRetrieveContext,
|
||||
assistant,
|
||||
[
|
||||
NewConversation,
|
||||
Assist,
|
||||
Split,
|
||||
CycleMessageRole,
|
||||
QuoteSelection,
|
||||
ToggleFocus,
|
||||
ResetKey,
|
||||
InlineAssist,
|
||||
ToggleIncludeConversation,
|
||||
ToggleRetrieveContext,
|
||||
]
|
||||
);
|
||||
|
||||
#[derive(
|
||||
|
@ -47,7 +50,7 @@ struct MessageMetadata {
|
|||
enum MessageStatus {
|
||||
Pending,
|
||||
Done,
|
||||
Error(Arc<str>),
|
||||
Error(SharedString),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
|
|
@ -1628,8 +1628,9 @@ impl Conversation {
|
|||
metadata.status = MessageStatus::Done;
|
||||
}
|
||||
Err(error) => {
|
||||
metadata.status =
|
||||
MessageStatus::Error(error.to_string().trim().into());
|
||||
metadata.status = MessageStatus::Error(SharedString::from(
|
||||
error.to_string().trim().to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
cx.notify();
|
||||
|
@ -2273,7 +2274,7 @@ impl ConversationEditor {
|
|||
Some(
|
||||
div()
|
||||
.id("error")
|
||||
.tooltip(move |cx| Tooltip::text(&error, cx))
|
||||
.tooltip(move |cx| Tooltip::text(error.clone(), cx))
|
||||
.child(IconElement::new(Icon::XCircle)),
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -26,10 +26,13 @@ const POLL_INTERVAL: Duration = Duration::from_secs(60 * 60);
|
|||
|
||||
//todo!(remove CheckThatAutoUpdaterWorks)
|
||||
actions!(
|
||||
Check,
|
||||
DismissErrorMessage,
|
||||
ViewReleaseNotes,
|
||||
CheckThatAutoUpdaterWorks
|
||||
auto_update,
|
||||
[
|
||||
Check,
|
||||
DismissErrorMessage,
|
||||
ViewReleaseNotes,
|
||||
CheckThatAutoUpdaterWorks
|
||||
]
|
||||
);
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
@ -19,7 +19,7 @@ search = { package = "search2", path = "../search2" }
|
|||
settings = { package = "settings2", path = "../settings2" }
|
||||
theme = { package = "theme2", path = "../theme2" }
|
||||
workspace = { package = "workspace2", path = "../workspace2" }
|
||||
# outline = { path = "../outline" }
|
||||
outline = { package = "outline2", path = "../outline2" }
|
||||
itertools = "0.10"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use editor::Editor;
|
||||
use gpui::{
|
||||
Div, Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription,
|
||||
ViewContext, WeakView,
|
||||
ViewContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{prelude::*, ButtonLike, ButtonStyle, Label};
|
||||
use ui::{prelude::*, ButtonLike, ButtonStyle, Label, Tooltip};
|
||||
use workspace::{
|
||||
item::{ItemEvent, ItemHandle},
|
||||
ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||
ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
|
||||
};
|
||||
|
||||
pub enum Event {
|
||||
|
@ -18,16 +19,14 @@ pub struct Breadcrumbs {
|
|||
pane_focused: bool,
|
||||
active_item: Option<Box<dyn ItemHandle>>,
|
||||
subscription: Option<Subscription>,
|
||||
_workspace: WeakView<Workspace>,
|
||||
}
|
||||
|
||||
impl Breadcrumbs {
|
||||
pub fn new(workspace: &Workspace) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pane_focused: false,
|
||||
active_item: Default::default(),
|
||||
subscription: Default::default(),
|
||||
_workspace: workspace.weak_handle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,97 +61,24 @@ impl Render for Breadcrumbs {
|
|||
Label::new("›").into_any_element()
|
||||
});
|
||||
|
||||
let editor = active_item
|
||||
.downcast::<Editor>()
|
||||
.map(|editor| editor.downgrade());
|
||||
|
||||
element.child(
|
||||
ButtonLike::new("toggle outline view")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.child(h_stack().gap_1().children(breadcrumbs))
|
||||
// We disable the button when it is not focused
|
||||
// due to ... @julia what was the reason again?
|
||||
.disabled(!self.pane_focused)
|
||||
.on_click(move |_, _cx| {
|
||||
todo!("outline::toggle");
|
||||
// this.update(cx, |this, cx| {
|
||||
// if let Some(workspace) = this.workspace.upgrade() {
|
||||
// workspace.update(cx, |_workspace, _cx| {
|
||||
// outline::toggle(workspace, &Default::default(), cx)
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// .ok();
|
||||
}),
|
||||
.on_click(move |_, cx| {
|
||||
if let Some(editor) = editor.as_ref().and_then(|editor| editor.upgrade()) {
|
||||
outline::toggle(editor, &outline::Toggle, cx)
|
||||
}
|
||||
})
|
||||
.tooltip(|cx| Tooltip::for_action("Show symbol outline", &outline::Toggle, cx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// impl View for Breadcrumbs {
|
||||
// fn ui_name() -> &'static str {
|
||||
// "Breadcrumbs"
|
||||
// }
|
||||
|
||||
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
// let active_item = match &self.active_item {
|
||||
// Some(active_item) => active_item,
|
||||
// None => return Empty::new().into_any(),
|
||||
// };
|
||||
// let not_editor = active_item.downcast::<editor::Editor>().is_none();
|
||||
|
||||
// let theme = theme::current(cx).clone();
|
||||
// let style = &theme.workspace.toolbar.breadcrumbs;
|
||||
|
||||
// let breadcrumbs = match active_item.breadcrumbs(&theme, cx) {
|
||||
// Some(breadcrumbs) => breadcrumbs,
|
||||
// None => return Empty::new().into_any(),
|
||||
// }
|
||||
// .into_iter()
|
||||
// .map(|breadcrumb| {
|
||||
// Text::new(
|
||||
// breadcrumb.text,
|
||||
// theme.workspace.toolbar.breadcrumbs.default.text.clone(),
|
||||
// )
|
||||
// .with_highlights(breadcrumb.highlights.unwrap_or_default())
|
||||
// .into_any()
|
||||
// });
|
||||
|
||||
// let crumbs = Flex::row()
|
||||
// .with_children(Itertools::intersperse_with(breadcrumbs, || {
|
||||
// Label::new(" › ", style.default.text.clone()).into_any()
|
||||
// }))
|
||||
// .constrained()
|
||||
// .with_height(theme.workspace.toolbar.breadcrumb_height)
|
||||
// .contained();
|
||||
|
||||
// if not_editor || !self.pane_focused {
|
||||
// return crumbs
|
||||
// .with_style(style.default.container)
|
||||
// .aligned()
|
||||
// .left()
|
||||
// .into_any();
|
||||
// }
|
||||
|
||||
// MouseEventHandler::new::<Breadcrumbs, _>(0, cx, |state, _| {
|
||||
// let style = style.style_for(state);
|
||||
// crumbs.with_style(style.container)
|
||||
// })
|
||||
// .on_click(MouseButton::Left, |_, this, cx| {
|
||||
// if let Some(workspace) = this.workspace.upgrade(cx) {
|
||||
// workspace.update(cx, |workspace, cx| {
|
||||
// outline::toggle(workspace, &Default::default(), cx)
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// .with_tooltip::<Breadcrumbs>(
|
||||
// 0,
|
||||
// "Show symbol outline".to_owned(),
|
||||
// Some(Box::new(outline::Toggle)),
|
||||
// theme.tooltip.clone(),
|
||||
// cx,
|
||||
// )
|
||||
// .aligned()
|
||||
// .left()
|
||||
// .into_any()
|
||||
// }
|
||||
// }
|
||||
|
||||
impl ToolbarItemView for Breadcrumbs {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
|
|
|
@ -8,7 +8,8 @@ use collections::{hash_map, HashMap, HashSet};
|
|||
use db::RELEASE_CHANNEL;
|
||||
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
|
||||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, SharedString, Task,
|
||||
WeakModel,
|
||||
};
|
||||
use rpc::{
|
||||
proto::{self, ChannelVisibility},
|
||||
|
@ -46,7 +47,7 @@ pub struct ChannelStore {
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Channel {
|
||||
pub id: ChannelId,
|
||||
pub name: String,
|
||||
pub name: SharedString,
|
||||
pub visibility: proto::ChannelVisibility,
|
||||
pub role: proto::ChannelRole,
|
||||
pub unseen_note_version: Option<(u64, clock::Global)>,
|
||||
|
@ -895,14 +896,16 @@ impl ChannelStore {
|
|||
.channel_invitations
|
||||
.binary_search_by_key(&channel.id, |c| c.id)
|
||||
{
|
||||
Ok(ix) => Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name,
|
||||
Ok(ix) => {
|
||||
Arc::make_mut(&mut self.channel_invitations[ix]).name = channel.name.into()
|
||||
}
|
||||
Err(ix) => self.channel_invitations.insert(
|
||||
ix,
|
||||
Arc::new(Channel {
|
||||
id: channel.id,
|
||||
visibility: channel.visibility(),
|
||||
role: channel.role(),
|
||||
name: channel.name,
|
||||
name: channel.name.into(),
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
parent_path: channel.parent_path,
|
||||
|
|
|
@ -104,7 +104,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
|
||||
existing_channel.visibility = channel_proto.visibility();
|
||||
existing_channel.role = channel_proto.role();
|
||||
existing_channel.name = channel_proto.name;
|
||||
existing_channel.name = channel_proto.name.into();
|
||||
} else {
|
||||
self.channels_by_id.insert(
|
||||
channel_proto.id,
|
||||
|
@ -112,7 +112,7 @@ impl<'a> ChannelPathsInsertGuard<'a> {
|
|||
id: channel_proto.id,
|
||||
visibility: channel_proto.visibility(),
|
||||
role: channel_proto.role(),
|
||||
name: channel_proto.name,
|
||||
name: channel_proto.name.into(),
|
||||
unseen_note_version: None,
|
||||
unseen_message_id: None,
|
||||
parent_path: channel_proto.parent_path,
|
||||
|
@ -146,11 +146,11 @@ fn channel_path_sorting_key<'a>(
|
|||
let (parent_path, name) = channels_by_id
|
||||
.get(&id)
|
||||
.map_or((&[] as &[_], None), |channel| {
|
||||
(channel.parent_path.as_slice(), Some(channel.name.as_str()))
|
||||
(channel.parent_path.as_slice(), Some(channel.name.as_ref()))
|
||||
});
|
||||
parent_path
|
||||
.iter()
|
||||
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_str()))
|
||||
.filter_map(|id| Some(channels_by_id.get(id)?.name.as_ref()))
|
||||
.chain(name)
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894";
|
|||
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
|
||||
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
|
||||
actions!(SignIn, SignOut, Reconnect);
|
||||
actions!(client, [SignIn, SignOut, Reconnect]);
|
||||
|
||||
pub fn init_settings(cx: &mut AppContext) {
|
||||
TelemetrySettings::register(cx);
|
||||
|
|
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
|
|||
default-run = "collab"
|
||||
edition = "2021"
|
||||
name = "collab"
|
||||
version = "0.30.0"
|
||||
version = "0.30.1"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -116,12 +116,13 @@ struct CreateUserResponse {
|
|||
#[derive(Debug, Deserialize)]
|
||||
struct Panic {
|
||||
version: String,
|
||||
release_channel: String,
|
||||
text: String,
|
||||
}
|
||||
|
||||
#[instrument(skip(panic))]
|
||||
async fn trace_panic(panic: Json<Panic>) -> Result<()> {
|
||||
tracing::error!(version = %panic.version, text = %panic.text, "panic report");
|
||||
tracing::error!(version = %panic.version, release_channel = %panic.release_channel, text = %panic.text, "panic report");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use call::ActiveCall;
|
|||
use channel::{ChannelId, ChannelMembership, ChannelStore};
|
||||
use client::User;
|
||||
use futures::future::try_join_all;
|
||||
use gpui::{BackgroundExecutor, Model, TestAppContext};
|
||||
use gpui::{BackgroundExecutor, Model, SharedString, TestAppContext};
|
||||
use rpc::{
|
||||
proto::{self, ChannelRole},
|
||||
RECEIVE_TIMEOUT,
|
||||
|
@ -46,13 +46,13 @@ async fn test_core_channels(
|
|||
&[
|
||||
ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
ExpectedChannel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
name: "channel-b".into(),
|
||||
depth: 1,
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
|
@ -92,7 +92,7 @@ async fn test_core_channels(
|
|||
cx_b,
|
||||
&[ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Member,
|
||||
}],
|
||||
|
@ -140,13 +140,13 @@ async fn test_core_channels(
|
|||
&[
|
||||
ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
role: ChannelRole::Member,
|
||||
depth: 0,
|
||||
},
|
||||
ExpectedChannel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
name: "channel-b".into(),
|
||||
role: ChannelRole::Member,
|
||||
depth: 1,
|
||||
},
|
||||
|
@ -168,19 +168,19 @@ async fn test_core_channels(
|
|||
&[
|
||||
ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
role: ChannelRole::Member,
|
||||
depth: 0,
|
||||
},
|
||||
ExpectedChannel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
name: "channel-b".into(),
|
||||
role: ChannelRole::Member,
|
||||
depth: 1,
|
||||
},
|
||||
ExpectedChannel {
|
||||
id: channel_c_id,
|
||||
name: "channel-c".to_string(),
|
||||
name: "channel-c".into(),
|
||||
role: ChannelRole::Member,
|
||||
depth: 2,
|
||||
},
|
||||
|
@ -211,19 +211,19 @@ async fn test_core_channels(
|
|||
&[
|
||||
ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
ExpectedChannel {
|
||||
id: channel_b_id,
|
||||
name: "channel-b".to_string(),
|
||||
name: "channel-b".into(),
|
||||
depth: 1,
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
ExpectedChannel {
|
||||
id: channel_c_id,
|
||||
name: "channel-c".to_string(),
|
||||
name: "channel-c".into(),
|
||||
depth: 2,
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
|
@ -245,7 +245,7 @@ async fn test_core_channels(
|
|||
cx_a,
|
||||
&[ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Admin,
|
||||
}],
|
||||
|
@ -255,7 +255,7 @@ async fn test_core_channels(
|
|||
cx_b,
|
||||
&[ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Admin,
|
||||
}],
|
||||
|
@ -278,7 +278,7 @@ async fn test_core_channels(
|
|||
cx_a,
|
||||
&[ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a".to_string(),
|
||||
name: "channel-a".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Admin,
|
||||
}],
|
||||
|
@ -309,7 +309,7 @@ async fn test_core_channels(
|
|||
cx_a,
|
||||
&[ExpectedChannel {
|
||||
id: channel_a_id,
|
||||
name: "channel-a-renamed".to_string(),
|
||||
name: "channel-a-renamed".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Admin,
|
||||
}],
|
||||
|
@ -418,7 +418,7 @@ async fn test_channel_room(
|
|||
cx_b,
|
||||
&[ExpectedChannel {
|
||||
id: zed_id,
|
||||
name: "zed".to_string(),
|
||||
name: "zed".into(),
|
||||
depth: 0,
|
||||
role: ChannelRole::Member,
|
||||
}],
|
||||
|
@ -680,7 +680,7 @@ async fn test_permissions_update_while_invited(
|
|||
&[ExpectedChannel {
|
||||
depth: 0,
|
||||
id: rust_id,
|
||||
name: "rust".to_string(),
|
||||
name: "rust".into(),
|
||||
role: ChannelRole::Member,
|
||||
}],
|
||||
);
|
||||
|
@ -708,7 +708,7 @@ async fn test_permissions_update_while_invited(
|
|||
&[ExpectedChannel {
|
||||
depth: 0,
|
||||
id: rust_id,
|
||||
name: "rust".to_string(),
|
||||
name: "rust".into(),
|
||||
role: ChannelRole::Member,
|
||||
}],
|
||||
);
|
||||
|
@ -747,7 +747,7 @@ async fn test_channel_rename(
|
|||
&[ExpectedChannel {
|
||||
depth: 0,
|
||||
id: rust_id,
|
||||
name: "rust-archive".to_string(),
|
||||
name: "rust-archive".into(),
|
||||
role: ChannelRole::Admin,
|
||||
}],
|
||||
);
|
||||
|
@ -759,7 +759,7 @@ async fn test_channel_rename(
|
|||
&[ExpectedChannel {
|
||||
depth: 0,
|
||||
id: rust_id,
|
||||
name: "rust-archive".to_string(),
|
||||
name: "rust-archive".into(),
|
||||
role: ChannelRole::Member,
|
||||
}],
|
||||
);
|
||||
|
@ -888,7 +888,7 @@ async fn test_lost_channel_creation(
|
|||
&[ExpectedChannel {
|
||||
depth: 0,
|
||||
id: channel_id,
|
||||
name: "x".to_string(),
|
||||
name: "x".into(),
|
||||
role: ChannelRole::Member,
|
||||
}],
|
||||
);
|
||||
|
@ -912,13 +912,13 @@ async fn test_lost_channel_creation(
|
|||
ExpectedChannel {
|
||||
depth: 0,
|
||||
id: channel_id,
|
||||
name: "x".to_string(),
|
||||
name: "x".into(),
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
ExpectedChannel {
|
||||
depth: 1,
|
||||
id: subchannel_id,
|
||||
name: "subchannel".to_string(),
|
||||
name: "subchannel".into(),
|
||||
role: ChannelRole::Admin,
|
||||
},
|
||||
],
|
||||
|
@ -943,13 +943,13 @@ async fn test_lost_channel_creation(
|
|||
ExpectedChannel {
|
||||
depth: 0,
|
||||
id: channel_id,
|
||||
name: "x".to_string(),
|
||||
name: "x".into(),
|
||||
role: ChannelRole::Member,
|
||||
},
|
||||
ExpectedChannel {
|
||||
depth: 1,
|
||||
id: subchannel_id,
|
||||
name: "subchannel".to_string(),
|
||||
name: "subchannel".into(),
|
||||
role: ChannelRole::Member,
|
||||
},
|
||||
],
|
||||
|
@ -1221,13 +1221,13 @@ async fn test_channel_membership_notifications(
|
|||
ExpectedChannel {
|
||||
depth: 0,
|
||||
id: zed_channel,
|
||||
name: "zed".to_string(),
|
||||
name: "zed".into(),
|
||||
role: ChannelRole::Guest,
|
||||
},
|
||||
ExpectedChannel {
|
||||
depth: 1,
|
||||
id: vim_channel,
|
||||
name: "vim".to_string(),
|
||||
name: "vim".into(),
|
||||
role: ChannelRole::Member,
|
||||
},
|
||||
],
|
||||
|
@ -1250,13 +1250,13 @@ async fn test_channel_membership_notifications(
|
|||
ExpectedChannel {
|
||||
depth: 0,
|
||||
id: zed_channel,
|
||||
name: "zed".to_string(),
|
||||
name: "zed".into(),
|
||||
role: ChannelRole::Guest,
|
||||
},
|
||||
ExpectedChannel {
|
||||
depth: 1,
|
||||
id: vim_channel,
|
||||
name: "vim".to_string(),
|
||||
name: "vim".into(),
|
||||
role: ChannelRole::Guest,
|
||||
},
|
||||
],
|
||||
|
@ -1476,7 +1476,7 @@ async fn test_channel_moving(
|
|||
struct ExpectedChannel {
|
||||
depth: usize,
|
||||
id: ChannelId,
|
||||
name: String,
|
||||
name: SharedString,
|
||||
role: ChannelRole,
|
||||
}
|
||||
|
||||
|
@ -1515,7 +1515,7 @@ fn assert_channels(
|
|||
.ordered_channels()
|
||||
.map(|(depth, channel)| ExpectedChannel {
|
||||
depth,
|
||||
name: channel.name.clone(),
|
||||
name: channel.name.clone().into(),
|
||||
id: channel.id,
|
||||
role: channel.role,
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::db::ChannelRole;
|
|||
use super::{run_randomized_test, RandomizedTest, TestClient, TestError, TestServer, UserTestPlan};
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use gpui::{BackgroundExecutor, TestAppContext};
|
||||
use gpui::{BackgroundExecutor, SharedString, TestAppContext};
|
||||
use rand::prelude::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{
|
||||
|
@ -30,13 +30,13 @@ struct RandomChannelBufferTest;
|
|||
#[derive(Clone, Serialize, Deserialize)]
|
||||
enum ChannelBufferOperation {
|
||||
JoinChannelNotes {
|
||||
channel_name: String,
|
||||
channel_name: SharedString,
|
||||
},
|
||||
LeaveChannelNotes {
|
||||
channel_name: String,
|
||||
channel_name: SharedString,
|
||||
},
|
||||
EditChannelNotes {
|
||||
channel_name: String,
|
||||
channel_name: SharedString,
|
||||
edits: Vec<(Range<usize>, Arc<str>)>,
|
||||
},
|
||||
Noop,
|
||||
|
|
|
@ -26,7 +26,7 @@ use workspace::{
|
|||
ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId,
|
||||
};
|
||||
|
||||
actions!(Deploy);
|
||||
actions!(collab, [Deploy]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
register_followable_item::<ChannelView>(cx)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,13 +3,14 @@ use client::UserId;
|
|||
use collections::HashMap;
|
||||
use editor::{AnchorRangeExt, Editor};
|
||||
use gpui::{
|
||||
elements::ChildView, AnyElement, AsyncAppContext, Element, Entity, ModelHandle, Task, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle,
|
||||
AnyView, AsyncWindowContext, FocusableView, Model, Render, SharedString, Task, View,
|
||||
ViewContext, WeakView,
|
||||
};
|
||||
use language::{language_settings::SoftWrap, Buffer, BufferSnapshot, LanguageRegistry};
|
||||
use lazy_static::lazy_static;
|
||||
use project::search::SearchQuery;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use workspace::item::ItemHandle;
|
||||
|
||||
const MENTIONS_DEBOUNCE_INTERVAL: Duration = Duration::from_millis(50);
|
||||
|
||||
|
@ -19,8 +20,8 @@ lazy_static! {
|
|||
}
|
||||
|
||||
pub struct MessageEditor {
|
||||
pub editor: ViewHandle<Editor>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
pub editor: View<Editor>,
|
||||
channel_store: Model<ChannelStore>,
|
||||
users: HashMap<String, UserId>,
|
||||
mentions: Vec<UserId>,
|
||||
mentions_task: Option<Task<()>>,
|
||||
|
@ -30,8 +31,8 @@ pub struct MessageEditor {
|
|||
impl MessageEditor {
|
||||
pub fn new(
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
channel_store: ModelHandle<ChannelStore>,
|
||||
editor: ViewHandle<Editor>,
|
||||
channel_store: Model<ChannelStore>,
|
||||
editor: View<Editor>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Self {
|
||||
editor.update(cx, |editor, cx| {
|
||||
|
@ -48,15 +49,13 @@ impl MessageEditor {
|
|||
cx.subscribe(&buffer, Self::on_buffer_event).detach();
|
||||
|
||||
let markdown = language_registry.language_for_name("Markdown");
|
||||
cx.app_context()
|
||||
.spawn(|mut cx| async move {
|
||||
let markdown = markdown.await?;
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
buffer.set_language(Some(markdown), cx)
|
||||
});
|
||||
anyhow::Ok(())
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let markdown = markdown.await?;
|
||||
buffer.update(&mut cx, |buffer, cx| {
|
||||
buffer.set_language(Some(markdown), cx)
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
|
||||
Self {
|
||||
editor,
|
||||
|
@ -71,7 +70,7 @@ impl MessageEditor {
|
|||
pub fn set_channel(
|
||||
&mut self,
|
||||
channel_id: u64,
|
||||
channel_name: Option<String>,
|
||||
channel_name: Option<SharedString>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
|
@ -132,26 +131,28 @@ impl MessageEditor {
|
|||
|
||||
fn on_buffer_event(
|
||||
&mut self,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
buffer: Model<Buffer>,
|
||||
event: &language::Event,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if let language::Event::Reparsed | language::Event::Edited = event {
|
||||
let buffer = buffer.read(cx).snapshot();
|
||||
self.mentions_task = Some(cx.spawn(|this, cx| async move {
|
||||
cx.background().timer(MENTIONS_DEBOUNCE_INTERVAL).await;
|
||||
cx.background_executor()
|
||||
.timer(MENTIONS_DEBOUNCE_INTERVAL)
|
||||
.await;
|
||||
Self::find_mentions(this, buffer, cx).await;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_mentions(
|
||||
this: WeakViewHandle<MessageEditor>,
|
||||
this: WeakView<MessageEditor>,
|
||||
buffer: BufferSnapshot,
|
||||
mut cx: AsyncAppContext,
|
||||
mut cx: AsyncWindowContext,
|
||||
) {
|
||||
let (buffer, ranges) = cx
|
||||
.background()
|
||||
.background_executor()
|
||||
.spawn(async move {
|
||||
let ranges = MENTIONS_SEARCH.search(&buffer, None).await;
|
||||
(buffer, ranges)
|
||||
|
@ -180,11 +181,7 @@ impl MessageEditor {
|
|||
}
|
||||
|
||||
editor.clear_highlights::<Self>(cx);
|
||||
editor.highlight_text::<Self>(
|
||||
anchor_ranges,
|
||||
theme::current(cx).chat_panel.rich_text.mention_highlight,
|
||||
cx,
|
||||
)
|
||||
editor.highlight_text::<Self>(anchor_ranges, gpui::red().into(), cx)
|
||||
});
|
||||
|
||||
this.mentions = mentioned_user_ids;
|
||||
|
@ -192,21 +189,17 @@ impl MessageEditor {
|
|||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
impl Entity for MessageEditor {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for MessageEditor {
|
||||
fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self> {
|
||||
ChildView::new(&self.editor, cx).into_any()
|
||||
pub(crate) fn focus_handle(&self, cx: &gpui::AppContext) -> gpui::FocusHandle {
|
||||
self.editor.read(cx).focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||
if cx.is_self_focused() {
|
||||
cx.focus(&self.editor);
|
||||
}
|
||||
impl Render for MessageEditor {
|
||||
type Element = AnyView;
|
||||
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
self.editor.to_any()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,7 +207,7 @@ impl View for MessageEditor {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use client::{Client, User, UserStore};
|
||||
use gpui::{TestAppContext, WindowHandle};
|
||||
use gpui::{Context as _, TestAppContext, VisualContext as _};
|
||||
use language::{Language, LanguageConfig};
|
||||
use rpc::proto;
|
||||
use settings::SettingsStore;
|
||||
|
@ -222,8 +215,17 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_message_editor(cx: &mut TestAppContext) {
|
||||
let editor = init_test(cx);
|
||||
let editor = editor.root(cx);
|
||||
let language_registry = init_test(cx);
|
||||
|
||||
let (editor, cx) = cx.add_window_view(|cx| {
|
||||
MessageEditor::new(
|
||||
language_registry,
|
||||
ChannelStore::global(cx),
|
||||
cx.build_view(|cx| Editor::auto_height(4, cx)),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.set_members(
|
||||
|
@ -255,7 +257,7 @@ mod tests {
|
|||
});
|
||||
});
|
||||
|
||||
cx.foreground().advance_clock(MENTIONS_DEBOUNCE_INTERVAL);
|
||||
cx.executor().advance_clock(MENTIONS_DEBOUNCE_INTERVAL);
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
let (text, ranges) = marked_text_ranges("Hello, «@a-b»! Have you met «@C_D»?", false);
|
||||
|
@ -269,15 +271,14 @@ mod tests {
|
|||
});
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) -> WindowHandle<MessageEditor> {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) -> Arc<LanguageRegistry> {
|
||||
cx.update(|cx| {
|
||||
let http = FakeHttpClient::with_404_response();
|
||||
let client = Client::new(http.clone(), cx);
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init((), cx);
|
||||
let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||
let settings = SettingsStore::test(cx);
|
||||
cx.set_global(settings);
|
||||
theme::init(theme::LoadThemes::JustBase, cx);
|
||||
language::init(cx);
|
||||
editor::init(cx);
|
||||
client::init(&client, cx);
|
||||
|
@ -292,16 +293,6 @@ mod tests {
|
|||
},
|
||||
Some(tree_sitter_markdown::language()),
|
||||
)));
|
||||
|
||||
let editor = cx.add_window(|cx| {
|
||||
MessageEditor::new(
|
||||
language_registry,
|
||||
ChannelStore::global(cx),
|
||||
cx.add_view(|cx| Editor::auto_height(4, None, cx)),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.foreground().run_until_parked();
|
||||
editor
|
||||
language_registry
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ use theme::{ActiveTheme, ThemeSettings};
|
|||
// channel_id: ChannelId,
|
||||
// }
|
||||
|
||||
#[derive(Action, PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenChannelNotes {
|
||||
pub channel_id: ChannelId,
|
||||
}
|
||||
|
@ -122,15 +122,20 @@ pub struct OpenChannelNotes {
|
|||
// to: ChannelId,
|
||||
// }
|
||||
|
||||
impl_actions!(collab_panel, [OpenChannelNotes]);
|
||||
|
||||
actions!(
|
||||
ToggleFocus,
|
||||
Remove,
|
||||
Secondary,
|
||||
CollapseSelectedChannel,
|
||||
ExpandSelectedChannel,
|
||||
StartMoveChannel,
|
||||
MoveSelected,
|
||||
InsertSpace,
|
||||
collab_panel,
|
||||
[
|
||||
ToggleFocus,
|
||||
Remove,
|
||||
Secondary,
|
||||
CollapseSelectedChannel,
|
||||
ExpandSelectedChannel,
|
||||
StartMoveChannel,
|
||||
MoveSelected,
|
||||
InsertSpace,
|
||||
]
|
||||
);
|
||||
|
||||
// impl_actions!(
|
||||
|
@ -169,12 +174,12 @@ use editor::Editor;
|
|||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt, FeatureFlagViewExt};
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, canvas, div, img, overlay, point, prelude::*, px, rems, serde_json, size, Action,
|
||||
AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div, EventEmitter,
|
||||
FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement, Length, Model,
|
||||
MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render, RenderOnce,
|
||||
ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
actions, canvas, div, img, impl_actions, overlay, point, prelude::*, px, rems, serde_json,
|
||||
size, Action, AppContext, AsyncWindowContext, Bounds, ClipboardItem, DismissEvent, Div,
|
||||
EventEmitter, FocusHandle, Focusable, FocusableView, Hsla, InteractiveElement, IntoElement,
|
||||
Length, Model, MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Quad, Render,
|
||||
RenderOnce, ScrollHandle, SharedString, Size, Stateful, Styled, Subscription, Task, View,
|
||||
ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use project::{Fs, Project};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
@ -192,6 +197,7 @@ use workspace::{
|
|||
};
|
||||
|
||||
use crate::channel_view::ChannelView;
|
||||
use crate::chat_panel::ChatPanel;
|
||||
use crate::{face_pile::FacePile, CollaborationPanelSettings};
|
||||
|
||||
use self::channel_modal::ChannelModal;
|
||||
|
@ -852,7 +858,7 @@ impl CollabPanel {
|
|||
.extend(channel_store.ordered_channels().enumerate().map(
|
||||
|(ix, (_, channel))| StringMatchCandidate {
|
||||
id: ix,
|
||||
string: channel.name.clone(),
|
||||
string: channel.name.clone().into(),
|
||||
char_bag: channel.name.chars().collect(),
|
||||
},
|
||||
));
|
||||
|
@ -2102,14 +2108,13 @@ impl CollabPanel {
|
|||
};
|
||||
cx.window_context().defer(move |cx| {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
todo!();
|
||||
// if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
|
||||
// panel.update(cx, |panel, cx| {
|
||||
// panel
|
||||
// .select_channel(channel_id, None, cx)
|
||||
// .detach_and_log_err(cx);
|
||||
// });
|
||||
// }
|
||||
if let Some(panel) = workspace.focus_panel::<ChatPanel>(cx) {
|
||||
panel.update(cx, |panel, cx| {
|
||||
panel
|
||||
.select_channel(channel_id, None, cx)
|
||||
.detach_and_log_err(cx);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -2262,7 +2267,7 @@ impl CollabPanel {
|
|||
}
|
||||
};
|
||||
|
||||
Some(channel.name.as_str())
|
||||
Some(channel.name.as_ref())
|
||||
});
|
||||
|
||||
if let Some(name) = channel_name {
|
||||
|
@ -2603,9 +2608,14 @@ impl CollabPanel {
|
|||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
})
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.join_channel_chat(channel_id, cx)
|
||||
}))
|
||||
.tooltip(|cx| {
|
||||
Tooltip::text("Open channel chat", cx)
|
||||
}),
|
||||
)
|
||||
.tooltip(|cx| Tooltip::text("Open channel chat", cx)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
|
|
|
@ -13,12 +13,16 @@ use picker::{Picker, PickerDelegate};
|
|||
use std::sync::Arc;
|
||||
use ui::prelude::*;
|
||||
use util::TryFutureExt;
|
||||
use workspace::ModalView;
|
||||
|
||||
actions!(
|
||||
SelectNextControl,
|
||||
ToggleMode,
|
||||
ToggleMemberAdmin,
|
||||
RemoveMember
|
||||
channel_modal,
|
||||
[
|
||||
SelectNextControl,
|
||||
ToggleMode,
|
||||
ToggleMemberAdmin,
|
||||
RemoveMember
|
||||
]
|
||||
);
|
||||
|
||||
// pub fn init(cx: &mut AppContext) {
|
||||
|
@ -140,6 +144,7 @@ impl ChannelModal {
|
|||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for ChannelModal {}
|
||||
impl ModalView for ChannelModal {}
|
||||
|
||||
impl FocusableView for ChannelModal {
|
||||
fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle {
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::sync::Arc;
|
|||
use theme::ActiveTheme as _;
|
||||
use ui::prelude::*;
|
||||
use util::{ResultExt as _, TryFutureExt};
|
||||
use workspace::ModalView;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
//Picker::<ContactFinderDelegate>::init(cx);
|
||||
|
@ -95,6 +96,7 @@ pub struct ContactFinderDelegate {
|
|||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for ContactFinder {}
|
||||
impl ModalView for ContactFinder {}
|
||||
|
||||
impl FocusableView for ContactFinder {
|
||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||
|
|
|
@ -20,11 +20,14 @@ const MAX_PROJECT_NAME_LENGTH: usize = 40;
|
|||
const MAX_BRANCH_NAME_LENGTH: usize = 40;
|
||||
|
||||
actions!(
|
||||
ShareProject,
|
||||
UnshareProject,
|
||||
ToggleUserMenu,
|
||||
ToggleProjectMenu,
|
||||
SwitchBranch
|
||||
collab,
|
||||
[
|
||||
ShareProject,
|
||||
UnshareProject,
|
||||
ToggleUserMenu,
|
||||
ToggleProjectMenu,
|
||||
SwitchBranch
|
||||
]
|
||||
);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::{rc::Rc, sync::Arc};
|
|||
use call::{report_call_event_for_room, ActiveCall, Room};
|
||||
pub use collab_panel::CollabPanel;
|
||||
pub use collab_titlebar_item::CollabTitlebarItem;
|
||||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
|
||||
use gpui::{
|
||||
actions, point, AppContext, GlobalPixels, Pixels, PlatformDisplay, Size, Task, WindowBounds,
|
||||
WindowKind, WindowOptions,
|
||||
|
@ -23,7 +24,10 @@ use settings::Settings;
|
|||
use util::ResultExt;
|
||||
use workspace::AppState;
|
||||
|
||||
actions!(ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall);
|
||||
actions!(
|
||||
collab,
|
||||
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
|
||||
);
|
||||
|
||||
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
||||
CollaborationPanelSettings::register(cx);
|
||||
|
@ -34,7 +38,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
|
|||
collab_titlebar_item::init(cx);
|
||||
collab_panel::init(cx);
|
||||
channel_view::init(cx);
|
||||
// chat_panel::init(cx);
|
||||
chat_panel::init(cx);
|
||||
notifications::init(&app_state, cx);
|
||||
|
||||
// cx.add_global_action(toggle_screen_sharing);
|
||||
|
@ -157,6 +161,6 @@ fn notification_window_options(
|
|||
// .into_any()
|
||||
// }
|
||||
|
||||
// fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
|
||||
// cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
|
||||
// }
|
||||
fn is_channels_feature_enabled(cx: &gpui::WindowContext<'_>) -> bool {
|
||||
cx.is_staff() || cx.has_flag::<ChannelsAlpha>()
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use collections::{CommandPaletteFilter, HashMap};
|
|||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, Action, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
|
||||
Keystroke, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
|
||||
ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate};
|
||||
|
||||
|
@ -16,10 +16,10 @@ use util::{
|
|||
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
|
||||
ResultExt,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
use workspace::{ModalView, Workspace};
|
||||
use zed_actions::OpenZedURL;
|
||||
|
||||
actions!(Toggle);
|
||||
actions!(command_palette, [Toggle]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.set_global(HitCounts::default());
|
||||
|
@ -27,6 +27,8 @@ pub fn init(cx: &mut AppContext) {
|
|||
cx.observe_new_views(CommandPalette::register).detach();
|
||||
}
|
||||
|
||||
impl ModalView for CommandPalette {}
|
||||
|
||||
pub struct CommandPalette {
|
||||
picker: View<Picker<CommandPaletteDelegate>>,
|
||||
}
|
||||
|
@ -48,7 +50,7 @@ impl CommandPalette {
|
|||
.available_actions()
|
||||
.into_iter()
|
||||
.filter_map(|action| {
|
||||
let name = gpui::remove_the_2(action.name());
|
||||
let name = action.name();
|
||||
let namespace = name.split("::").next().unwrap_or("malformed action name");
|
||||
if filter.is_some_and(|f| {
|
||||
f.hidden_namespaces.contains(namespace)
|
||||
|
@ -60,7 +62,6 @@ impl CommandPalette {
|
|||
Some(Command {
|
||||
name: humanize_action_name(&name),
|
||||
action,
|
||||
keystrokes: vec![], // todo!()
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
@ -109,7 +110,6 @@ pub struct CommandPaletteDelegate {
|
|||
struct Command {
|
||||
name: String,
|
||||
action: Box<dyn Action>,
|
||||
keystrokes: Vec<Keystroke>,
|
||||
}
|
||||
|
||||
impl Clone for Command {
|
||||
|
@ -117,7 +117,6 @@ impl Clone for Command {
|
|||
Self {
|
||||
name: self.name.clone(),
|
||||
action: self.action.boxed_clone(),
|
||||
keystrokes: self.keystrokes.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,6 +227,7 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(CommandInterceptResult {
|
||||
action,
|
||||
string,
|
||||
|
@ -243,7 +243,6 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||
commands.push(Command {
|
||||
name: string.clone(),
|
||||
action,
|
||||
keystrokes: vec![],
|
||||
});
|
||||
matches.insert(
|
||||
0,
|
||||
|
@ -255,6 +254,7 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
picker
|
||||
.update(&mut cx, |picker, _| {
|
||||
let delegate = &mut picker.delegate;
|
||||
|
@ -284,6 +284,8 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||
}
|
||||
let action_ix = self.matches[self.selected_ix].candidate_id;
|
||||
let command = self.commands.swap_remove(action_ix);
|
||||
self.matches.clear();
|
||||
self.commands.clear();
|
||||
cx.update_global(|hit_counts: &mut HitCounts, _| {
|
||||
*hit_counts.0.entry(command.name).or_default() += 1;
|
||||
});
|
||||
|
@ -299,13 +301,8 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||
selected: bool,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let Some(r#match) = self.matches.get(ix) else {
|
||||
return None;
|
||||
};
|
||||
let Some(command) = self.commands.get(r#match.candidate_id) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let r#match = self.matches.get(ix)?;
|
||||
let command = self.commands.get(r#match.candidate_id)?;
|
||||
Some(
|
||||
ListItem::new(ix).inset(true).selected(selected).child(
|
||||
h_stack()
|
||||
|
@ -353,8 +350,7 @@ impl std::fmt::Debug for Command {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Command")
|
||||
.field("name", &self.name)
|
||||
.field("keystrokes", &self.keystrokes)
|
||||
.finish()
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,12 +34,15 @@ use util::{
|
|||
};
|
||||
|
||||
actions!(
|
||||
Suggest,
|
||||
NextSuggestion,
|
||||
PreviousSuggestion,
|
||||
Reinstall,
|
||||
SignIn,
|
||||
SignOut
|
||||
copilot,
|
||||
[
|
||||
Suggest,
|
||||
NextSuggestion,
|
||||
PreviousSuggestion,
|
||||
Reinstall,
|
||||
SignIn,
|
||||
SignOut
|
||||
]
|
||||
);
|
||||
|
||||
pub fn init(
|
||||
|
|
|
@ -43,7 +43,7 @@ use workspace::{
|
|||
ItemNavHistory, Pane, ToolbarItemLocation, Workspace,
|
||||
};
|
||||
|
||||
actions!(Deploy, ToggleWarnings);
|
||||
actions!(diagnostics, [Deploy, ToggleWarnings]);
|
||||
|
||||
const CONTEXT_LINE_COUNT: u32 = 1;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ mod link_go_to_definition;
|
|||
mod mouse_context_menu;
|
||||
pub mod movement;
|
||||
mod persistence;
|
||||
mod rust_analyzer_ext;
|
||||
pub mod scroll;
|
||||
pub mod selections_collection;
|
||||
|
||||
|
@ -300,6 +301,7 @@ actions!(
|
|||
DeleteToEndOfLine,
|
||||
CutToEndOfLine,
|
||||
DuplicateLine,
|
||||
ExpandMacroRecursively,
|
||||
MoveLineUp,
|
||||
MoveLineDown,
|
||||
JoinLines,
|
||||
|
@ -425,6 +427,8 @@ pub fn init_settings(cx: &mut AppContext) {
|
|||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
init_settings(cx);
|
||||
|
||||
rust_analyzer_ext::apply_related_actions(cx);
|
||||
cx.add_action(Editor::new_file);
|
||||
cx.add_action(Editor::new_file_in_direction);
|
||||
cx.add_action(Editor::cancel);
|
||||
|
|
98
crates/editor/src/rust_analyzer_ext.rs
Normal file
98
crates/editor/src/rust_analyzer_ext.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use gpui::{AppContext, Task, ViewContext};
|
||||
use language::Language;
|
||||
use multi_buffer::MultiBuffer;
|
||||
use project::lsp_ext_command::ExpandMacro;
|
||||
use text::ToPointUtf16;
|
||||
|
||||
use crate::{Editor, ExpandMacroRecursively};
|
||||
|
||||
pub fn apply_related_actions(cx: &mut AppContext) {
|
||||
cx.add_async_action(expand_macro_recursively);
|
||||
}
|
||||
|
||||
pub fn expand_macro_recursively(
|
||||
editor: &mut Editor,
|
||||
_: &ExpandMacroRecursively,
|
||||
cx: &mut ViewContext<'_, '_, Editor>,
|
||||
) -> Option<Task<anyhow::Result<()>>> {
|
||||
if editor.selections.count() == 0 {
|
||||
return None;
|
||||
}
|
||||
let project = editor.project.as_ref()?;
|
||||
let workspace = editor.workspace(cx)?;
|
||||
let multibuffer = editor.buffer().read(cx);
|
||||
|
||||
let (trigger_anchor, rust_language, server_to_query, buffer) = editor
|
||||
.selections
|
||||
.disjoint_anchors()
|
||||
.into_iter()
|
||||
.filter(|selection| selection.start == selection.end)
|
||||
.filter_map(|selection| Some((selection.start.buffer_id?, selection.start)))
|
||||
.filter_map(|(buffer_id, trigger_anchor)| {
|
||||
let buffer = multibuffer.buffer(buffer_id)?;
|
||||
let rust_language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
|
||||
if !is_rust_language(&rust_language) {
|
||||
return None;
|
||||
}
|
||||
Some((trigger_anchor, rust_language, buffer))
|
||||
})
|
||||
.find_map(|(trigger_anchor, rust_language, buffer)| {
|
||||
project
|
||||
.read(cx)
|
||||
.language_servers_for_buffer(buffer.read(cx), cx)
|
||||
.into_iter()
|
||||
.find_map(|(adapter, server)| {
|
||||
if adapter.name.0.as_ref() == "rust-analyzer" {
|
||||
Some((
|
||||
trigger_anchor,
|
||||
Arc::clone(&rust_language),
|
||||
server.server_id(),
|
||||
buffer.clone(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})?;
|
||||
|
||||
let project = project.clone();
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
let position = trigger_anchor.text_anchor.to_point_utf16(&buffer_snapshot);
|
||||
let expand_macro_task = project.update(cx, |project, cx| {
|
||||
project.request_lsp(
|
||||
buffer,
|
||||
project::LanguageServerToQuery::Other(server_to_query),
|
||||
ExpandMacro { position },
|
||||
cx,
|
||||
)
|
||||
});
|
||||
Some(cx.spawn(|_, mut cx| async move {
|
||||
let macro_expansion = expand_macro_task.await.context("expand macro")?;
|
||||
if macro_expansion.is_empty() {
|
||||
log::info!("Empty macro expansion for position {position:?}");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buffer = project.update(&mut cx, |project, cx| {
|
||||
project.create_buffer(¯o_expansion.expansion, Some(rust_language), cx)
|
||||
})?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let buffer = cx.add_model(|cx| {
|
||||
MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
|
||||
});
|
||||
workspace.add_item(
|
||||
Box::new(cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
|
||||
cx,
|
||||
);
|
||||
});
|
||||
|
||||
anyhow::Ok(())
|
||||
}))
|
||||
}
|
||||
|
||||
fn is_rust_language(language: &Language) -> bool {
|
||||
language.name().as_ref() == "Rust"
|
||||
}
|
|
@ -13,6 +13,7 @@ mod link_go_to_definition;
|
|||
mod mouse_context_menu;
|
||||
pub mod movement;
|
||||
mod persistence;
|
||||
mod rust_analyzer_ext;
|
||||
pub mod scroll;
|
||||
pub mod selections_collection;
|
||||
|
||||
|
@ -39,8 +40,8 @@ use futures::FutureExt;
|
|||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use git::diff_hunk_to_display;
|
||||
use gpui::{
|
||||
actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement,
|
||||
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
|
||||
actions, div, impl_actions, point, prelude::*, px, relative, rems, size, uniform_list, Action,
|
||||
AnyElement, AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Context,
|
||||
DispatchPhase, Div, ElementId, EventEmitter, FocusHandle, FocusableView, FontFeatures,
|
||||
FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, InteractiveText, KeyContext, Model,
|
||||
MouseButton, ParentElement, Pixels, Render, RenderOnce, SharedString, Styled, StyledText,
|
||||
|
@ -107,7 +108,7 @@ use ui::{
|
|||
use ui::{prelude::*, IconSize};
|
||||
use util::{post_inc, RangeExt, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
item::{ItemEvent, ItemHandle},
|
||||
item::{Item, ItemEvent, ItemHandle},
|
||||
searchable::SearchEvent,
|
||||
ItemNavHistory, Pane, SplitDirection, ViewId, Workspace,
|
||||
};
|
||||
|
@ -185,82 +186,101 @@ pub fn render_parsed_markdown(
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct SelectNext {
|
||||
#[serde(default)]
|
||||
pub replace_newest: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct SelectPrevious {
|
||||
#[serde(default)]
|
||||
pub replace_newest: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct SelectAllMatches {
|
||||
#[serde(default)]
|
||||
pub replace_newest: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct SelectToBeginningOfLine {
|
||||
#[serde(default)]
|
||||
stop_at_soft_wraps: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct MovePageUp {
|
||||
#[serde(default)]
|
||||
center_cursor: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct MovePageDown {
|
||||
#[serde(default)]
|
||||
center_cursor: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct SelectToEndOfLine {
|
||||
#[serde(default)]
|
||||
stop_at_soft_wraps: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct ToggleCodeActions {
|
||||
#[serde(default)]
|
||||
pub deployed_from_indicator: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct ConfirmCompletion {
|
||||
#[serde(default)]
|
||||
pub item_ix: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct ConfirmCodeAction {
|
||||
#[serde(default)]
|
||||
pub item_ix: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct ToggleComments {
|
||||
#[serde(default)]
|
||||
pub advance_downwards: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct FoldAt {
|
||||
pub buffer_row: u32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
pub struct UnfoldAt {
|
||||
pub buffer_row: u32,
|
||||
}
|
||||
|
||||
impl_actions!(
|
||||
editor,
|
||||
[
|
||||
SelectNext,
|
||||
SelectPrevious,
|
||||
SelectAllMatches,
|
||||
SelectToBeginningOfLine,
|
||||
MovePageUp,
|
||||
MovePageDown,
|
||||
SelectToEndOfLine,
|
||||
ToggleCodeActions,
|
||||
ConfirmCompletion,
|
||||
ConfirmCodeAction,
|
||||
ToggleComments,
|
||||
FoldAt,
|
||||
UnfoldAt
|
||||
]
|
||||
);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum InlayId {
|
||||
Suggestion(usize),
|
||||
|
@ -277,121 +297,125 @@ impl InlayId {
|
|||
}
|
||||
|
||||
actions!(
|
||||
AddSelectionAbove,
|
||||
AddSelectionBelow,
|
||||
Backspace,
|
||||
Cancel,
|
||||
ConfirmRename,
|
||||
ContextMenuFirst,
|
||||
ContextMenuLast,
|
||||
ContextMenuNext,
|
||||
ContextMenuPrev,
|
||||
ConvertToKebabCase,
|
||||
ConvertToLowerCamelCase,
|
||||
ConvertToLowerCase,
|
||||
ConvertToSnakeCase,
|
||||
ConvertToTitleCase,
|
||||
ConvertToUpperCamelCase,
|
||||
ConvertToUpperCase,
|
||||
Copy,
|
||||
CopyHighlightJson,
|
||||
CopyPath,
|
||||
CopyRelativePath,
|
||||
Cut,
|
||||
CutToEndOfLine,
|
||||
Delete,
|
||||
DeleteLine,
|
||||
DeleteToBeginningOfLine,
|
||||
DeleteToEndOfLine,
|
||||
DeleteToNextSubwordEnd,
|
||||
DeleteToNextWordEnd,
|
||||
DeleteToPreviousSubwordStart,
|
||||
DeleteToPreviousWordStart,
|
||||
DuplicateLine,
|
||||
FindAllReferences,
|
||||
Fold,
|
||||
FoldSelectedRanges,
|
||||
Format,
|
||||
GoToDefinition,
|
||||
GoToDefinitionSplit,
|
||||
GoToDiagnostic,
|
||||
GoToHunk,
|
||||
GoToPrevDiagnostic,
|
||||
GoToPrevHunk,
|
||||
GoToTypeDefinition,
|
||||
GoToTypeDefinitionSplit,
|
||||
HalfPageDown,
|
||||
HalfPageUp,
|
||||
Hover,
|
||||
Indent,
|
||||
JoinLines,
|
||||
LineDown,
|
||||
LineUp,
|
||||
MoveDown,
|
||||
MoveLeft,
|
||||
MoveLineDown,
|
||||
MoveLineUp,
|
||||
MoveRight,
|
||||
MoveToBeginning,
|
||||
MoveToBeginningOfLine,
|
||||
MoveToEnclosingBracket,
|
||||
MoveToEnd,
|
||||
MoveToEndOfLine,
|
||||
MoveToEndOfParagraph,
|
||||
MoveToNextSubwordEnd,
|
||||
MoveToNextWordEnd,
|
||||
MoveToPreviousSubwordStart,
|
||||
MoveToPreviousWordStart,
|
||||
MoveToStartOfParagraph,
|
||||
MoveUp,
|
||||
Newline,
|
||||
NewlineAbove,
|
||||
NewlineBelow,
|
||||
NextScreen,
|
||||
OpenExcerpts,
|
||||
Outdent,
|
||||
PageDown,
|
||||
PageUp,
|
||||
Paste,
|
||||
Redo,
|
||||
RedoSelection,
|
||||
Rename,
|
||||
RestartLanguageServer,
|
||||
RevealInFinder,
|
||||
ReverseLines,
|
||||
ScrollCursorBottom,
|
||||
ScrollCursorCenter,
|
||||
ScrollCursorTop,
|
||||
SelectAll,
|
||||
SelectDown,
|
||||
SelectLargerSyntaxNode,
|
||||
SelectLeft,
|
||||
SelectLine,
|
||||
SelectRight,
|
||||
SelectSmallerSyntaxNode,
|
||||
SelectToBeginning,
|
||||
SelectToEnd,
|
||||
SelectToEndOfParagraph,
|
||||
SelectToNextSubwordEnd,
|
||||
SelectToNextWordEnd,
|
||||
SelectToPreviousSubwordStart,
|
||||
SelectToPreviousWordStart,
|
||||
SelectToStartOfParagraph,
|
||||
SelectUp,
|
||||
ShowCharacterPalette,
|
||||
ShowCompletions,
|
||||
ShuffleLines,
|
||||
SortLinesCaseInsensitive,
|
||||
SortLinesCaseSensitive,
|
||||
SplitSelectionIntoLines,
|
||||
Tab,
|
||||
TabPrev,
|
||||
ToggleInlayHints,
|
||||
ToggleSoftWrap,
|
||||
Transpose,
|
||||
Undo,
|
||||
UndoSelection,
|
||||
UnfoldLines,
|
||||
editor,
|
||||
[
|
||||
AddSelectionAbove,
|
||||
AddSelectionBelow,
|
||||
Backspace,
|
||||
Cancel,
|
||||
ConfirmRename,
|
||||
ContextMenuFirst,
|
||||
ContextMenuLast,
|
||||
ContextMenuNext,
|
||||
ContextMenuPrev,
|
||||
ConvertToKebabCase,
|
||||
ConvertToLowerCamelCase,
|
||||
ConvertToLowerCase,
|
||||
ConvertToSnakeCase,
|
||||
ConvertToTitleCase,
|
||||
ConvertToUpperCamelCase,
|
||||
ConvertToUpperCase,
|
||||
Copy,
|
||||
CopyHighlightJson,
|
||||
CopyPath,
|
||||
CopyRelativePath,
|
||||
Cut,
|
||||
CutToEndOfLine,
|
||||
Delete,
|
||||
DeleteLine,
|
||||
DeleteToBeginningOfLine,
|
||||
DeleteToEndOfLine,
|
||||
DeleteToNextSubwordEnd,
|
||||
DeleteToNextWordEnd,
|
||||
DeleteToPreviousSubwordStart,
|
||||
DeleteToPreviousWordStart,
|
||||
DuplicateLine,
|
||||
ExpandMacroRecursively,
|
||||
FindAllReferences,
|
||||
Fold,
|
||||
FoldSelectedRanges,
|
||||
Format,
|
||||
GoToDefinition,
|
||||
GoToDefinitionSplit,
|
||||
GoToDiagnostic,
|
||||
GoToHunk,
|
||||
GoToPrevDiagnostic,
|
||||
GoToPrevHunk,
|
||||
GoToTypeDefinition,
|
||||
GoToTypeDefinitionSplit,
|
||||
HalfPageDown,
|
||||
HalfPageUp,
|
||||
Hover,
|
||||
Indent,
|
||||
JoinLines,
|
||||
LineDown,
|
||||
LineUp,
|
||||
MoveDown,
|
||||
MoveLeft,
|
||||
MoveLineDown,
|
||||
MoveLineUp,
|
||||
MoveRight,
|
||||
MoveToBeginning,
|
||||
MoveToBeginningOfLine,
|
||||
MoveToEnclosingBracket,
|
||||
MoveToEnd,
|
||||
MoveToEndOfLine,
|
||||
MoveToEndOfParagraph,
|
||||
MoveToNextSubwordEnd,
|
||||
MoveToNextWordEnd,
|
||||
MoveToPreviousSubwordStart,
|
||||
MoveToPreviousWordStart,
|
||||
MoveToStartOfParagraph,
|
||||
MoveUp,
|
||||
Newline,
|
||||
NewlineAbove,
|
||||
NewlineBelow,
|
||||
NextScreen,
|
||||
OpenExcerpts,
|
||||
Outdent,
|
||||
PageDown,
|
||||
PageUp,
|
||||
Paste,
|
||||
Redo,
|
||||
RedoSelection,
|
||||
Rename,
|
||||
RestartLanguageServer,
|
||||
RevealInFinder,
|
||||
ReverseLines,
|
||||
ScrollCursorBottom,
|
||||
ScrollCursorCenter,
|
||||
ScrollCursorTop,
|
||||
SelectAll,
|
||||
SelectDown,
|
||||
SelectLargerSyntaxNode,
|
||||
SelectLeft,
|
||||
SelectLine,
|
||||
SelectRight,
|
||||
SelectSmallerSyntaxNode,
|
||||
SelectToBeginning,
|
||||
SelectToEnd,
|
||||
SelectToEndOfParagraph,
|
||||
SelectToNextSubwordEnd,
|
||||
SelectToNextWordEnd,
|
||||
SelectToPreviousSubwordStart,
|
||||
SelectToPreviousWordStart,
|
||||
SelectToStartOfParagraph,
|
||||
SelectUp,
|
||||
ShowCharacterPalette,
|
||||
ShowCompletions,
|
||||
ShuffleLines,
|
||||
SortLinesCaseInsensitive,
|
||||
SortLinesCaseSensitive,
|
||||
SplitSelectionIntoLines,
|
||||
Tab,
|
||||
TabPrev,
|
||||
ToggleInlayHints,
|
||||
ToggleSoftWrap,
|
||||
Transpose,
|
||||
Undo,
|
||||
UndoSelection,
|
||||
UnfoldLines,
|
||||
]
|
||||
);
|
||||
|
||||
enum DocumentHighlightRead {}
|
||||
|
@ -8682,13 +8706,13 @@ impl Editor {
|
|||
);
|
||||
}
|
||||
|
||||
// pub fn set_searchable(&mut self, searchable: bool) {
|
||||
// self.searchable = searchable;
|
||||
// }
|
||||
pub fn set_searchable(&mut self, searchable: bool) {
|
||||
self.searchable = searchable;
|
||||
}
|
||||
|
||||
// pub fn searchable(&self) -> bool {
|
||||
// self.searchable
|
||||
// }
|
||||
pub fn searchable(&self) -> bool {
|
||||
self.searchable
|
||||
}
|
||||
|
||||
fn open_excerpts(&mut self, _: &OpenExcerpts, cx: &mut ViewContext<Self>) {
|
||||
let buffer = self.buffer.read(cx);
|
||||
|
@ -9319,7 +9343,6 @@ impl Render for Editor {
|
|||
scrollbar_width: px(12.),
|
||||
syntax: cx.theme().syntax().clone(),
|
||||
diagnostic_style: cx.theme().diagnostic_style(),
|
||||
// TODO kb find `HighlightStyle` usages
|
||||
// todo!("what about the rest of the highlight style parts?")
|
||||
inlays_style: HighlightStyle {
|
||||
color: Some(cx.theme().status().hint),
|
||||
|
|
|
@ -32,7 +32,7 @@ use gpui::{
|
|||
Style, Styled, TextRun, TextStyle, View, ViewContext, WeakView, WindowContext, WrappedLine,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::language_settings::ShowWhitespaceSetting;
|
||||
use language::{language_settings::ShowWhitespaceSetting, Language};
|
||||
use multi_buffer::Anchor;
|
||||
use project::{
|
||||
project_settings::{GitGutterSetting, ProjectSettings},
|
||||
|
@ -135,11 +135,13 @@ impl EditorElement {
|
|||
|
||||
fn register_actions(&self, cx: &mut WindowContext) {
|
||||
let view = &self.editor;
|
||||
self.editor.update(cx, |editor, cx| {
|
||||
view.update(cx, |editor, cx| {
|
||||
for action in editor.editor_actions.iter() {
|
||||
(action)(cx)
|
||||
}
|
||||
});
|
||||
|
||||
crate::rust_analyzer_ext::apply_related_actions(view, cx);
|
||||
register_action(view, cx, Editor::move_left);
|
||||
register_action(view, cx, Editor::move_right);
|
||||
register_action(view, cx, Editor::move_down);
|
||||
|
@ -385,17 +387,17 @@ impl EditorElement {
|
|||
gutter_bounds: Bounds<Pixels>,
|
||||
stacking_order: &StackingOrder,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
) {
|
||||
let mut click_count = event.click_count;
|
||||
let modifiers = event.modifiers;
|
||||
|
||||
if gutter_bounds.contains_point(&event.position) {
|
||||
click_count = 3; // Simulate triple-click when clicking the gutter to select lines
|
||||
} else if !text_bounds.contains_point(&event.position) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if !cx.was_top_layer(&event.position, stacking_order) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
let point_for_position = position_map.point_for_position(text_bounds, event.position);
|
||||
|
@ -427,7 +429,7 @@ impl EditorElement {
|
|||
);
|
||||
}
|
||||
|
||||
true
|
||||
cx.stop_propagation();
|
||||
}
|
||||
|
||||
fn mouse_right_down(
|
||||
|
@ -436,9 +438,9 @@ impl EditorElement {
|
|||
position_map: &PositionMap,
|
||||
text_bounds: Bounds<Pixels>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
) {
|
||||
if !text_bounds.contains_point(&event.position) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
let point_for_position = position_map.point_for_position(text_bounds, event.position);
|
||||
mouse_context_menu::deploy_context_menu(
|
||||
|
@ -447,7 +449,7 @@ impl EditorElement {
|
|||
point_for_position.previous_valid,
|
||||
cx,
|
||||
);
|
||||
true
|
||||
cx.stop_propagation();
|
||||
}
|
||||
|
||||
fn mouse_up(
|
||||
|
@ -457,7 +459,7 @@ impl EditorElement {
|
|||
text_bounds: Bounds<Pixels>,
|
||||
stacking_order: &StackingOrder,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
) {
|
||||
let end_selection = editor.has_pending_selection();
|
||||
let pending_nonempty_selections = editor.has_pending_nonempty_selection();
|
||||
|
||||
|
@ -479,10 +481,10 @@ impl EditorElement {
|
|||
go_to_fetched_definition(editor, point, split, cx);
|
||||
}
|
||||
|
||||
return true;
|
||||
cx.stop_propagation();
|
||||
} else if end_selection {
|
||||
cx.stop_propagation();
|
||||
}
|
||||
|
||||
end_selection
|
||||
}
|
||||
|
||||
fn mouse_moved(
|
||||
|
@ -493,7 +495,7 @@ impl EditorElement {
|
|||
gutter_bounds: Bounds<Pixels>,
|
||||
stacking_order: &StackingOrder,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
) {
|
||||
let modifiers = event.modifiers;
|
||||
if editor.has_pending_selection() && event.pressed_button == Some(MouseButton::Left) {
|
||||
let point_for_position = position_map.point_for_position(text_bounds, event.position);
|
||||
|
@ -562,11 +564,13 @@ impl EditorElement {
|
|||
}
|
||||
}
|
||||
|
||||
true
|
||||
cx.stop_propagation();
|
||||
} else {
|
||||
update_go_to_definition_link(editor, None, modifiers.command, modifiers.shift, cx);
|
||||
hover_at(editor, None, cx);
|
||||
gutter_hovered && was_top
|
||||
if gutter_hovered && was_top {
|
||||
cx.stop_propagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,9 +580,9 @@ impl EditorElement {
|
|||
position_map: &PositionMap,
|
||||
bounds: &InteractiveBounds,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
) {
|
||||
if !bounds.visibly_contains(&event.position, cx) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
let line_height = position_map.line_height;
|
||||
|
@ -602,8 +606,7 @@ impl EditorElement {
|
|||
let y = f32::from((scroll_position.y * line_height - delta.y) / line_height);
|
||||
let scroll_position = point(x, y).clamp(&point(0., 0.), &position_map.scroll_max);
|
||||
editor.scroll(scroll_position, axis, cx);
|
||||
|
||||
true
|
||||
cx.stop_propagation();
|
||||
}
|
||||
|
||||
fn paint_background(
|
||||
|
@ -749,44 +752,47 @@ impl EditorElement {
|
|||
}
|
||||
}
|
||||
|
||||
for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
|
||||
if let Some(mut fold_indicator) = fold_indicator {
|
||||
let mut fold_indicator = fold_indicator.into_any_element();
|
||||
cx.with_z_index(1, |cx| {
|
||||
for (ix, fold_indicator) in layout.fold_indicators.drain(..).enumerate() {
|
||||
if let Some(mut fold_indicator) = fold_indicator {
|
||||
let mut fold_indicator = fold_indicator.into_any_element();
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height * 0.55),
|
||||
);
|
||||
let fold_indicator_size = fold_indicator.measure(available_space, cx);
|
||||
|
||||
let position = point(
|
||||
bounds.size.width - layout.gutter_padding,
|
||||
ix as f32 * line_height - (scroll_top % line_height),
|
||||
);
|
||||
let centering_offset = point(
|
||||
(layout.gutter_padding + layout.gutter_margin - fold_indicator_size.width)
|
||||
/ 2.,
|
||||
(line_height - fold_indicator_size.height) / 2.,
|
||||
);
|
||||
let origin = bounds.origin + position + centering_offset;
|
||||
fold_indicator.draw(origin, available_space, cx);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(indicator) = layout.code_actions_indicator.take() {
|
||||
let mut button = indicator.button.into_any_element();
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height * 0.55),
|
||||
AvailableSpace::Definite(line_height),
|
||||
);
|
||||
let fold_indicator_size = fold_indicator.measure(available_space, cx);
|
||||
let indicator_size = button.measure(available_space, cx);
|
||||
|
||||
let position = point(
|
||||
bounds.size.width - layout.gutter_padding,
|
||||
ix as f32 * line_height - (scroll_top % line_height),
|
||||
);
|
||||
let centering_offset = point(
|
||||
(layout.gutter_padding + layout.gutter_margin - fold_indicator_size.width) / 2.,
|
||||
(line_height - fold_indicator_size.height) / 2.,
|
||||
);
|
||||
let origin = bounds.origin + position + centering_offset;
|
||||
fold_indicator.draw(origin, available_space, cx);
|
||||
let mut x = Pixels::ZERO;
|
||||
let mut y = indicator.row as f32 * line_height - scroll_top;
|
||||
// Center indicator.
|
||||
x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
|
||||
y += (line_height - indicator_size.height) / 2.;
|
||||
|
||||
button.draw(bounds.origin + point(x, y), available_space, cx);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(indicator) = layout.code_actions_indicator.take() {
|
||||
let mut button = indicator.button.into_any_element();
|
||||
let available_space = size(
|
||||
AvailableSpace::MinContent,
|
||||
AvailableSpace::Definite(line_height),
|
||||
);
|
||||
let indicator_size = button.measure(available_space, cx);
|
||||
|
||||
let mut x = Pixels::ZERO;
|
||||
let mut y = indicator.row as f32 * line_height - scroll_top;
|
||||
// Center indicator.
|
||||
x += ((layout.gutter_padding + layout.gutter_margin) - indicator_size.width) / 2.;
|
||||
y += (line_height - indicator_size.height) / 2.;
|
||||
|
||||
button.draw(bounds.origin + point(x, y), available_space, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn paint_diff_hunks(bounds: Bounds<Pixels>, layout: &LayoutState, cx: &mut WindowContext) {
|
||||
|
@ -824,8 +830,8 @@ impl EditorElement {
|
|||
};
|
||||
|
||||
let color = match status {
|
||||
DiffHunkStatus::Added => gpui::green(), // todo!("use the appropriate color")
|
||||
DiffHunkStatus::Modified => gpui::yellow(), // todo!("use the appropriate color")
|
||||
DiffHunkStatus::Added => cx.theme().status().created,
|
||||
DiffHunkStatus::Modified => cx.theme().status().modified,
|
||||
|
||||
//TODO: This rendering is entirely a horrible hack
|
||||
DiffHunkStatus::Removed => {
|
||||
|
@ -842,7 +848,7 @@ impl EditorElement {
|
|||
cx.paint_quad(
|
||||
highlight_bounds,
|
||||
Corners::all(1. * line_height),
|
||||
gpui::red(), // todo!("use the right color")
|
||||
cx.theme().status().deleted,
|
||||
Edges::default(),
|
||||
transparent_black(),
|
||||
);
|
||||
|
@ -1235,203 +1241,216 @@ impl EditorElement {
|
|||
bounds.upper_right().x - self.style.scrollbar_width
|
||||
}
|
||||
|
||||
// fn paint_scrollbar(
|
||||
// &mut self,
|
||||
// bounds: Bounds<Pixels>,
|
||||
// layout: &mut LayoutState,
|
||||
// editor: &Editor,
|
||||
// cx: &mut ViewContext<Editor>,
|
||||
// ) {
|
||||
// enum ScrollbarMouseHandlers {}
|
||||
// if layout.mode != EditorMode::Full {
|
||||
// return;
|
||||
// }
|
||||
fn paint_scrollbar(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
layout: &mut LayoutState,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
if layout.mode != EditorMode::Full {
|
||||
return;
|
||||
}
|
||||
|
||||
// let style = &self.style.theme.scrollbar;
|
||||
let top = bounds.origin.y;
|
||||
let bottom = bounds.lower_left().y;
|
||||
let right = bounds.lower_right().x;
|
||||
let left = self.scrollbar_left(&bounds);
|
||||
let row_range = layout.scrollbar_row_range.clone();
|
||||
let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
|
||||
|
||||
// let top = bounds.min_y;
|
||||
// let bottom = bounds.max_y;
|
||||
// let right = bounds.max_x;
|
||||
// let left = self.scrollbar_left(&bounds);
|
||||
// let row_range = &layout.scrollbar_row_range;
|
||||
// let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
|
||||
let mut height = bounds.size.height;
|
||||
let mut first_row_y_offset = px(0.0);
|
||||
|
||||
// let mut height = bounds.height();
|
||||
// let mut first_row_y_offset = 0.0;
|
||||
// Impose a minimum height on the scrollbar thumb
|
||||
let row_height = height / max_row;
|
||||
let min_thumb_height = layout.position_map.line_height;
|
||||
let thumb_height = (row_range.end - row_range.start) * row_height;
|
||||
if thumb_height < min_thumb_height {
|
||||
first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
|
||||
height -= min_thumb_height - thumb_height;
|
||||
}
|
||||
|
||||
// // Impose a minimum height on the scrollbar thumb
|
||||
// let row_height = height / max_row;
|
||||
// let min_thumb_height =
|
||||
// style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
|
||||
// let thumb_height = (row_range.end - row_range.start) * row_height;
|
||||
// if thumb_height < min_thumb_height {
|
||||
// first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
|
||||
// height -= min_thumb_height - thumb_height;
|
||||
// }
|
||||
let y_for_row = |row: f32| -> Pixels { top + first_row_y_offset + row * row_height };
|
||||
|
||||
// let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * row_height };
|
||||
let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
|
||||
let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
|
||||
let track_bounds = Bounds::from_corners(point(left, top), point(right, bottom));
|
||||
let thumb_bounds = Bounds::from_corners(point(left, thumb_top), point(right, thumb_bottom));
|
||||
|
||||
// let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
|
||||
// let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
|
||||
// let track_bounds = Bounds::<Pixels>::from_points(point(left, top), point(right, bottom));
|
||||
// let thumb_bounds = Bounds::<Pixels>::from_points(point(left, thumb_top), point(right, thumb_bottom));
|
||||
if layout.show_scrollbars {
|
||||
cx.paint_quad(
|
||||
track_bounds,
|
||||
Corners::default(),
|
||||
gpui::blue(), // todo!("style.track.background_color")
|
||||
Edges::default(), // todo!("style.track.border")
|
||||
transparent_black(), // todo!("style.track.border")
|
||||
);
|
||||
let scrollbar_settings = EditorSettings::get_global(cx).scrollbar;
|
||||
if layout.is_singleton && scrollbar_settings.selections {
|
||||
let start_anchor = Anchor::min();
|
||||
let end_anchor = Anchor::max();
|
||||
let background_ranges = self
|
||||
.editor
|
||||
.read(cx)
|
||||
.background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
|
||||
start_anchor..end_anchor,
|
||||
&layout.position_map.snapshot,
|
||||
50000,
|
||||
);
|
||||
for range in background_ranges {
|
||||
let start_y = y_for_row(range.start().row() as f32);
|
||||
let mut end_y = y_for_row(range.end().row() as f32);
|
||||
if end_y - start_y < px(1.) {
|
||||
end_y = start_y + px(1.);
|
||||
}
|
||||
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
||||
cx.paint_quad(
|
||||
bounds,
|
||||
Corners::default(),
|
||||
gpui::yellow(), // todo!("theme.editor.scrollbar")
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: px(1.),
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
gpui::green(), // todo!("style.thumb.border.color")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if layout.show_scrollbars {
|
||||
// cx.paint_quad(Quad {
|
||||
// bounds: track_bounds,
|
||||
// border: style.track.border.into(),
|
||||
// background: style.track.background_color,
|
||||
// ..Default::default()
|
||||
// });
|
||||
// let scrollbar_settings = settings::get::<EditorSettings>(cx).scrollbar;
|
||||
// let theme = theme::current(cx);
|
||||
// let scrollbar_theme = &theme.editor.scrollbar;
|
||||
// if layout.is_singleton && scrollbar_settings.selections {
|
||||
// let start_anchor = Anchor::min();
|
||||
// let end_anchor = Anchor::max;
|
||||
// let color = scrollbar_theme.selections;
|
||||
// let border = Border {
|
||||
// width: 1.,
|
||||
// color: style.thumb.border.color,
|
||||
// overlay: false,
|
||||
// top: false,
|
||||
// right: true,
|
||||
// bottom: false,
|
||||
// left: true,
|
||||
// };
|
||||
// let mut push_region = |start: DisplayPoint, end: DisplayPoint| {
|
||||
// let start_y = y_for_row(start.row() as f32);
|
||||
// let mut end_y = y_for_row(end.row() as f32);
|
||||
// if end_y - start_y < 1. {
|
||||
// end_y = start_y + 1.;
|
||||
// }
|
||||
// let bounds = Bounds::<Pixels>::from_points(point(left, start_y), point(right, end_y));
|
||||
if layout.is_singleton && scrollbar_settings.git_diff {
|
||||
for hunk in layout
|
||||
.position_map
|
||||
.snapshot
|
||||
.buffer_snapshot
|
||||
.git_diff_hunks_in_range(0..(max_row.floor() as u32))
|
||||
{
|
||||
let start_display = Point::new(hunk.buffer_range.start, 0)
|
||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||
let end_display = Point::new(hunk.buffer_range.end, 0)
|
||||
.to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||
let start_y = y_for_row(start_display.row() as f32);
|
||||
let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
|
||||
y_for_row((end_display.row() + 1) as f32)
|
||||
} else {
|
||||
y_for_row((end_display.row()) as f32)
|
||||
};
|
||||
|
||||
// cx.paint_quad(Quad {
|
||||
// bounds,
|
||||
// background: Some(color),
|
||||
// border: border.into(),
|
||||
// corner_radii: style.thumb.corner_radii.into(),
|
||||
// })
|
||||
// };
|
||||
// let background_ranges = editor
|
||||
// .background_highlight_row_ranges::<crate::items::BufferSearchHighlights>(
|
||||
// start_anchor..end_anchor,
|
||||
// &layout.position_map.snapshot,
|
||||
// 50000,
|
||||
// );
|
||||
// for row in background_ranges {
|
||||
// let start = row.start();
|
||||
// let end = row.end();
|
||||
// push_region(*start, *end);
|
||||
// }
|
||||
// }
|
||||
if end_y - start_y < px(1.) {
|
||||
end_y = start_y + px(1.);
|
||||
}
|
||||
let bounds = Bounds::from_corners(point(left, start_y), point(right, end_y));
|
||||
|
||||
// if layout.is_singleton && scrollbar_settings.git_diff {
|
||||
// let diff_style = scrollbar_theme.git.clone();
|
||||
// for hunk in layout
|
||||
// .position_map
|
||||
// .snapshot
|
||||
// .buffer_snapshot
|
||||
// .git_diff_hunks_in_range(0..(max_row.floor() as u32))
|
||||
// {
|
||||
// let start_display = Point::new(hunk.buffer_range.start, 0)
|
||||
// .to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||
// let end_display = Point::new(hunk.buffer_range.end, 0)
|
||||
// .to_display_point(&layout.position_map.snapshot.display_snapshot);
|
||||
// let start_y = y_for_row(start_display.row() as f32);
|
||||
// let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end {
|
||||
// y_for_row((end_display.row() + 1) as f32)
|
||||
// } else {
|
||||
// y_for_row((end_display.row()) as f32)
|
||||
// };
|
||||
let color = match hunk.status() {
|
||||
DiffHunkStatus::Added => gpui::green(), // todo!("use the right color")
|
||||
DiffHunkStatus::Modified => gpui::yellow(), // todo!("use the right color")
|
||||
DiffHunkStatus::Removed => gpui::red(), // todo!("use the right color")
|
||||
};
|
||||
cx.paint_quad(
|
||||
bounds,
|
||||
Corners::default(),
|
||||
color,
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: px(1.),
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
gpui::green(), // todo!("style.thumb.border.color")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if end_y - start_y < 1. {
|
||||
// end_y = start_y + 1.;
|
||||
// }
|
||||
// let bounds = Bounds::<Pixels>::from_points(point(left, start_y), point(right, end_y));
|
||||
cx.paint_quad(
|
||||
thumb_bounds,
|
||||
Corners::default(),
|
||||
gpui::black(), // todo!("style.thumb.background_color")
|
||||
Edges {
|
||||
top: Pixels::ZERO,
|
||||
right: px(1.),
|
||||
bottom: Pixels::ZERO,
|
||||
left: px(1.),
|
||||
},
|
||||
gpui::green(), // todo!("style.thumb.border.color")
|
||||
);
|
||||
}
|
||||
|
||||
// let color = match hunk.status() {
|
||||
// DiffHunkStatus::Added => diff_style.inserted,
|
||||
// DiffHunkStatus::Modified => diff_style.modified,
|
||||
// DiffHunkStatus::Removed => diff_style.deleted,
|
||||
// };
|
||||
let mouse_position = cx.mouse_position();
|
||||
if track_bounds.contains_point(&mouse_position) {
|
||||
cx.set_cursor_style(CursorStyle::Arrow);
|
||||
}
|
||||
|
||||
// let border = Border {
|
||||
// width: 1.,
|
||||
// color: style.thumb.border.color,
|
||||
// overlay: false,
|
||||
// top: false,
|
||||
// right: true,
|
||||
// bottom: false,
|
||||
// left: true,
|
||||
// };
|
||||
cx.on_mouse_event({
|
||||
let editor = self.editor.clone();
|
||||
move |event: &MouseMoveEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Capture {
|
||||
return;
|
||||
}
|
||||
|
||||
// cx.paint_quad(Quad {
|
||||
// bounds,
|
||||
// background: Some(color),
|
||||
// border: border.into(),
|
||||
// corner_radii: style.thumb.corner_radii.into(),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
editor.update(cx, |editor, cx| {
|
||||
if event.pressed_button == Some(MouseButton::Left)
|
||||
&& editor.scroll_manager.is_dragging_scrollbar()
|
||||
{
|
||||
let y = mouse_position.y;
|
||||
let new_y = event.position.y;
|
||||
if thumb_top < y && y < thumb_bottom {
|
||||
let mut position = editor.scroll_position(cx);
|
||||
position.y += (new_y - y) * (max_row as f32) / height;
|
||||
if position.y < 0.0 {
|
||||
position.y = 0.0;
|
||||
}
|
||||
editor.set_scroll_position(position, cx);
|
||||
}
|
||||
cx.stop_propagation();
|
||||
} else {
|
||||
editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
|
||||
if track_bounds.contains_point(&event.position) {
|
||||
editor.scroll_manager.show_scrollbar(cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// cx.paint_quad(Quad {
|
||||
// bounds: thumb_bounds,
|
||||
// border: style.thumb.border.into(),
|
||||
// background: style.thumb.background_color,
|
||||
// corner_radii: style.thumb.corner_radii.into(),
|
||||
// });
|
||||
// }
|
||||
if self.editor.read(cx).scroll_manager.is_dragging_scrollbar() {
|
||||
cx.on_mouse_event({
|
||||
let editor = self.editor.clone();
|
||||
move |event: &MouseUpEvent, phase, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.scroll_manager.set_is_dragging_scrollbar(false, cx);
|
||||
cx.stop_propagation();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cx.on_mouse_event({
|
||||
let editor = self.editor.clone();
|
||||
move |event: &MouseDownEvent, phase, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
if track_bounds.contains_point(&event.position) {
|
||||
editor.scroll_manager.set_is_dragging_scrollbar(true, cx);
|
||||
|
||||
// cx.scene().push_cursor_region(CursorRegion {
|
||||
// bounds: track_bounds,
|
||||
// style: CursorStyle::Arrow,
|
||||
// });
|
||||
// let region_id = cx.view_id();
|
||||
// cx.scene().push_mouse_region(
|
||||
// MouseRegion::new::<ScrollbarMouseHandlers>(region_id, region_id, track_bounds)
|
||||
// .on_move(move |event, editor: &mut Editor, cx| {
|
||||
// if event.pressed_button.is_none() {
|
||||
// editor.scroll_manager.show_scrollbar(cx);
|
||||
// }
|
||||
// })
|
||||
// .on_down(MouseButton::Left, {
|
||||
// let row_range = row_range.clone();
|
||||
// move |event, editor: &mut Editor, cx| {
|
||||
// let y = event.position.y;
|
||||
// if y < thumb_top || thumb_bottom < y {
|
||||
// let center_row = ((y - top) * max_row as f32 / height).round() as u32;
|
||||
// let top_row = center_row
|
||||
// .saturating_sub((row_range.end - row_range.start) as u32 / 2);
|
||||
// let mut position = editor.scroll_position(cx);
|
||||
// position.set_y(top_row as f32);
|
||||
// editor.set_scroll_position(position, cx);
|
||||
// } else {
|
||||
// editor.scroll_manager.show_scrollbar(cx);
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .on_drag(MouseButton::Left, {
|
||||
// move |event, editor: &mut Editor, cx| {
|
||||
// if event.end {
|
||||
// return;
|
||||
// }
|
||||
let y = event.position.y;
|
||||
if y < thumb_top || thumb_bottom < y {
|
||||
let center_row =
|
||||
((y - top) * max_row as f32 / height).round() as u32;
|
||||
let top_row = center_row
|
||||
.saturating_sub((row_range.end - row_range.start) as u32 / 2);
|
||||
let mut position = editor.scroll_position(cx);
|
||||
position.y = top_row as f32;
|
||||
editor.set_scroll_position(position, cx);
|
||||
} else {
|
||||
editor.scroll_manager.show_scrollbar(cx);
|
||||
}
|
||||
|
||||
// let y = event.prev_mouse_position.y;
|
||||
// let new_y = event.position.y;
|
||||
// if thumb_top < y && y < thumb_bottom {
|
||||
// let mut position = editor.scroll_position(cx);
|
||||
// position.set_y(position.y + (new_y - y) * (max_row as f32) / height);
|
||||
// if position.y < 0.0 {
|
||||
// position.set_y(0.);
|
||||
// }
|
||||
// editor.set_scroll_position(position, cx);
|
||||
// }
|
||||
// }
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
cx.stop_propagation();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn paint_highlighted_range(
|
||||
|
@ -2457,12 +2476,9 @@ impl EditorElement {
|
|||
return;
|
||||
}
|
||||
|
||||
let handled = editor.update(cx, |editor, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
Self::scroll(editor, event, &position_map, &interactive_bounds, cx)
|
||||
});
|
||||
if handled {
|
||||
cx.stop_propagation();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2476,7 +2492,7 @@ impl EditorElement {
|
|||
return;
|
||||
}
|
||||
|
||||
let handled = match event.button {
|
||||
match event.button {
|
||||
MouseButton::Left => editor.update(cx, |editor, cx| {
|
||||
Self::mouse_left_down(
|
||||
editor,
|
||||
|
@ -2491,12 +2507,8 @@ impl EditorElement {
|
|||
MouseButton::Right => editor.update(cx, |editor, cx| {
|
||||
Self::mouse_right_down(editor, event, &position_map, text_bounds, cx)
|
||||
}),
|
||||
_ => false,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if handled {
|
||||
cx.stop_propagation()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2506,7 +2518,7 @@ impl EditorElement {
|
|||
let stacking_order = cx.stacking_order().clone();
|
||||
|
||||
move |event: &MouseUpEvent, phase, cx| {
|
||||
let handled = editor.update(cx, |editor, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
Self::mouse_up(
|
||||
editor,
|
||||
event,
|
||||
|
@ -2516,10 +2528,6 @@ impl EditorElement {
|
|||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
if handled {
|
||||
cx.stop_propagation()
|
||||
}
|
||||
}
|
||||
});
|
||||
cx.on_mouse_event({
|
||||
|
@ -2532,7 +2540,7 @@ impl EditorElement {
|
|||
return;
|
||||
}
|
||||
|
||||
let stop_propogating = editor.update(cx, |editor, cx| {
|
||||
editor.update(cx, |editor, cx| {
|
||||
Self::mouse_moved(
|
||||
editor,
|
||||
event,
|
||||
|
@ -2543,10 +2551,6 @@ impl EditorElement {
|
|||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
if stop_propogating {
|
||||
cx.stop_propagation()
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2845,9 +2849,11 @@ impl Element for EditorElement {
|
|||
cx.with_z_index(1, |cx| {
|
||||
cx.with_element_id(Some("editor_blocks"), |cx| {
|
||||
self.paint_blocks(bounds, &mut layout, cx);
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
cx.with_z_index(2, |cx| self.paint_scrollbar(bounds, &mut layout, cx));
|
||||
});
|
||||
});
|
||||
})
|
||||
|
@ -2949,7 +2955,7 @@ impl PositionMap {
|
|||
) -> PointForPosition {
|
||||
let scroll_position = self.snapshot.scroll_position();
|
||||
let position = position - text_bounds.origin;
|
||||
let y = position.y.max(px(0.)).min(self.size.width);
|
||||
let y = position.y.max(px(0.)).min(self.size.height);
|
||||
let x = position.x + (scroll_position.x * self.em_width);
|
||||
let row = (f32::from(y / self.line_height) + scroll_position.y) as u32;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ pub const MIN_POPOVER_CHARACTER_WIDTH: f32 = 20.;
|
|||
pub const MIN_POPOVER_LINE_HEIGHT: Pixels = px(4.);
|
||||
pub const HOVER_POPOVER_GAP: Pixels = px(10.);
|
||||
|
||||
actions!(Hover);
|
||||
actions!(editor, [Hover]);
|
||||
|
||||
/// Bindable action which uses the most recent selection head to trigger a hover
|
||||
pub fn hover(editor: &mut Editor, _: &Hover, cx: &mut ViewContext<Editor>) {
|
||||
|
|
119
crates/editor2/src/rust_analyzer_ext.rs
Normal file
119
crates/editor2/src/rust_analyzer_ext.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use gpui::{Context, Model, View, ViewContext, VisualContext, WindowContext};
|
||||
use language::Language;
|
||||
use multi_buffer::MultiBuffer;
|
||||
use project::lsp_ext_command::ExpandMacro;
|
||||
use text::ToPointUtf16;
|
||||
|
||||
use crate::{element::register_action, Editor, ExpandMacroRecursively};
|
||||
|
||||
pub fn apply_related_actions(editor: &View<Editor>, cx: &mut WindowContext) {
|
||||
let is_rust_related = editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.all_buffers()
|
||||
.iter()
|
||||
.any(|b| match b.read(cx).language() {
|
||||
Some(l) => is_rust_language(l),
|
||||
None => false,
|
||||
})
|
||||
});
|
||||
|
||||
if is_rust_related {
|
||||
register_action(editor, cx, expand_macro_recursively);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_macro_recursively(
|
||||
editor: &mut Editor,
|
||||
_: &ExpandMacroRecursively,
|
||||
cx: &mut ViewContext<'_, Editor>,
|
||||
) {
|
||||
if editor.selections.count() == 0 {
|
||||
return;
|
||||
}
|
||||
let Some(project) = &editor.project else {
|
||||
return;
|
||||
};
|
||||
let Some(workspace) = editor.workspace() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let multibuffer = editor.buffer().read(cx);
|
||||
|
||||
let Some((trigger_anchor, rust_language, server_to_query, buffer)) = editor
|
||||
.selections
|
||||
.disjoint_anchors()
|
||||
.into_iter()
|
||||
.filter(|selection| selection.start == selection.end)
|
||||
.filter_map(|selection| Some((selection.start.buffer_id?, selection.start)))
|
||||
.filter_map(|(buffer_id, trigger_anchor)| {
|
||||
let buffer = multibuffer.buffer(buffer_id)?;
|
||||
let rust_language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
|
||||
if !is_rust_language(&rust_language) {
|
||||
return None;
|
||||
}
|
||||
Some((trigger_anchor, rust_language, buffer))
|
||||
})
|
||||
.find_map(|(trigger_anchor, rust_language, buffer)| {
|
||||
project
|
||||
.read(cx)
|
||||
.language_servers_for_buffer(buffer.read(cx), cx)
|
||||
.into_iter()
|
||||
.find_map(|(adapter, server)| {
|
||||
if adapter.name.0.as_ref() == "rust-analyzer" {
|
||||
Some((
|
||||
trigger_anchor,
|
||||
Arc::clone(&rust_language),
|
||||
server.server_id(),
|
||||
buffer.clone(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let project = project.clone();
|
||||
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||
let position = trigger_anchor.text_anchor.to_point_utf16(&buffer_snapshot);
|
||||
let expand_macro_task = project.update(cx, |project, cx| {
|
||||
project.request_lsp(
|
||||
buffer,
|
||||
project::LanguageServerToQuery::Other(server_to_query),
|
||||
ExpandMacro { position },
|
||||
cx,
|
||||
)
|
||||
});
|
||||
cx.spawn(|editor, mut cx| async move {
|
||||
let macro_expansion = expand_macro_task.await.context("expand macro")?;
|
||||
if macro_expansion.is_empty() {
|
||||
log::info!("Empty macro expansion for position {position:?}");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buffer = project.update(&mut cx, |project, cx| {
|
||||
project.create_buffer(¯o_expansion.expansion, Some(rust_language), cx)
|
||||
})??;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let buffer = cx.build_model(|cx| {
|
||||
MultiBuffer::singleton(buffer, cx).with_title(macro_expansion.name)
|
||||
});
|
||||
workspace.add_item(
|
||||
Box::new(cx.build_view(|cx| Editor::for_multibuffer(buffer, Some(project), cx))),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
fn is_rust_language(language: &Language) -> bool {
|
||||
language.name().as_ref() == "Rust"
|
||||
}
|
|
@ -136,6 +136,7 @@ pub struct ScrollManager {
|
|||
last_autoscroll: Option<(gpui::Point<f32>, f32, f32, AutoscrollStrategy)>,
|
||||
show_scrollbars: bool,
|
||||
hide_scrollbar_task: Option<Task<()>>,
|
||||
dragging_scrollbar: bool,
|
||||
visible_line_count: Option<f32>,
|
||||
}
|
||||
|
||||
|
@ -148,6 +149,7 @@ impl ScrollManager {
|
|||
autoscroll_request: None,
|
||||
show_scrollbars: true,
|
||||
hide_scrollbar_task: None,
|
||||
dragging_scrollbar: false,
|
||||
last_autoscroll: None,
|
||||
visible_line_count: None,
|
||||
}
|
||||
|
@ -278,6 +280,17 @@ impl ScrollManager {
|
|||
self.autoscroll_request.is_some()
|
||||
}
|
||||
|
||||
pub fn is_dragging_scrollbar(&self) -> bool {
|
||||
self.dragging_scrollbar
|
||||
}
|
||||
|
||||
pub fn set_is_dragging_scrollbar(&mut self, dragging: bool, cx: &mut ViewContext<Editor>) {
|
||||
if dragging != self.dragging_scrollbar {
|
||||
self.dragging_scrollbar = dragging;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
|
||||
if max < self.anchor.offset.x {
|
||||
self.anchor.offset.x = max;
|
||||
|
|
|
@ -32,7 +32,7 @@ impl Render for DeployFeedbackButton {
|
|||
IconButton::new("give-feedback", Icon::Envelope)
|
||||
.style(ui::ButtonStyle::Subtle)
|
||||
.selected(is_open)
|
||||
.tooltip(|cx| Tooltip::text("Give Feedback", cx))
|
||||
.tooltip(|cx| Tooltip::text("Share Feedback", cx))
|
||||
.on_click(|_, cx| {
|
||||
cx.dispatch_action(Box::new(GiveFeedback));
|
||||
})
|
||||
|
|
|
@ -5,15 +5,18 @@ use workspace::Workspace;
|
|||
pub mod deploy_feedback_button;
|
||||
pub mod feedback_modal;
|
||||
|
||||
actions!(GiveFeedback, SubmitFeedback);
|
||||
actions!(feedback, [GiveFeedback, SubmitFeedback]);
|
||||
|
||||
mod system_specs;
|
||||
|
||||
actions!(
|
||||
CopySystemSpecsIntoClipboard,
|
||||
FileBugReport,
|
||||
RequestFeature,
|
||||
OpenZedCommunityRepo
|
||||
zed,
|
||||
[
|
||||
CopySystemSpecsIntoClipboard,
|
||||
FileBugReport,
|
||||
RequestFeature,
|
||||
OpenZedCommunityRepo
|
||||
]
|
||||
);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
|
|
|
@ -1,28 +1,38 @@
|
|||
use std::{ops::RangeInclusive, sync::Arc};
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::{anyhow, bail};
|
||||
use client::{Client, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::{Editor, EditorEvent};
|
||||
use futures::AsyncReadExt;
|
||||
use gpui::{
|
||||
div, red, rems, serde_json, AppContext, DismissEvent, Div, EventEmitter, FocusHandle,
|
||||
FocusableView, Model, PromptLevel, Render, Task, View, ViewContext,
|
||||
div, rems, serde_json, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView,
|
||||
Model, PromptLevel, Render, Task, View, ViewContext,
|
||||
};
|
||||
use isahc::Request;
|
||||
use language::Buffer;
|
||||
use project::Project;
|
||||
use regex::Regex;
|
||||
use serde_derive::Serialize;
|
||||
use ui::{prelude::*, Button, ButtonStyle, Label, Tooltip};
|
||||
use ui::{prelude::*, Button, ButtonStyle, IconPosition, Tooltip};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
use crate::{system_specs::SystemSpecs, GiveFeedback, OpenZedCommunityRepo};
|
||||
|
||||
// For UI testing purposes
|
||||
const SEND_SUCCESS_IN_DEV_MODE: bool = true;
|
||||
|
||||
// Temporary, until tests are in place
|
||||
#[cfg(debug_assertions)]
|
||||
const DEV_MODE: bool = true;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
const DEV_MODE: bool = false;
|
||||
|
||||
const DATABASE_KEY_NAME: &str = "email_address";
|
||||
const EMAIL_REGEX: &str = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b";
|
||||
const FEEDBACK_CHAR_LIMIT: RangeInclusive<usize> = 10..=5000;
|
||||
const FEEDBACK_CHAR_LIMIT: RangeInclusive<i32> = 10..=5000;
|
||||
const FEEDBACK_SUBMISSION_ERROR_TEXT: &str =
|
||||
"Feedback failed to submit, see error log for details.";
|
||||
|
||||
|
@ -41,8 +51,9 @@ pub struct FeedbackModal {
|
|||
system_specs: SystemSpecs,
|
||||
feedback_editor: View<Editor>,
|
||||
email_address_editor: View<Editor>,
|
||||
character_count: usize,
|
||||
pending_submission: bool,
|
||||
awaiting_submission: bool,
|
||||
user_submitted: bool,
|
||||
character_count: i32,
|
||||
}
|
||||
|
||||
impl FocusableView for FeedbackModal {
|
||||
|
@ -52,6 +63,25 @@ impl FocusableView for FeedbackModal {
|
|||
}
|
||||
impl EventEmitter<DismissEvent> for FeedbackModal {}
|
||||
|
||||
impl ModalView for FeedbackModal {
|
||||
fn dismiss(&mut self, cx: &mut ViewContext<Self>) -> Task<bool> {
|
||||
if self.user_submitted {
|
||||
self.set_user_submitted(false, cx);
|
||||
return cx.spawn(|_, _| async { true });
|
||||
}
|
||||
|
||||
let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some();
|
||||
|
||||
if !has_feedback {
|
||||
return cx.spawn(|_, _| async { true });
|
||||
}
|
||||
|
||||
let answer = cx.prompt(PromptLevel::Info, "Discard feedback?", &["Yes", "No"]);
|
||||
|
||||
cx.spawn(|_, _| async { answer.await.ok() == Some(0) })
|
||||
}
|
||||
}
|
||||
|
||||
impl FeedbackModal {
|
||||
pub fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||
let _handle = cx.view().downgrade();
|
||||
|
@ -104,6 +134,11 @@ impl FeedbackModal {
|
|||
|
||||
let feedback_editor = cx.build_view(|cx| {
|
||||
let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx);
|
||||
editor.set_placeholder_text(
|
||||
"You can use markdown to organize your feedback wiht add code and links, or organize feedback.",
|
||||
cx,
|
||||
);
|
||||
// editor.set_show_gutter(false, cx);
|
||||
editor.set_vertical_scroll_margin(5, cx);
|
||||
editor
|
||||
});
|
||||
|
@ -119,7 +154,7 @@ impl FeedbackModal {
|
|||
.as_singleton()
|
||||
.expect("Feedback editor is never a multi-buffer")
|
||||
.read(cx)
|
||||
.len();
|
||||
.len() as i32;
|
||||
cx.notify();
|
||||
}
|
||||
_ => {}
|
||||
|
@ -131,7 +166,8 @@ impl FeedbackModal {
|
|||
system_specs: system_specs.clone(),
|
||||
feedback_editor,
|
||||
email_address_editor,
|
||||
pending_submission: false,
|
||||
awaiting_submission: false,
|
||||
user_submitted: false,
|
||||
character_count: 0,
|
||||
}
|
||||
}
|
||||
|
@ -163,37 +199,53 @@ impl FeedbackModal {
|
|||
}
|
||||
};
|
||||
|
||||
this.update(&mut cx, |feedback_editor, cx| {
|
||||
feedback_editor.set_pending_submission(true, cx);
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.set_awaiting_submission(true, cx);
|
||||
})
|
||||
.log_err();
|
||||
|
||||
if let Err(error) =
|
||||
FeedbackModal::submit_feedback(&feedback_text, email, client, specs).await
|
||||
{
|
||||
log::error!("{}", error);
|
||||
this.update(&mut cx, |feedback_editor, cx| {
|
||||
let prompt = cx.prompt(
|
||||
PromptLevel::Critical,
|
||||
FEEDBACK_SUBMISSION_ERROR_TEXT,
|
||||
&["OK"],
|
||||
);
|
||||
cx.spawn(|_, _cx| async move {
|
||||
prompt.await.ok();
|
||||
let res =
|
||||
FeedbackModal::submit_feedback(&feedback_text, email, client, specs).await;
|
||||
|
||||
match res {
|
||||
Ok(_) => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.set_user_submitted(true, cx);
|
||||
cx.emit(DismissEvent)
|
||||
})
|
||||
.detach();
|
||||
feedback_editor.set_pending_submission(false, cx);
|
||||
})
|
||||
.log_err();
|
||||
.ok();
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!("{}", error);
|
||||
this.update(&mut cx, |this, cx| {
|
||||
let prompt = cx.prompt(
|
||||
PromptLevel::Critical,
|
||||
FEEDBACK_SUBMISSION_ERROR_TEXT,
|
||||
&["OK"],
|
||||
);
|
||||
cx.spawn(|_, _cx| async move {
|
||||
prompt.await.ok();
|
||||
})
|
||||
.detach();
|
||||
this.set_awaiting_submission(false, cx);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
|
||||
fn set_pending_submission(&mut self, pending_submission: bool, cx: &mut ViewContext<Self>) {
|
||||
self.pending_submission = pending_submission;
|
||||
fn set_awaiting_submission(&mut self, awaiting_submission: bool, cx: &mut ViewContext<Self>) {
|
||||
self.awaiting_submission = awaiting_submission;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn set_user_submitted(&mut self, user_submitted: bool, cx: &mut ViewContext<Self>) {
|
||||
self.user_submitted = user_submitted;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
|
@ -203,6 +255,14 @@ impl FeedbackModal {
|
|||
zed_client: Arc<Client>,
|
||||
system_specs: SystemSpecs,
|
||||
) -> anyhow::Result<()> {
|
||||
if DEV_MODE {
|
||||
if SEND_SUCCESS_IN_DEV_MODE {
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(anyhow!("Error submitting feedback"));
|
||||
}
|
||||
}
|
||||
|
||||
let feedback_endpoint = format!("{}/api/feedback", *ZED_SERVER_URL);
|
||||
let telemetry = zed_client.telemetry();
|
||||
let metrics_id = telemetry.metrics_id();
|
||||
|
@ -233,11 +293,8 @@ impl FeedbackModal {
|
|||
}
|
||||
|
||||
// TODO: Escape button calls dismiss
|
||||
// TODO: Should do same as hitting cancel / clicking outside of modal
|
||||
// Close immediately if no text in field
|
||||
// Ask to close if text in the field
|
||||
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
|
||||
cx.emit(DismissEvent);
|
||||
cx.emit(DismissEvent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,136 +308,128 @@ impl Render for FeedbackModal {
|
|||
};
|
||||
|
||||
let valid_character_count = FEEDBACK_CHAR_LIMIT.contains(&self.character_count);
|
||||
let characters_remaining =
|
||||
if valid_character_count || self.character_count > *FEEDBACK_CHAR_LIMIT.end() {
|
||||
*FEEDBACK_CHAR_LIMIT.end() as i32 - self.character_count as i32
|
||||
} else {
|
||||
self.character_count as i32 - *FEEDBACK_CHAR_LIMIT.start() as i32
|
||||
};
|
||||
|
||||
let allow_submission =
|
||||
valid_character_count && valid_email_address && !self.pending_submission;
|
||||
valid_character_count && valid_email_address && !self.awaiting_submission;
|
||||
|
||||
let has_feedback = self.feedback_editor.read(cx).text_option(cx).is_some();
|
||||
|
||||
let submit_button_text = if self.pending_submission {
|
||||
"Sending..."
|
||||
let submit_button_text = if self.awaiting_submission {
|
||||
"Submitting..."
|
||||
} else {
|
||||
"Send Feedback"
|
||||
"Submit"
|
||||
};
|
||||
let dismiss = cx.listener(|_, _, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
});
|
||||
// TODO: get the "are you sure you want to dismiss?" prompt here working
|
||||
let dismiss_prompt = cx.listener(|_, _, _| {
|
||||
// let answer = cx.prompt(PromptLevel::Info, "Exit feedback?", &["Yes", "No"]);
|
||||
// cx.spawn(|_, _| async move {
|
||||
// let answer = answer.await.ok();
|
||||
// if answer == Some(0) {
|
||||
// cx.emit(DismissEvent);
|
||||
// }
|
||||
// })
|
||||
// .detach();
|
||||
});
|
||||
|
||||
let open_community_repo =
|
||||
cx.listener(|_, _, cx| cx.dispatch_action(Box::new(OpenZedCommunityRepo)));
|
||||
|
||||
// TODO: Nate UI pass
|
||||
// Moved this here because providing it inline breaks rustfmt
|
||||
let provide_an_email_address =
|
||||
"Provide an email address if you want us to be able to reply.";
|
||||
|
||||
v_stack()
|
||||
.elevation_3(cx)
|
||||
.key_context("GiveFeedback")
|
||||
.on_action(cx.listener(Self::cancel))
|
||||
.min_w(rems(40.))
|
||||
.max_w(rems(96.))
|
||||
.border()
|
||||
.border_color(red())
|
||||
.h(rems(40.))
|
||||
.p_2()
|
||||
.gap_2()
|
||||
.h(rems(32.))
|
||||
.p_4()
|
||||
.gap_4()
|
||||
.child(v_stack().child(
|
||||
// TODO: Add Headline component to `ui2`
|
||||
div().text_xl().child("Share Feedback"),
|
||||
))
|
||||
.child(
|
||||
v_stack().child(
|
||||
div()
|
||||
.size_full()
|
||||
.child(Label::new("Give Feedback").color(Color::Default))
|
||||
.child(Label::new("This editor supports markdown").color(Color::Muted)),
|
||||
),
|
||||
Label::new(if self.character_count < *FEEDBACK_CHAR_LIMIT.start() {
|
||||
format!(
|
||||
"Feedback must be at least {} characters.",
|
||||
FEEDBACK_CHAR_LIMIT.start()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Characters: {}",
|
||||
*FEEDBACK_CHAR_LIMIT.end() - self.character_count
|
||||
)
|
||||
})
|
||||
.color(if valid_character_count {
|
||||
Color::Success
|
||||
} else {
|
||||
Color::Error
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.p_2()
|
||||
.border()
|
||||
.rounded_md()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(self.feedback_editor.clone()),
|
||||
)
|
||||
.child(
|
||||
div().child(
|
||||
Label::new(format!(
|
||||
"Characters: {}",
|
||||
characters_remaining
|
||||
))
|
||||
.color(
|
||||
if valid_character_count {
|
||||
Color::Success
|
||||
} else {
|
||||
Color::Error
|
||||
}
|
||||
)
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.border()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(self.email_address_editor.clone())
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.gap_1()
|
||||
.child(Button::new("community_repo", "Community Repo")
|
||||
.style(ButtonStyle::Filled)
|
||||
.color(Color::Muted)
|
||||
.on_click(open_community_repo)
|
||||
.child(
|
||||
h_stack()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.p_2()
|
||||
.border()
|
||||
.rounded_md()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.child(self.email_address_editor.clone()),
|
||||
)
|
||||
.child(h_stack().justify_between().gap_1()
|
||||
.child(
|
||||
Button::new("cancel_feedback", "Cancel")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.color(Color::Muted)
|
||||
// TODO: replicate this logic when clicking outside the modal
|
||||
// TODO: Will require somehow overriding the modal dismal default behavior
|
||||
.map(|this| {
|
||||
if has_feedback {
|
||||
this.on_click(dismiss_prompt)
|
||||
} else {
|
||||
this.on_click(dismiss)
|
||||
}
|
||||
})
|
||||
)
|
||||
.child(
|
||||
Button::new("send_feedback", submit_button_text)
|
||||
.color(Color::Accent)
|
||||
.style(ButtonStyle::Filled)
|
||||
// TODO: Ensure that while submitting, "Sending..." is shown and disable the button
|
||||
// TODO: If submit errors: show popup with error, don't close modal, set text back to "Send Feedback", and re-enable button
|
||||
// TODO: If submit is successful, close the modal
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
let _ = this.submit(cx);
|
||||
}))
|
||||
.tooltip(|cx| {
|
||||
Tooltip::with_meta(
|
||||
"Submit feedback to the Zed team.",
|
||||
None,
|
||||
"Provide an email address if you want us to be able to reply.",
|
||||
cx,
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.gap_1()
|
||||
.child(
|
||||
Button::new("community_repo", "Community Repo")
|
||||
.style(ButtonStyle::Transparent)
|
||||
.icon(Icon::ExternalLink)
|
||||
.icon_position(IconPosition::End)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(open_community_repo),
|
||||
)
|
||||
.child(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.child(
|
||||
Button::new("cancel_feedback", "Cancel")
|
||||
.style(ButtonStyle::Subtle)
|
||||
.color(Color::Muted)
|
||||
.on_click(cx.listener(move |_, _, cx| {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
this.update(&mut cx, |_, cx| {
|
||||
cx.emit(DismissEvent)
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
})),
|
||||
)
|
||||
})
|
||||
.when(!allow_submission, |this| this.disabled(true))
|
||||
),
|
||||
)
|
||||
|
||||
.child(
|
||||
Button::new("send_feedback", submit_button_text)
|
||||
.color(Color::Accent)
|
||||
.style(ButtonStyle::Filled)
|
||||
// TODO: Ensure that while submitting, "Sending..." is shown and disable the button
|
||||
// TODO: If submit errors: show popup with error, don't close modal, set text back to "Submit", and re-enable button
|
||||
.on_click(cx.listener(|this, _, cx| {
|
||||
this.submit(cx).detach();
|
||||
}))
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::with_meta(
|
||||
"Submit feedback to the Zed team.",
|
||||
None,
|
||||
provide_an_email_address,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.when(!allow_submission, |this| this.disabled(true)),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Maybe store email address whenever the modal is closed, versus just on submit, so users can remove it if they want without submitting
|
||||
// TODO: Testing of various button states, dismissal prompts, etc.
|
||||
|
|
|
@ -17,9 +17,11 @@ use std::{
|
|||
use text::Point;
|
||||
use ui::{prelude::*, HighlightedLabel, ListItem};
|
||||
use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
|
||||
use workspace::Workspace;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
actions!(Toggle);
|
||||
actions!(file_finder, [Toggle]);
|
||||
|
||||
impl ModalView for FileFinder {}
|
||||
|
||||
pub struct FileFinder {
|
||||
picker: View<Picker<FileFinderDelegate>>,
|
||||
|
|
|
@ -8,8 +8,9 @@ use text::{Bias, Point};
|
|||
use theme::ActiveTheme;
|
||||
use ui::{h_stack, prelude::*, v_stack, Label};
|
||||
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||
use workspace::ModalView;
|
||||
|
||||
actions!(Toggle);
|
||||
actions!(go_to_line, [Toggle]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.observe_new_views(GoToLine::register).detach();
|
||||
|
@ -23,6 +24,8 @@ pub struct GoToLine {
|
|||
_subscriptions: Vec<Subscription>,
|
||||
}
|
||||
|
||||
impl ModalView for GoToLine {}
|
||||
|
||||
impl FocusableView for GoToLine {
|
||||
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||
self.line_editor.focus_handle(cx)
|
||||
|
|
|
@ -22,7 +22,7 @@ Actions are frequently unit structs, for which we have a macro. The above could
|
|||
|
||||
```rust
|
||||
mod menu {
|
||||
actions!(MoveUp, MoveDown);
|
||||
actions!(gpui, [MoveUp, MoveDown]);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -3,34 +3,33 @@ use anyhow::{anyhow, Context, Result};
|
|||
use collections::HashMap;
|
||||
pub use no_action::NoAction;
|
||||
use serde_json::json;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
ops::Deref,
|
||||
};
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
/// Actions are used to implement keyboard-driven UI.
|
||||
/// When you declare an action, you can bind keys to the action in the keymap and
|
||||
/// listeners for that action in the element tree.
|
||||
///
|
||||
/// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit struct
|
||||
/// action for each listed action name.
|
||||
/// action for each listed action name in the given namespace.
|
||||
/// ```rust
|
||||
/// actions!(MoveUp, MoveDown, MoveLeft, MoveRight, Newline);
|
||||
/// actions!(editor, [MoveUp, MoveDown, MoveLeft, MoveRight, Newline]);
|
||||
/// ```
|
||||
/// More complex data types can also be actions. If you annotate your type with the action derive macro
|
||||
/// it will be implemented and registered automatically.
|
||||
/// More complex data types can also be actions, providing they implement Clone, PartialEq,
|
||||
/// and serde_derive::Deserialize.
|
||||
/// Use `impl_actions!` to automatically implement the action in the given namespace.
|
||||
/// ```
|
||||
/// #[derive(Clone, PartialEq, serde_derive::Deserialize, Action)]
|
||||
/// #[derive(Clone, PartialEq, serde_derive::Deserialize)]
|
||||
/// pub struct SelectNext {
|
||||
/// pub replace_newest: bool,
|
||||
/// }
|
||||
/// impl_actions!(editor, [SelectNext]);
|
||||
/// ```
|
||||
///
|
||||
/// If you want to control the behavior of the action trait manually, you can use the lower-level `#[register_action]`
|
||||
/// macro, which only generates the code needed to register your action before `main`.
|
||||
///
|
||||
/// ```
|
||||
/// #[gpui::register_action]
|
||||
/// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::fmt::Debug)]
|
||||
/// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone)]
|
||||
/// pub struct Paste {
|
||||
/// pub content: SharedString,
|
||||
/// }
|
||||
|
@ -38,6 +37,7 @@ use std::{
|
|||
/// impl gpui::Action for Paste {
|
||||
/// ///...
|
||||
/// }
|
||||
/// register_action!(Paste);
|
||||
/// ```
|
||||
pub trait Action: 'static {
|
||||
fn boxed_clone(&self) -> Box<dyn Action>;
|
||||
|
@ -56,7 +56,7 @@ pub trait Action: 'static {
|
|||
impl std::fmt::Debug for dyn Action {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("dyn Action")
|
||||
.field("type_name", &self.name())
|
||||
.field("name", &self.name())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ impl ActionRegistry {
|
|||
for builder in __GPUI_ACTIONS {
|
||||
let action = builder();
|
||||
//todo(remove)
|
||||
let name: SharedString = remove_the_2(action.name).into();
|
||||
let name: SharedString = action.name.into();
|
||||
self.builders_by_name.insert(name.clone(), action.build);
|
||||
self.names_by_type_id.insert(action.type_id, name.clone());
|
||||
self.all_names.push(name);
|
||||
|
@ -139,11 +139,9 @@ impl ActionRegistry {
|
|||
name: &str,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<Box<dyn Action>> {
|
||||
//todo(remove)
|
||||
let name = remove_the_2(name);
|
||||
let build_action = self
|
||||
.builders_by_name
|
||||
.get(name.deref())
|
||||
.get(name)
|
||||
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
|
||||
(build_action)(params.unwrap_or_else(|| json!({})))
|
||||
.with_context(|| format!("Attempting to build action {}", name))
|
||||
|
@ -155,36 +153,88 @@ impl ActionRegistry {
|
|||
}
|
||||
|
||||
/// Defines unit structs that can be used as actions.
|
||||
/// To use more complex data types as actions, annotate your type with the #[action] macro.
|
||||
/// To use more complex data types as actions, use `impl_actions!`
|
||||
#[macro_export]
|
||||
macro_rules! actions {
|
||||
() => {};
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
$(
|
||||
#[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, gpui::serde_derive::Deserialize)]
|
||||
#[serde(crate = "gpui::serde")]
|
||||
pub struct $name;
|
||||
|
||||
( $name:ident ) => {
|
||||
#[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, gpui::serde_derive::Deserialize, gpui::Action)]
|
||||
#[serde(crate = "gpui::serde")]
|
||||
pub struct $name;
|
||||
};
|
||||
gpui::__impl_action!($namespace, $name,
|
||||
fn build(_: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
||||
Ok(Box::new(Self))
|
||||
}
|
||||
);
|
||||
|
||||
( $name:ident, $($rest:tt)* ) => {
|
||||
actions!($name);
|
||||
actions!($($rest)*);
|
||||
gpui::register_action!($name);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
//todo!(remove)
|
||||
pub fn remove_the_2(action_name: &str) -> String {
|
||||
let mut separator_matches = action_name.rmatch_indices("::");
|
||||
separator_matches.next().unwrap();
|
||||
let name_start_ix = separator_matches.next().map_or(0, |(ix, _)| ix + 2);
|
||||
// todo!() remove the 2 replacement when migration is done
|
||||
action_name[name_start_ix..]
|
||||
.replace("2::", "::")
|
||||
.to_string()
|
||||
/// Implements the Action trait for any struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize
|
||||
#[macro_export]
|
||||
macro_rules! impl_actions {
|
||||
($namespace:path, [ $($name:ident),* $(,)? ]) => {
|
||||
$(
|
||||
gpui::__impl_action!($namespace, $name,
|
||||
fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>> {
|
||||
Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
|
||||
}
|
||||
);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! __impl_action {
|
||||
($namespace:path, $name:ident, $build:item) => {
|
||||
impl gpui::Action for $name {
|
||||
fn name(&self) -> &'static str
|
||||
{
|
||||
concat!(
|
||||
stringify!($namespace),
|
||||
"::",
|
||||
stringify!($name),
|
||||
)
|
||||
}
|
||||
|
||||
// todo!() why is this needed in addition to name?
|
||||
fn debug_name() -> &'static str
|
||||
where
|
||||
Self: ::std::marker::Sized
|
||||
{
|
||||
concat!(
|
||||
stringify!($namespace),
|
||||
"::",
|
||||
stringify!($name),
|
||||
)
|
||||
}
|
||||
|
||||
$build
|
||||
|
||||
fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
|
||||
action
|
||||
.as_any()
|
||||
.downcast_ref::<Self>()
|
||||
.map_or(false, |a| self == a)
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> std::boxed::Box<dyn gpui::Action> {
|
||||
::std::boxed::Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn ::std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod no_action {
|
||||
use crate as gpui;
|
||||
|
||||
actions!(NoAction);
|
||||
actions!(zed, [NoAction]);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use smallvec::SmallVec;
|
|||
use smol::future::FutureExt;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test_context::*;
|
||||
use time::UtcOffset;
|
||||
|
||||
use crate::{
|
||||
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
|
||||
|
@ -536,6 +537,10 @@ impl AppContext {
|
|||
self.platform.restart()
|
||||
}
|
||||
|
||||
pub fn local_timezone(&self) -> UtcOffset {
|
||||
self.platform.local_timezone()
|
||||
}
|
||||
|
||||
pub(crate) fn push_effect(&mut self, effect: Effect) {
|
||||
match &effect {
|
||||
Effect::Notify { emitter } => {
|
||||
|
|
496
crates/gpui2/src/elements/list.rs
Normal file
496
crates/gpui2/src/elements/list.rs
Normal file
|
@ -0,0 +1,496 @@
|
|||
use crate::{
|
||||
px, AnyElement, AvailableSpace, BorrowAppContext, DispatchPhase, Element, IntoElement, Pixels,
|
||||
Point, ScrollWheelEvent, Size, Style, StyleRefinement, Styled, WindowContext,
|
||||
};
|
||||
use collections::VecDeque;
|
||||
use refineable::Refineable as _;
|
||||
use std::{cell::RefCell, ops::Range, rc::Rc};
|
||||
use sum_tree::{Bias, SumTree};
|
||||
|
||||
pub fn list(state: ListState) -> List {
|
||||
List {
|
||||
state,
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct List {
|
||||
state: ListState,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ListState(Rc<RefCell<StateInner>>);
|
||||
|
||||
struct StateInner {
|
||||
last_layout_width: Option<Pixels>,
|
||||
render_item: Box<dyn FnMut(usize, &mut WindowContext) -> AnyElement>,
|
||||
items: SumTree<ListItem>,
|
||||
logical_scroll_top: Option<ListOffset>,
|
||||
alignment: ListAlignment,
|
||||
overdraw: Pixels,
|
||||
#[allow(clippy::type_complexity)]
|
||||
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ListAlignment {
|
||||
Top,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
pub struct ListScrollEvent {
|
||||
pub visible_range: Range<usize>,
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ListItem {
|
||||
Unrendered,
|
||||
Rendered { height: Pixels },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
struct ListItemSummary {
|
||||
count: usize,
|
||||
rendered_count: usize,
|
||||
unrendered_count: usize,
|
||||
height: Pixels,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Count(usize);
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct RenderedCount(usize);
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct UnrenderedCount(usize);
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct Height(Pixels);
|
||||
|
||||
impl ListState {
|
||||
pub fn new<F>(
|
||||
element_count: usize,
|
||||
orientation: ListAlignment,
|
||||
overdraw: Pixels,
|
||||
render_item: F,
|
||||
) -> Self
|
||||
where
|
||||
F: 'static + FnMut(usize, &mut WindowContext) -> AnyElement,
|
||||
{
|
||||
let mut items = SumTree::new();
|
||||
items.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
||||
Self(Rc::new(RefCell::new(StateInner {
|
||||
last_layout_width: None,
|
||||
render_item: Box::new(render_item),
|
||||
items,
|
||||
logical_scroll_top: None,
|
||||
alignment: orientation,
|
||||
overdraw,
|
||||
scroll_handler: None,
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn reset(&self, element_count: usize) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
state.logical_scroll_top = None;
|
||||
state.items = SumTree::new();
|
||||
state
|
||||
.items
|
||||
.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
||||
}
|
||||
|
||||
pub fn item_count(&self) -> usize {
|
||||
self.0.borrow().items.summary().count
|
||||
}
|
||||
|
||||
pub fn splice(&self, old_range: Range<usize>, count: usize) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
if let Some(ListOffset {
|
||||
item_ix,
|
||||
offset_in_item,
|
||||
}) = state.logical_scroll_top.as_mut()
|
||||
{
|
||||
if old_range.contains(item_ix) {
|
||||
*item_ix = old_range.start;
|
||||
*offset_in_item = px(0.);
|
||||
} else if old_range.end <= *item_ix {
|
||||
*item_ix = *item_ix - (old_range.end - old_range.start) + count;
|
||||
}
|
||||
}
|
||||
|
||||
let mut old_heights = state.items.cursor::<Count>();
|
||||
let mut new_heights = old_heights.slice(&Count(old_range.start), Bias::Right, &());
|
||||
old_heights.seek_forward(&Count(old_range.end), Bias::Right, &());
|
||||
|
||||
new_heights.extend((0..count).map(|_| ListItem::Unrendered), &());
|
||||
new_heights.append(old_heights.suffix(&()), &());
|
||||
drop(old_heights);
|
||||
state.items = new_heights;
|
||||
}
|
||||
|
||||
pub fn set_scroll_handler(
|
||||
&self,
|
||||
handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
|
||||
) {
|
||||
self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
|
||||
}
|
||||
|
||||
pub fn logical_scroll_top(&self) -> ListOffset {
|
||||
self.0.borrow().logical_scroll_top()
|
||||
}
|
||||
|
||||
pub fn scroll_to(&self, mut scroll_top: ListOffset) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
let item_count = state.items.summary().count;
|
||||
if scroll_top.item_ix >= item_count {
|
||||
scroll_top.item_ix = item_count;
|
||||
scroll_top.offset_in_item = px(0.);
|
||||
}
|
||||
state.logical_scroll_top = Some(scroll_top);
|
||||
}
|
||||
}
|
||||
|
||||
impl StateInner {
|
||||
fn visible_range(&self, height: Pixels, scroll_top: &ListOffset) -> Range<usize> {
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
let start_y = cursor.start().height + scroll_top.offset_in_item;
|
||||
cursor.seek_forward(&Height(start_y + height), Bias::Left, &());
|
||||
scroll_top.item_ix..cursor.start().count + 1
|
||||
}
|
||||
|
||||
fn scroll(
|
||||
&mut self,
|
||||
scroll_top: &ListOffset,
|
||||
height: Pixels,
|
||||
delta: Point<Pixels>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
let scroll_max = (self.items.summary().height - height).max(px(0.));
|
||||
let new_scroll_top = (self.scroll_top(scroll_top) - delta.y)
|
||||
.max(px(0.))
|
||||
.min(scroll_max);
|
||||
|
||||
if self.alignment == ListAlignment::Bottom && new_scroll_top == scroll_max {
|
||||
self.logical_scroll_top = None;
|
||||
} else {
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Height(new_scroll_top), Bias::Right, &());
|
||||
let item_ix = cursor.start().count;
|
||||
let offset_in_item = new_scroll_top - cursor.start().height;
|
||||
self.logical_scroll_top = Some(ListOffset {
|
||||
item_ix,
|
||||
offset_in_item,
|
||||
});
|
||||
}
|
||||
|
||||
if self.scroll_handler.is_some() {
|
||||
let visible_range = self.visible_range(height, scroll_top);
|
||||
self.scroll_handler.as_mut().unwrap()(
|
||||
&ListScrollEvent {
|
||||
visible_range,
|
||||
count: self.items.summary().count,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn logical_scroll_top(&self) -> ListOffset {
|
||||
self.logical_scroll_top
|
||||
.unwrap_or_else(|| match self.alignment {
|
||||
ListAlignment::Top => ListOffset {
|
||||
item_ix: 0,
|
||||
offset_in_item: px(0.),
|
||||
},
|
||||
ListAlignment::Bottom => ListOffset {
|
||||
item_ix: self.items.summary().count,
|
||||
offset_in_item: px(0.),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn scroll_top(&self, logical_scroll_top: &ListOffset) -> Pixels {
|
||||
let mut cursor = self.items.cursor::<ListItemSummary>();
|
||||
cursor.seek(&Count(logical_scroll_top.item_ix), Bias::Right, &());
|
||||
cursor.start().height + logical_scroll_top.offset_in_item
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ListItem {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Unrendered => write!(f, "Unrendered"),
|
||||
Self::Rendered { height, .. } => {
|
||||
f.debug_struct("Rendered").field("height", height).finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ListOffset {
|
||||
pub item_ix: usize,
|
||||
pub offset_in_item: Pixels,
|
||||
}
|
||||
|
||||
impl Element for List {
|
||||
type State = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_state: Option<Self::State>,
|
||||
cx: &mut crate::WindowContext,
|
||||
) -> (crate::LayoutId, Self::State) {
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
let layout_id = cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.request_layout(&style, None)
|
||||
});
|
||||
(layout_id, ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
self,
|
||||
bounds: crate::Bounds<crate::Pixels>,
|
||||
_state: &mut Self::State,
|
||||
cx: &mut crate::WindowContext,
|
||||
) {
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
|
||||
// If the width of the list has changed, invalidate all cached item heights
|
||||
if state.last_layout_width != Some(bounds.size.width) {
|
||||
state.items = SumTree::from_iter(
|
||||
(0..state.items.summary().count).map(|_| ListItem::Unrendered),
|
||||
&(),
|
||||
)
|
||||
}
|
||||
|
||||
let old_items = state.items.clone();
|
||||
let mut measured_items = VecDeque::new();
|
||||
let mut item_elements = VecDeque::new();
|
||||
let mut rendered_height = px(0.);
|
||||
let mut scroll_top = state.logical_scroll_top();
|
||||
|
||||
let available_item_space = Size {
|
||||
width: AvailableSpace::Definite(bounds.size.width),
|
||||
height: AvailableSpace::MinContent,
|
||||
};
|
||||
|
||||
// Render items after the scroll top, including those in the trailing overdraw
|
||||
let mut cursor = old_items.cursor::<Count>();
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
for (ix, item) in cursor.by_ref().enumerate() {
|
||||
let visible_height = rendered_height - scroll_top.offset_in_item;
|
||||
if visible_height >= bounds.size.height + state.overdraw {
|
||||
break;
|
||||
}
|
||||
|
||||
// Use the previously cached height if available
|
||||
let mut height = if let ListItem::Rendered { height } = item {
|
||||
Some(*height)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// If we're within the visible area or the height wasn't cached, render and measure the item's element
|
||||
if visible_height < bounds.size.height || height.is_none() {
|
||||
let mut element = (state.render_item)(scroll_top.item_ix + ix, cx);
|
||||
let element_size = element.measure(available_item_space, cx);
|
||||
height = Some(element_size.height);
|
||||
if visible_height < bounds.size.height {
|
||||
item_elements.push_back(element);
|
||||
}
|
||||
}
|
||||
|
||||
let height = height.unwrap();
|
||||
rendered_height += height;
|
||||
measured_items.push_back(ListItem::Rendered { height });
|
||||
}
|
||||
|
||||
// Prepare to start walking upward from the item at the scroll top.
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
|
||||
// If the rendered items do not fill the visible region, then adjust
|
||||
// the scroll top upward.
|
||||
if rendered_height - scroll_top.offset_in_item < bounds.size.height {
|
||||
while rendered_height < bounds.size.height {
|
||||
cursor.prev(&());
|
||||
if cursor.item().is_some() {
|
||||
let mut element = (state.render_item)(cursor.start().0, cx);
|
||||
let element_size = element.measure(available_item_space, cx);
|
||||
|
||||
rendered_height += element_size.height;
|
||||
measured_items.push_front(ListItem::Rendered {
|
||||
height: element_size.height,
|
||||
});
|
||||
item_elements.push_front(element)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scroll_top = ListOffset {
|
||||
item_ix: cursor.start().0,
|
||||
offset_in_item: rendered_height - bounds.size.height,
|
||||
};
|
||||
|
||||
match state.alignment {
|
||||
ListAlignment::Top => {
|
||||
scroll_top.offset_in_item = scroll_top.offset_in_item.max(px(0.));
|
||||
state.logical_scroll_top = Some(scroll_top);
|
||||
}
|
||||
ListAlignment::Bottom => {
|
||||
scroll_top = ListOffset {
|
||||
item_ix: cursor.start().0,
|
||||
offset_in_item: rendered_height - bounds.size.height,
|
||||
};
|
||||
state.logical_scroll_top = None;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Measure items in the leading overdraw
|
||||
let mut leading_overdraw = scroll_top.offset_in_item;
|
||||
while leading_overdraw < state.overdraw {
|
||||
cursor.prev(&());
|
||||
if let Some(item) = cursor.item() {
|
||||
let height = if let ListItem::Rendered { height } = item {
|
||||
*height
|
||||
} else {
|
||||
let mut element = (state.render_item)(cursor.start().0, cx);
|
||||
element.measure(available_item_space, cx).height
|
||||
};
|
||||
|
||||
leading_overdraw += height;
|
||||
measured_items.push_front(ListItem::Rendered { height });
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let measured_range = cursor.start().0..(cursor.start().0 + measured_items.len());
|
||||
let mut cursor = old_items.cursor::<Count>();
|
||||
let mut new_items = cursor.slice(&Count(measured_range.start), Bias::Right, &());
|
||||
new_items.extend(measured_items, &());
|
||||
cursor.seek(&Count(measured_range.end), Bias::Right, &());
|
||||
new_items.append(cursor.suffix(&()), &());
|
||||
|
||||
// Paint the visible items
|
||||
let mut item_origin = bounds.origin;
|
||||
item_origin.y -= scroll_top.offset_in_item;
|
||||
for mut item_element in item_elements {
|
||||
let item_height = item_element.measure(available_item_space, cx).height;
|
||||
item_element.draw(item_origin, available_item_space, cx);
|
||||
item_origin.y += item_height;
|
||||
}
|
||||
|
||||
state.items = new_items;
|
||||
state.last_layout_width = Some(bounds.size.width);
|
||||
|
||||
let list_state = self.state.clone();
|
||||
let height = bounds.size.height;
|
||||
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble {
|
||||
list_state.0.borrow_mut().scroll(
|
||||
&scroll_top,
|
||||
height,
|
||||
event.delta.pixel_delta(px(20.)),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for List {
|
||||
type Element = Self;
|
||||
|
||||
fn element_id(&self) -> Option<crate::ElementId> {
|
||||
None
|
||||
}
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for List {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Item for ListItem {
|
||||
type Summary = ListItemSummary;
|
||||
|
||||
fn summary(&self) -> Self::Summary {
|
||||
match self {
|
||||
ListItem::Unrendered => ListItemSummary {
|
||||
count: 1,
|
||||
rendered_count: 0,
|
||||
unrendered_count: 1,
|
||||
height: px(0.),
|
||||
},
|
||||
ListItem::Rendered { height } => ListItemSummary {
|
||||
count: 1,
|
||||
rendered_count: 1,
|
||||
unrendered_count: 0,
|
||||
height: *height,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Summary for ListItemSummary {
|
||||
type Context = ();
|
||||
|
||||
fn add_summary(&mut self, summary: &Self, _: &()) {
|
||||
self.count += summary.count;
|
||||
self.rendered_count += summary.rendered_count;
|
||||
self.unrendered_count += summary.unrendered_count;
|
||||
self.height += summary.height;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Count {
|
||||
fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
|
||||
self.0 += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ListItemSummary> for RenderedCount {
|
||||
fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
|
||||
self.0 += summary.rendered_count;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ListItemSummary> for UnrenderedCount {
|
||||
fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
|
||||
self.0 += summary.unrendered_count;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, ListItemSummary> for Height {
|
||||
fn add_summary(&mut self, summary: &'a ListItemSummary, _: &()) {
|
||||
self.0 += summary.height;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Count {
|
||||
fn cmp(&self, other: &ListItemSummary, _: &()) -> std::cmp::Ordering {
|
||||
self.0.partial_cmp(&other.count).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
|
||||
fn cmp(&self, other: &ListItemSummary, _: &()) -> std::cmp::Ordering {
|
||||
self.0.partial_cmp(&other.height).unwrap()
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
mod canvas;
|
||||
mod div;
|
||||
mod img;
|
||||
mod list;
|
||||
mod overlay;
|
||||
mod svg;
|
||||
mod text;
|
||||
|
@ -9,6 +10,7 @@ mod uniform_list;
|
|||
pub use canvas::*;
|
||||
pub use div::*;
|
||||
pub use img::*;
|
||||
pub use list::*;
|
||||
pub use overlay::*;
|
||||
pub use svg::*;
|
||||
pub use text::*;
|
||||
|
|
|
@ -131,7 +131,7 @@ impl Element for UniformList {
|
|||
}
|
||||
});
|
||||
let height = match available_space.height {
|
||||
AvailableSpace::Definite(x) => desired_height.min(x),
|
||||
AvailableSpace::Definite(height) => desired_height.min(height),
|
||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
|
||||
desired_height
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#[macro_use]
|
||||
mod action;
|
||||
mod app;
|
||||
|
||||
mod assets;
|
||||
mod color;
|
||||
mod element;
|
||||
|
@ -15,6 +16,7 @@ mod keymap;
|
|||
mod platform;
|
||||
pub mod prelude;
|
||||
mod scene;
|
||||
mod shared_string;
|
||||
mod style;
|
||||
mod styled;
|
||||
mod subscription;
|
||||
|
@ -57,6 +59,7 @@ pub use scene::*;
|
|||
pub use serde;
|
||||
pub use serde_derive;
|
||||
pub use serde_json;
|
||||
pub use shared_string::*;
|
||||
pub use smallvec;
|
||||
pub use smol::Timer;
|
||||
pub use style::*;
|
||||
|
@ -71,10 +74,9 @@ pub use util::arc_cow::ArcCow;
|
|||
pub use view::*;
|
||||
pub use window::*;
|
||||
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::{Borrow, BorrowMut},
|
||||
borrow::BorrowMut,
|
||||
};
|
||||
use taffy::TaffyLayoutEngine;
|
||||
|
||||
|
@ -209,42 +211,3 @@ impl<T> Flatten<T> for Result<T> {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
|
||||
pub struct SharedString(ArcCow<'static, str>);
|
||||
|
||||
impl Default for SharedString {
|
||||
fn default() -> Self {
|
||||
Self(ArcCow::Owned("".into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for SharedString {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for SharedString {
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SharedString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SharedString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
|
||||
fn from(value: T) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -302,7 +302,7 @@ mod test {
|
|||
focus_handle: FocusHandle,
|
||||
}
|
||||
|
||||
actions!(TestAction);
|
||||
actions!(test, [TestAction]);
|
||||
|
||||
impl Render for TestView {
|
||||
type Element = Stateful<Div>;
|
||||
|
|
|
@ -149,13 +149,19 @@ impl DispatchTree {
|
|||
}
|
||||
|
||||
pub fn available_actions(&self, target: DispatchNodeId) -> Vec<Box<dyn Action>> {
|
||||
let mut actions = Vec::new();
|
||||
let mut actions = Vec::<Box<dyn Action>>::new();
|
||||
for node_id in self.dispatch_path(target) {
|
||||
let node = &self.nodes[node_id.0];
|
||||
for DispatchActionListener { action_type, .. } in &node.action_listeners {
|
||||
// Intentionally silence these errors without logging.
|
||||
// If an action cannot be built by default, it's not available.
|
||||
actions.extend(self.action_registry.build_action_type(action_type).ok());
|
||||
if let Err(ix) = actions.binary_search_by_key(action_type, |a| a.as_any().type_id())
|
||||
{
|
||||
// Intentionally silence these errors without logging.
|
||||
// If an action cannot be built by default, it's not available.
|
||||
let action = self.action_registry.build_action_type(action_type).ok();
|
||||
if let Some(action) = action {
|
||||
actions.insert(ix, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
actions
|
||||
|
|
|
@ -293,11 +293,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_actions_definition() {
|
||||
{
|
||||
actions!(A, B, C, D, E, F, G);
|
||||
actions!(test, [A, B, C, D, E, F, G]);
|
||||
}
|
||||
|
||||
{
|
||||
actions!(
|
||||
test,
|
||||
[
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
|
@ -305,6 +307,7 @@ mod tests {
|
|||
E,
|
||||
F,
|
||||
G, // Don't wrap, test the trailing comma
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
101
crates/gpui2/src/shared_string.rs
Normal file
101
crates/gpui2/src/shared_string.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
use derive_more::{Deref, DerefMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{borrow::Borrow, sync::Arc};
|
||||
use util::arc_cow::ArcCow;
|
||||
|
||||
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
|
||||
pub struct SharedString(ArcCow<'static, str>);
|
||||
|
||||
impl Default for SharedString {
|
||||
fn default() -> Self {
|
||||
Self(ArcCow::Owned("".into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for SharedString {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for SharedString {
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SharedString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SharedString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for SharedString {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_ref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<SharedString> for String {
|
||||
fn eq(&self, other: &SharedString) -> bool {
|
||||
self == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for SharedString {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_ref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for SharedString {
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.as_ref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Arc<str>> for SharedString {
|
||||
fn into(self) -> Arc<str> {
|
||||
match self.0 {
|
||||
ArcCow::Borrowed(borrowed) => Arc::from(borrowed),
|
||||
ArcCow::Owned(owned) => owned.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<ArcCow<'static, str>>> From<T> for SharedString {
|
||||
fn from(value: T) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for SharedString {
|
||||
fn into(self) -> String {
|
||||
self.0.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for SharedString {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for SharedString {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Ok(SharedString::from(s))
|
||||
}
|
||||
}
|
|
@ -245,6 +245,13 @@ pub trait Styled: Sized {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the flex direction of the element to `column-reverse`.
|
||||
/// [Docs](https://tailwindcss.com/docs/flex-direction#column-reverse)
|
||||
fn flex_col_reverse(mut self) -> Self {
|
||||
self.style().flex_direction = Some(FlexDirection::ColumnReverse);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the flex direction of the element to `row`.
|
||||
/// [Docs](https://tailwindcss.com/docs/flex-direction#row)
|
||||
fn flex_row(mut self) -> Self {
|
||||
|
@ -252,6 +259,13 @@ pub trait Styled: Sized {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the flex direction of the element to `row-reverse`.
|
||||
/// [Docs](https://tailwindcss.com/docs/flex-direction#row-reverse)
|
||||
fn flex_row_reverse(mut self) -> Self {
|
||||
self.style().flex_direction = Some(FlexDirection::RowReverse);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the element to allow a flex item to grow and shrink as needed, ignoring its initial size.
|
||||
/// [Docs](https://tailwindcss.com/docs/flex#flex-1)
|
||||
fn flex_1(mut self) -> Self {
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
use gpui2::{actions, impl_actions};
|
||||
use gpui2_macros::register_action;
|
||||
use serde_derive::Deserialize;
|
||||
|
||||
#[test]
|
||||
fn test_derive() {
|
||||
fn test_action_macros() {
|
||||
use gpui2 as gpui;
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, gpui2_macros::Action)]
|
||||
actions!(test, [TestAction]);
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize)]
|
||||
struct AnotherTestAction;
|
||||
|
||||
#[gpui2_macros::register_action]
|
||||
impl_actions!(test, [AnotherTestAction]);
|
||||
|
||||
#[derive(PartialEq, Clone, gpui::serde_derive::Deserialize)]
|
||||
struct RegisterableAction {}
|
||||
|
||||
register_action!(RegisterableAction);
|
||||
|
||||
impl gpui::Action for RegisterableAction {
|
||||
fn boxed_clone(&self) -> Box<dyn gpui::Action> {
|
||||
todo!()
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
// Input:
|
||||
//
|
||||
// #[action]
|
||||
// struct Foo {
|
||||
// bar: String,
|
||||
// }
|
||||
|
||||
// Output:
|
||||
//
|
||||
// #[gpui::register_action]
|
||||
// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
|
||||
// struct Foo {
|
||||
// bar: String,
|
||||
// }
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput, Error};
|
||||
|
||||
use crate::register_action::register_action;
|
||||
|
||||
pub fn action(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let name = &input.ident;
|
||||
|
||||
if input.generics.lt_token.is_some() {
|
||||
return Error::new(name.span(), "Actions must be a concrete type")
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let is_unit_struct = match input.data {
|
||||
syn::Data::Struct(struct_data) => struct_data.fields.is_empty(),
|
||||
syn::Data::Enum(_) => false,
|
||||
syn::Data::Union(_) => false,
|
||||
};
|
||||
|
||||
let build_impl = if is_unit_struct {
|
||||
quote! {
|
||||
let _ = value;
|
||||
Ok(std::boxed::Box::new(Self {}))
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
|
||||
}
|
||||
};
|
||||
|
||||
let register_action = register_action(&name);
|
||||
|
||||
let output = quote! {
|
||||
const _: fn() = || {
|
||||
fn assert_impl<T: ?Sized + for<'a> gpui::serde::Deserialize<'a> + ::std::cmp::PartialEq + ::std::clone::Clone>() {}
|
||||
assert_impl::<#name>();
|
||||
};
|
||||
|
||||
impl gpui::Action for #name {
|
||||
fn name(&self) -> &'static str
|
||||
{
|
||||
::std::any::type_name::<#name>()
|
||||
}
|
||||
|
||||
fn debug_name() -> &'static str
|
||||
where
|
||||
Self: ::std::marker::Sized
|
||||
{
|
||||
::std::any::type_name::<#name>()
|
||||
}
|
||||
|
||||
fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>>
|
||||
where
|
||||
Self: ::std::marker::Sized {
|
||||
#build_impl
|
||||
}
|
||||
|
||||
fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
|
||||
action
|
||||
.as_any()
|
||||
.downcast_ref::<Self>()
|
||||
.map_or(false, |a| self == a)
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> std::boxed::Box<dyn gpui::Action> {
|
||||
::std::boxed::Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn ::std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#register_action
|
||||
};
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
mod action;
|
||||
mod derive_into_element;
|
||||
mod register_action;
|
||||
mod style_helpers;
|
||||
|
@ -6,14 +5,9 @@ mod test;
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[proc_macro_derive(Action)]
|
||||
pub fn action(input: TokenStream) -> TokenStream {
|
||||
action::action(input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn register_action(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
register_action::register_action_macro(attr, item)
|
||||
#[proc_macro]
|
||||
pub fn register_action(ident: TokenStream) -> TokenStream {
|
||||
register_action::register_action_macro(ident)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(IntoElement)]
|
||||
|
|
|
@ -14,47 +14,13 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Ident;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, DeriveInput, Error};
|
||||
use syn::parse_macro_input;
|
||||
|
||||
pub fn register_action_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let registration = register_action(&input.ident);
|
||||
|
||||
let has_action_derive = input
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| {
|
||||
(|| {
|
||||
let meta = attr.parse_meta().ok()?;
|
||||
meta.path().is_ident("derive").then(|| match meta {
|
||||
syn::Meta::Path(_) => None,
|
||||
syn::Meta::NameValue(_) => None,
|
||||
syn::Meta::List(list) => list
|
||||
.nested
|
||||
.iter()
|
||||
.find(|list| match list {
|
||||
syn::NestedMeta::Meta(meta) => meta.path().is_ident("Action"),
|
||||
syn::NestedMeta::Lit(_) => false,
|
||||
})
|
||||
.map(|_| true),
|
||||
})?
|
||||
})()
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.is_some();
|
||||
|
||||
if has_action_derive {
|
||||
return Error::new(
|
||||
input.ident.span(),
|
||||
"The Action derive macro has already registered this action",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
pub fn register_action_macro(ident: TokenStream) -> TokenStream {
|
||||
let name = parse_macro_input!(ident as Ident);
|
||||
let registration = register_action(&name);
|
||||
|
||||
TokenStream::from(quote! {
|
||||
#input
|
||||
|
||||
#registration
|
||||
})
|
||||
}
|
||||
|
@ -78,7 +44,7 @@ pub(crate) fn register_action(type_name: &Ident) -> proc_macro2::TokenStream {
|
|||
#[doc(hidden)]
|
||||
fn #action_builder_fn_name() -> gpui::ActionData {
|
||||
gpui::ActionData {
|
||||
name: ::std::any::type_name::<#type_name>(),
|
||||
name: <#type_name as gpui::Action>::debug_name(),
|
||||
type_id: ::std::any::TypeId::of::<#type_name>(),
|
||||
build: <#type_name as gpui::Action>::build,
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use gpui::{actions, AsyncAppContext};
|
|||
use std::path::Path;
|
||||
use util::ResultExt;
|
||||
|
||||
actions!(Install);
|
||||
actions!(cli, [Install]);
|
||||
|
||||
pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
|
||||
let cli_path = cx.update(|cx| cx.path_for_auxiliary_executable("cli"))??;
|
||||
|
|
|
@ -14,9 +14,9 @@ use project::Project;
|
|||
use std::sync::Arc;
|
||||
use ui::{prelude::*, HighlightedLabel, ListItem};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
actions!(Toggle);
|
||||
actions!(language_selector, [Toggle]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.observe_new_views(LanguageSelector::register).detach();
|
||||
|
@ -81,6 +81,7 @@ impl FocusableView for LanguageSelector {
|
|||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for LanguageSelector {}
|
||||
impl ModalView for LanguageSelector {}
|
||||
|
||||
pub struct LanguageSelectorDelegate {
|
||||
language_selector: WeakView<LanguageSelector>,
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use futures::StreamExt;
|
||||
use gpui::{Action, KeyBinding};
|
||||
use gpui::{actions, KeyBinding};
|
||||
use live_kit_client2::{
|
||||
LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room,
|
||||
};
|
||||
use live_kit_server::token::{self, VideoGrant};
|
||||
use log::LevelFilter;
|
||||
use serde_derive::Deserialize;
|
||||
use simplelog::SimpleLogger;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Action)]
|
||||
struct Quit;
|
||||
actions!(live_kit_client, [Quit]);
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||
|
|
|
@ -10,12 +10,15 @@ use gpui::actions;
|
|||
pub fn init() {}
|
||||
|
||||
actions!(
|
||||
Cancel,
|
||||
Confirm,
|
||||
SecondaryConfirm,
|
||||
SelectPrev,
|
||||
SelectNext,
|
||||
SelectFirst,
|
||||
SelectLast,
|
||||
ShowContextMenu
|
||||
menu,
|
||||
[
|
||||
Cancel,
|
||||
Confirm,
|
||||
SecondaryConfirm,
|
||||
SelectPrev,
|
||||
SelectNext,
|
||||
SelectFirst,
|
||||
SelectLast,
|
||||
ShowContextMenu
|
||||
]
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use editor::{
|
||||
display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Anchor, AnchorRangeExt,
|
||||
DisplayPoint, Editor, ToPoint,
|
||||
DisplayPoint, Editor, EditorMode, ToPoint,
|
||||
};
|
||||
use fuzzy::StringMatch;
|
||||
use gpui::{
|
||||
|
@ -20,29 +20,26 @@ use std::{
|
|||
use theme::{color_alpha, ActiveTheme, ThemeSettings};
|
||||
use ui::{prelude::*, ListItem};
|
||||
use util::ResultExt;
|
||||
use workspace::Workspace;
|
||||
use workspace::ModalView;
|
||||
|
||||
actions!(Toggle);
|
||||
actions!(outline, [Toggle]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.observe_new_views(OutlineView::register).detach();
|
||||
}
|
||||
|
||||
pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
||||
if let Some(editor) = workspace
|
||||
.active_item(cx)
|
||||
.and_then(|item| item.downcast::<Editor>())
|
||||
{
|
||||
let outline = editor
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.outline(Some(&cx.theme().syntax()));
|
||||
pub fn toggle(editor: View<Editor>, _: &Toggle, cx: &mut WindowContext) {
|
||||
let outline = editor
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.outline(Some(&cx.theme().syntax()));
|
||||
|
||||
if let Some(outline) = outline {
|
||||
if let Some((workspace, outline)) = editor.read(cx).workspace().zip(outline) {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace.toggle_modal(cx, |cx| OutlineView::new(outline, editor, cx));
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +54,7 @@ impl FocusableView for OutlineView {
|
|||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for OutlineView {}
|
||||
impl ModalView for OutlineView {}
|
||||
|
||||
impl Render for OutlineView {
|
||||
type Element = Div;
|
||||
|
@ -67,8 +65,15 @@ impl Render for OutlineView {
|
|||
}
|
||||
|
||||
impl OutlineView {
|
||||
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
||||
workspace.register_action(toggle);
|
||||
fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
|
||||
if editor.mode() == EditorMode::Full {
|
||||
let handle = cx.view().downgrade();
|
||||
editor.register_action(move |action, cx| {
|
||||
if let Some(editor) = handle.upgrade() {
|
||||
toggle(editor, action, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn new(
|
||||
|
@ -238,6 +243,7 @@ impl PickerDelegate for OutlineViewDelegate {
|
|||
s.select_ranges([position..position])
|
||||
});
|
||||
active_editor.highlight_rows(None);
|
||||
active_editor.focus(cx);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
|
|||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub(crate) trait LspCommand: 'static + Sized {
|
||||
pub trait LspCommand: 'static + Sized {
|
||||
type Response: 'static + Default + Send;
|
||||
type LspRequest: 'static + Send + lsp::request::Request;
|
||||
type ProtoRequest: 'static + Send + proto::RequestMessage;
|
||||
|
|
137
crates/project/src/lsp_ext_command.rs
Normal file
137
crates/project/src/lsp_ext_command.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use gpui::{AppContext, AsyncAppContext, ModelHandle};
|
||||
use language::{point_to_lsp, proto::deserialize_anchor, Buffer};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use rpc::proto::{self, PeerId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use text::{PointUtf16, ToPointUtf16};
|
||||
|
||||
use crate::{lsp_command::LspCommand, Project};
|
||||
|
||||
pub enum LspExpandMacro {}
|
||||
|
||||
impl lsp::request::Request for LspExpandMacro {
|
||||
type Params = ExpandMacroParams;
|
||||
type Result = Option<ExpandedMacro>;
|
||||
const METHOD: &'static str = "rust-analyzer/expandMacro";
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExpandMacroParams {
|
||||
pub text_document: lsp::TextDocumentIdentifier,
|
||||
pub position: lsp::Position,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExpandedMacro {
|
||||
pub name: String,
|
||||
pub expansion: String,
|
||||
}
|
||||
|
||||
impl ExpandedMacro {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.name.is_empty() && self.expansion.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExpandMacro {
|
||||
pub position: PointUtf16,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for ExpandMacro {
|
||||
type Response = ExpandedMacro;
|
||||
type LspRequest = LspExpandMacro;
|
||||
type ProtoRequest = proto::LspExtExpandMacro;
|
||||
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> ExpandMacroParams {
|
||||
ExpandMacroParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: lsp::Url::from_file_path(path).unwrap(),
|
||||
},
|
||||
position: point_to_lsp(self.position),
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_lsp(
|
||||
self,
|
||||
message: Option<ExpandedMacro>,
|
||||
_: ModelHandle<Project>,
|
||||
_: ModelHandle<Buffer>,
|
||||
_: LanguageServerId,
|
||||
_: AsyncAppContext,
|
||||
) -> anyhow::Result<ExpandedMacro> {
|
||||
Ok(message
|
||||
.map(|message| ExpandedMacro {
|
||||
name: message.name,
|
||||
expansion: message.expansion,
|
||||
})
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro {
|
||||
proto::LspExtExpandMacro {
|
||||
project_id,
|
||||
buffer_id: buffer.remote_id(),
|
||||
position: Some(language::proto::serialize_anchor(
|
||||
&buffer.anchor_before(self.position),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_proto(
|
||||
message: Self::ProtoRequest,
|
||||
_: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
let position = message
|
||||
.position
|
||||
.and_then(deserialize_anchor)
|
||||
.context("invalid position")?;
|
||||
Ok(Self {
|
||||
position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer)),
|
||||
})
|
||||
}
|
||||
|
||||
fn response_to_proto(
|
||||
response: ExpandedMacro,
|
||||
_: &mut Project,
|
||||
_: PeerId,
|
||||
_: &clock::Global,
|
||||
_: &mut AppContext,
|
||||
) -> proto::LspExtExpandMacroResponse {
|
||||
proto::LspExtExpandMacroResponse {
|
||||
name: response.name,
|
||||
expansion: response.expansion,
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::LspExtExpandMacroResponse,
|
||||
_: ModelHandle<Project>,
|
||||
_: ModelHandle<Buffer>,
|
||||
_: AsyncAppContext,
|
||||
) -> anyhow::Result<ExpandedMacro> {
|
||||
Ok(ExpandedMacro {
|
||||
name: message.name,
|
||||
expansion: message.expansion,
|
||||
})
|
||||
}
|
||||
|
||||
fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> u64 {
|
||||
message.buffer_id
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
mod ignore;
|
||||
mod lsp_command;
|
||||
pub mod lsp_command;
|
||||
pub mod lsp_ext_command;
|
||||
mod prettier_support;
|
||||
pub mod project_settings;
|
||||
pub mod search;
|
||||
|
@ -174,7 +175,7 @@ struct DelayedDebounced {
|
|||
cancel_channel: Option<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
enum LanguageServerToQuery {
|
||||
pub enum LanguageServerToQuery {
|
||||
Primary,
|
||||
Other(LanguageServerId),
|
||||
}
|
||||
|
@ -626,6 +627,7 @@ impl Project {
|
|||
client.add_model_request_handler(Self::handle_open_buffer_by_path);
|
||||
client.add_model_request_handler(Self::handle_save_buffer);
|
||||
client.add_model_message_handler(Self::handle_update_diff_base);
|
||||
client.add_model_request_handler(Self::handle_lsp_command::<lsp_ext_command::ExpandMacro>);
|
||||
}
|
||||
|
||||
pub fn local(
|
||||
|
@ -5863,7 +5865,7 @@ impl Project {
|
|||
.await;
|
||||
}
|
||||
|
||||
fn request_lsp<R: LspCommand>(
|
||||
pub fn request_lsp<R: LspCommand>(
|
||||
&self,
|
||||
buffer_handle: ModelHandle<Buffer>,
|
||||
server: LanguageServerToQuery,
|
||||
|
|
|
@ -33,7 +33,7 @@ pub fn lsp_formatting_options(tab_size: u32) -> lsp::FormattingOptions {
|
|||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
pub(crate) trait LspCommand: 'static + Sized + Send {
|
||||
pub trait LspCommand: 'static + Sized + Send {
|
||||
type Response: 'static + Default + Send;
|
||||
type LspRequest: 'static + Send + lsp::request::Request;
|
||||
type ProtoRequest: 'static + Send + proto::RequestMessage;
|
||||
|
|
137
crates/project2/src/lsp_ext_command.rs
Normal file
137
crates/project2/src/lsp_ext_command.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use gpui::{AppContext, AsyncAppContext, Model};
|
||||
use language::{point_to_lsp, proto::deserialize_anchor, Buffer};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use rpc::proto::{self, PeerId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use text::{PointUtf16, ToPointUtf16};
|
||||
|
||||
use crate::{lsp_command::LspCommand, Project};
|
||||
|
||||
pub enum LspExpandMacro {}
|
||||
|
||||
impl lsp::request::Request for LspExpandMacro {
|
||||
type Params = ExpandMacroParams;
|
||||
type Result = Option<ExpandedMacro>;
|
||||
const METHOD: &'static str = "rust-analyzer/expandMacro";
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExpandMacroParams {
|
||||
pub text_document: lsp::TextDocumentIdentifier,
|
||||
pub position: lsp::Position,
|
||||
}
|
||||
|
||||
#[derive(Default, Deserialize, Serialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExpandedMacro {
|
||||
pub name: String,
|
||||
pub expansion: String,
|
||||
}
|
||||
|
||||
impl ExpandedMacro {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.name.is_empty() && self.expansion.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExpandMacro {
|
||||
pub position: PointUtf16,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspCommand for ExpandMacro {
|
||||
type Response = ExpandedMacro;
|
||||
type LspRequest = LspExpandMacro;
|
||||
type ProtoRequest = proto::LspExtExpandMacro;
|
||||
|
||||
fn to_lsp(
|
||||
&self,
|
||||
path: &Path,
|
||||
_: &Buffer,
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &AppContext,
|
||||
) -> ExpandMacroParams {
|
||||
ExpandMacroParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: lsp::Url::from_file_path(path).unwrap(),
|
||||
},
|
||||
position: point_to_lsp(self.position),
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_lsp(
|
||||
self,
|
||||
message: Option<ExpandedMacro>,
|
||||
_: Model<Project>,
|
||||
_: Model<Buffer>,
|
||||
_: LanguageServerId,
|
||||
_: AsyncAppContext,
|
||||
) -> anyhow::Result<ExpandedMacro> {
|
||||
Ok(message
|
||||
.map(|message| ExpandedMacro {
|
||||
name: message.name,
|
||||
expansion: message.expansion,
|
||||
})
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::LspExtExpandMacro {
|
||||
proto::LspExtExpandMacro {
|
||||
project_id,
|
||||
buffer_id: buffer.remote_id(),
|
||||
position: Some(language::proto::serialize_anchor(
|
||||
&buffer.anchor_before(self.position),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn from_proto(
|
||||
message: Self::ProtoRequest,
|
||||
_: Model<Project>,
|
||||
buffer: Model<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> anyhow::Result<Self> {
|
||||
let position = message
|
||||
.position
|
||||
.and_then(deserialize_anchor)
|
||||
.context("invalid position")?;
|
||||
Ok(Self {
|
||||
position: buffer.update(&mut cx, |buffer, _| position.to_point_utf16(buffer))?,
|
||||
})
|
||||
}
|
||||
|
||||
fn response_to_proto(
|
||||
response: ExpandedMacro,
|
||||
_: &mut Project,
|
||||
_: PeerId,
|
||||
_: &clock::Global,
|
||||
_: &mut AppContext,
|
||||
) -> proto::LspExtExpandMacroResponse {
|
||||
proto::LspExtExpandMacroResponse {
|
||||
name: response.name,
|
||||
expansion: response.expansion,
|
||||
}
|
||||
}
|
||||
|
||||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::LspExtExpandMacroResponse,
|
||||
_: Model<Project>,
|
||||
_: Model<Buffer>,
|
||||
_: AsyncAppContext,
|
||||
) -> anyhow::Result<ExpandedMacro> {
|
||||
Ok(ExpandedMacro {
|
||||
name: message.name,
|
||||
expansion: message.expansion,
|
||||
})
|
||||
}
|
||||
|
||||
fn buffer_id_from_proto(message: &proto::LspExtExpandMacro) -> u64 {
|
||||
message.buffer_id
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
mod ignore;
|
||||
mod lsp_command;
|
||||
pub mod lsp_command;
|
||||
pub mod lsp_ext_command;
|
||||
mod prettier_support;
|
||||
pub mod project_settings;
|
||||
pub mod search;
|
||||
|
@ -172,7 +173,7 @@ struct DelayedDebounced {
|
|||
cancel_channel: Option<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
enum LanguageServerToQuery {
|
||||
pub enum LanguageServerToQuery {
|
||||
Primary,
|
||||
Other(LanguageServerId),
|
||||
}
|
||||
|
@ -623,6 +624,7 @@ impl Project {
|
|||
client.add_model_request_handler(Self::handle_open_buffer_by_path);
|
||||
client.add_model_request_handler(Self::handle_save_buffer);
|
||||
client.add_model_message_handler(Self::handle_update_diff_base);
|
||||
client.add_model_request_handler(Self::handle_lsp_command::<lsp_ext_command::ExpandMacro>);
|
||||
}
|
||||
|
||||
pub fn local(
|
||||
|
@ -5933,7 +5935,7 @@ impl Project {
|
|||
.await;
|
||||
}
|
||||
|
||||
fn request_lsp<R: LspCommand>(
|
||||
pub fn request_lsp<R: LspCommand>(
|
||||
&self,
|
||||
buffer_handle: Model<Buffer>,
|
||||
server: LanguageServerToQuery,
|
||||
|
|
|
@ -103,23 +103,26 @@ pub struct EntryDetails {
|
|||
}
|
||||
|
||||
actions!(
|
||||
ExpandSelectedEntry,
|
||||
CollapseSelectedEntry,
|
||||
CollapseAllEntries,
|
||||
NewDirectory,
|
||||
NewFile,
|
||||
Copy,
|
||||
CopyPath,
|
||||
CopyRelativePath,
|
||||
RevealInFinder,
|
||||
OpenInTerminal,
|
||||
Cut,
|
||||
Paste,
|
||||
Delete,
|
||||
Rename,
|
||||
Open,
|
||||
ToggleFocus,
|
||||
NewSearchInDirectory,
|
||||
project_panel,
|
||||
[
|
||||
ExpandSelectedEntry,
|
||||
CollapseSelectedEntry,
|
||||
CollapseAllEntries,
|
||||
NewDirectory,
|
||||
NewFile,
|
||||
Copy,
|
||||
CopyPath,
|
||||
CopyRelativePath,
|
||||
RevealInFinder,
|
||||
OpenInTerminal,
|
||||
Cut,
|
||||
Paste,
|
||||
Delete,
|
||||
Rename,
|
||||
Open,
|
||||
ToggleFocus,
|
||||
NewSearchInDirectory,
|
||||
]
|
||||
);
|
||||
|
||||
pub fn init_settings(cx: &mut AppContext) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
gpui::actions!(OpenRecent);
|
||||
gpui::actions!(projects, [OpenRecent]);
|
||||
|
|
|
@ -13,8 +13,8 @@ use std::sync::Arc;
|
|||
use ui::{prelude::*, ListItem};
|
||||
use util::paths::PathExt;
|
||||
use workspace::{
|
||||
notifications::simple_message_notification::MessageNotification, Workspace, WorkspaceLocation,
|
||||
WORKSPACE_DB,
|
||||
notifications::simple_message_notification::MessageNotification, ModalView, Workspace,
|
||||
WorkspaceLocation, WORKSPACE_DB,
|
||||
};
|
||||
|
||||
pub use projects::OpenRecent;
|
||||
|
@ -27,6 +27,8 @@ pub struct RecentProjects {
|
|||
picker: View<Picker<RecentProjectsDelegate>>,
|
||||
}
|
||||
|
||||
impl ModalView for RecentProjects {}
|
||||
|
||||
impl RecentProjects {
|
||||
fn new(delegate: RecentProjectsDelegate, cx: &mut ViewContext<Self>) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use std::{ops::Range, sync::Arc};
|
||||
|
||||
use anyhow::bail;
|
||||
use futures::FutureExt;
|
||||
use gpui::{AnyElement, FontStyle, FontWeight, HighlightStyle, UnderlineStyle};
|
||||
use gpui::{
|
||||
AnyElement, ElementId, FontStyle, FontWeight, HighlightStyle, InteractiveText, IntoElement,
|
||||
SharedString, StyledText, UnderlineStyle, WindowContext,
|
||||
};
|
||||
use language::{HighlightId, Language, LanguageRegistry};
|
||||
use std::{ops::Range, sync::Arc};
|
||||
use theme::ActiveTheme;
|
||||
use util::RangeExt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Highlight {
|
||||
Code,
|
||||
Id(HighlightId),
|
||||
Highlight(HighlightStyle),
|
||||
Mention,
|
||||
|
@ -28,24 +31,10 @@ impl From<HighlightId> for Highlight {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RichText {
|
||||
pub text: String,
|
||||
pub text: SharedString,
|
||||
pub highlights: Vec<(Range<usize>, Highlight)>,
|
||||
pub region_ranges: Vec<Range<usize>>,
|
||||
pub regions: Vec<RenderedRegion>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum BackgroundKind {
|
||||
Code,
|
||||
/// A mention background for non-self user.
|
||||
Mention,
|
||||
SelfMention,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct RenderedRegion {
|
||||
pub background_kind: Option<BackgroundKind>,
|
||||
pub link_url: Option<String>,
|
||||
pub link_ranges: Vec<Range<usize>>,
|
||||
pub link_urls: Arc<[String]>,
|
||||
}
|
||||
|
||||
/// Allows one to specify extra links to the rendered markdown, which can be used
|
||||
|
@ -56,94 +45,71 @@ pub struct Mention {
|
|||
}
|
||||
|
||||
impl RichText {
|
||||
pub fn element(
|
||||
&self,
|
||||
// syntax: Arc<SyntaxTheme>,
|
||||
// style: RichTextStyle,
|
||||
// cx: &mut ViewContext<V>,
|
||||
) -> AnyElement {
|
||||
todo!();
|
||||
pub fn element(&self, id: ElementId, cx: &mut WindowContext) -> AnyElement {
|
||||
let theme = cx.theme();
|
||||
let code_background = theme.colors().surface_background;
|
||||
|
||||
// let mut region_id = 0;
|
||||
// let view_id = cx.view_id();
|
||||
|
||||
// let regions = self.regions.clone();
|
||||
|
||||
// enum Markdown {}
|
||||
// Text::new(self.text.clone(), style.text.clone())
|
||||
// .with_highlights(
|
||||
// self.highlights
|
||||
// .iter()
|
||||
// .filter_map(|(range, highlight)| {
|
||||
// let style = match highlight {
|
||||
// Highlight::Id(id) => id.style(&syntax)?,
|
||||
// Highlight::Highlight(style) => style.clone(),
|
||||
// Highlight::Mention => style.mention_highlight,
|
||||
// Highlight::SelfMention => style.self_mention_highlight,
|
||||
// };
|
||||
// Some((range.clone(), style))
|
||||
// })
|
||||
// .collect::<Vec<_>>(),
|
||||
// )
|
||||
// .with_custom_runs(self.region_ranges.clone(), move |ix, bounds, cx| {
|
||||
// region_id += 1;
|
||||
// let region = regions[ix].clone();
|
||||
// if let Some(url) = region.link_url {
|
||||
// cx.scene().push_cursor_region(CursorRegion {
|
||||
// bounds,
|
||||
// style: CursorStyle::PointingHand,
|
||||
// });
|
||||
// cx.scene().push_mouse_region(
|
||||
// MouseRegion::new::<Markdown>(view_id, region_id, bounds)
|
||||
// .on_click::<V, _>(MouseButton::Left, move |_, _, cx| {
|
||||
// cx.platform().open_url(&url)
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
// if let Some(region_kind) = ®ion.background_kind {
|
||||
// let background = match region_kind {
|
||||
// BackgroundKind::Code => style.code_background,
|
||||
// BackgroundKind::Mention => style.mention_background,
|
||||
// BackgroundKind::SelfMention => style.self_mention_background,
|
||||
// };
|
||||
// if background.is_some() {
|
||||
// cx.scene().push_quad(gpui::Quad {
|
||||
// bounds,
|
||||
// background,
|
||||
// border: Default::default(),
|
||||
// corner_radii: (2.0).into(),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .with_soft_wrap(true)
|
||||
// .into_any()
|
||||
InteractiveText::new(
|
||||
id,
|
||||
StyledText::new(self.text.clone()).with_highlights(
|
||||
&cx.text_style(),
|
||||
self.highlights.iter().map(|(range, highlight)| {
|
||||
(
|
||||
range.clone(),
|
||||
match highlight {
|
||||
Highlight::Code => HighlightStyle {
|
||||
background_color: Some(code_background),
|
||||
..Default::default()
|
||||
},
|
||||
Highlight::Id(id) => HighlightStyle {
|
||||
background_color: Some(code_background),
|
||||
..id.style(&theme.syntax()).unwrap_or_default()
|
||||
},
|
||||
Highlight::Highlight(highlight) => *highlight,
|
||||
Highlight::Mention => HighlightStyle {
|
||||
font_weight: Some(FontWeight::BOLD),
|
||||
..Default::default()
|
||||
},
|
||||
Highlight::SelfMention => HighlightStyle {
|
||||
font_weight: Some(FontWeight::BOLD),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.on_click(self.link_ranges.clone(), {
|
||||
let link_urls = self.link_urls.clone();
|
||||
move |ix, cx| cx.open_url(&link_urls[ix])
|
||||
})
|
||||
.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()
|
||||
);
|
||||
}
|
||||
// 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(())
|
||||
}
|
||||
// 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(
|
||||
|
@ -151,7 +117,10 @@ pub fn render_markdown_mut(
|
|||
mut mentions: &[Mention],
|
||||
language_registry: &Arc<LanguageRegistry>,
|
||||
language: Option<&Arc<Language>>,
|
||||
data: &mut RichText,
|
||||
text: &mut String,
|
||||
highlights: &mut Vec<(Range<usize>, Highlight)>,
|
||||
link_ranges: &mut Vec<Range<usize>>,
|
||||
link_urls: &mut Vec<String>,
|
||||
) {
|
||||
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
|
||||
|
||||
|
@ -163,18 +132,18 @@ pub fn render_markdown_mut(
|
|||
|
||||
let options = Options::all();
|
||||
for (event, source_range) in Parser::new_ext(&block, options).into_offset_iter() {
|
||||
let prev_len = data.text.len();
|
||||
let prev_len = text.len();
|
||||
match event {
|
||||
Event::Text(t) => {
|
||||
if let Some(language) = ¤t_language {
|
||||
render_code(&mut data.text, &mut data.highlights, t.as_ref(), language);
|
||||
render_code(text, highlights, t.as_ref(), language);
|
||||
} else {
|
||||
if let Some(mention) = mentions.first() {
|
||||
if source_range.contains_inclusive(&mention.range) {
|
||||
mentions = &mentions[1..];
|
||||
let range = (prev_len + mention.range.start - source_range.start)
|
||||
..(prev_len + mention.range.end - source_range.start);
|
||||
data.highlights.push((
|
||||
highlights.push((
|
||||
range.clone(),
|
||||
if mention.is_self_mention {
|
||||
Highlight::SelfMention
|
||||
|
@ -182,19 +151,10 @@ pub fn render_markdown_mut(
|
|||
Highlight::Mention
|
||||
},
|
||||
));
|
||||
data.region_ranges.push(range);
|
||||
data.regions.push(RenderedRegion {
|
||||
background_kind: Some(if mention.is_self_mention {
|
||||
BackgroundKind::SelfMention
|
||||
} else {
|
||||
BackgroundKind::Mention
|
||||
}),
|
||||
link_url: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
data.text.push_str(t.as_ref());
|
||||
text.push_str(t.as_ref());
|
||||
let mut style = HighlightStyle::default();
|
||||
if bold_depth > 0 {
|
||||
style.font_weight = Some(FontWeight::BOLD);
|
||||
|
@ -203,11 +163,8 @@ pub fn render_markdown_mut(
|
|||
style.font_style = Some(FontStyle::Italic);
|
||||
}
|
||||
if let Some(link_url) = link_url.clone() {
|
||||
data.region_ranges.push(prev_len..data.text.len());
|
||||
data.regions.push(RenderedRegion {
|
||||
link_url: Some(link_url),
|
||||
background_kind: None,
|
||||
});
|
||||
link_ranges.push(prev_len..text.len());
|
||||
link_urls.push(link_url);
|
||||
style.underline = Some(UnderlineStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
|
@ -216,27 +173,25 @@ pub fn render_markdown_mut(
|
|||
|
||||
if style != HighlightStyle::default() {
|
||||
let mut new_highlight = true;
|
||||
if let Some((last_range, last_style)) = data.highlights.last_mut() {
|
||||
if let Some((last_range, last_style)) = highlights.last_mut() {
|
||||
if last_range.end == prev_len
|
||||
&& last_style == &Highlight::Highlight(style)
|
||||
{
|
||||
last_range.end = data.text.len();
|
||||
last_range.end = text.len();
|
||||
new_highlight = false;
|
||||
}
|
||||
}
|
||||
if new_highlight {
|
||||
data.highlights
|
||||
.push((prev_len..data.text.len(), Highlight::Highlight(style)));
|
||||
highlights.push((prev_len..text.len(), Highlight::Highlight(style)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Code(t) => {
|
||||
data.text.push_str(t.as_ref());
|
||||
data.region_ranges.push(prev_len..data.text.len());
|
||||
text.push_str(t.as_ref());
|
||||
if link_url.is_some() {
|
||||
data.highlights.push((
|
||||
prev_len..data.text.len(),
|
||||
highlights.push((
|
||||
prev_len..text.len(),
|
||||
Highlight::Highlight(HighlightStyle {
|
||||
underline: Some(UnderlineStyle {
|
||||
thickness: 1.0.into(),
|
||||
|
@ -246,19 +201,19 @@ pub fn render_markdown_mut(
|
|||
}),
|
||||
));
|
||||
}
|
||||
data.regions.push(RenderedRegion {
|
||||
background_kind: Some(BackgroundKind::Code),
|
||||
link_url: link_url.clone(),
|
||||
});
|
||||
if let Some(link_url) = link_url.clone() {
|
||||
link_ranges.push(prev_len..text.len());
|
||||
link_urls.push(link_url);
|
||||
}
|
||||
}
|
||||
Event::Start(tag) => match tag {
|
||||
Tag::Paragraph => new_paragraph(&mut data.text, &mut list_stack),
|
||||
Tag::Paragraph => new_paragraph(text, &mut list_stack),
|
||||
Tag::Heading(_, _, _) => {
|
||||
new_paragraph(&mut data.text, &mut list_stack);
|
||||
new_paragraph(text, &mut list_stack);
|
||||
bold_depth += 1;
|
||||
}
|
||||
Tag::CodeBlock(kind) => {
|
||||
new_paragraph(&mut data.text, &mut list_stack);
|
||||
new_paragraph(text, &mut list_stack);
|
||||
current_language = if let CodeBlockKind::Fenced(language) = kind {
|
||||
language_registry
|
||||
.language_for_name(language.as_ref())
|
||||
|
@ -278,18 +233,18 @@ pub fn render_markdown_mut(
|
|||
let len = list_stack.len();
|
||||
if let Some((list_number, has_content)) = list_stack.last_mut() {
|
||||
*has_content = false;
|
||||
if !data.text.is_empty() && !data.text.ends_with('\n') {
|
||||
data.text.push('\n');
|
||||
if !text.is_empty() && !text.ends_with('\n') {
|
||||
text.push('\n');
|
||||
}
|
||||
for _ in 0..len - 1 {
|
||||
data.text.push_str(" ");
|
||||
text.push_str(" ");
|
||||
}
|
||||
if let Some(number) = list_number {
|
||||
data.text.push_str(&format!("{}. ", number));
|
||||
text.push_str(&format!("{}. ", number));
|
||||
*number += 1;
|
||||
*has_content = false;
|
||||
} else {
|
||||
data.text.push_str("- ");
|
||||
text.push_str("- ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,8 +259,8 @@ pub fn render_markdown_mut(
|
|||
Tag::List(_) => drop(list_stack.pop()),
|
||||
_ => {}
|
||||
},
|
||||
Event::HardBreak => data.text.push('\n'),
|
||||
Event::SoftBreak => data.text.push(' '),
|
||||
Event::HardBreak => text.push('\n'),
|
||||
Event::SoftBreak => text.push(' '),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -317,18 +272,35 @@ 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 data = RichText {
|
||||
// text: Default::default(),
|
||||
// highlights: Default::default(),
|
||||
// region_ranges: Default::default(),
|
||||
// regions: Default::default(),
|
||||
// };
|
||||
|
||||
render_markdown_mut(&block, mentions, language_registry, language, &mut data);
|
||||
let mut text = String::new();
|
||||
let mut highlights = Vec::new();
|
||||
let mut link_ranges = Vec::new();
|
||||
let mut link_urls = Vec::new();
|
||||
render_markdown_mut(
|
||||
&block,
|
||||
mentions,
|
||||
language_registry,
|
||||
language,
|
||||
&mut text,
|
||||
&mut highlights,
|
||||
&mut link_ranges,
|
||||
&mut link_urls,
|
||||
);
|
||||
text.truncate(text.trim_end().len());
|
||||
|
||||
data.text = data.text.trim().to_string();
|
||||
|
||||
data
|
||||
RichText {
|
||||
text: SharedString::from(text),
|
||||
link_urls: link_urls.into(),
|
||||
link_ranges,
|
||||
highlights,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_code(
|
||||
|
@ -339,11 +311,19 @@ pub fn render_code(
|
|||
) {
|
||||
let prev_len = text.len();
|
||||
text.push_str(content);
|
||||
let mut offset = 0;
|
||||
for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
|
||||
if range.start > offset {
|
||||
highlights.push((prev_len + offset..prev_len + range.start, Highlight::Code));
|
||||
}
|
||||
highlights.push((
|
||||
prev_len + range.start..prev_len + range.end,
|
||||
Highlight::Id(highlight_id),
|
||||
));
|
||||
offset = range.end;
|
||||
}
|
||||
if offset < content.len() {
|
||||
highlights.push((prev_len + offset..prev_len + content.len(), Highlight::Code));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -178,7 +178,9 @@ message Envelope {
|
|||
GetNotifications get_notifications = 150;
|
||||
GetNotificationsResponse get_notifications_response = 151;
|
||||
DeleteNotification delete_notification = 152;
|
||||
MarkNotificationRead mark_notification_read = 153; // Current max
|
||||
MarkNotificationRead mark_notification_read = 153;
|
||||
LspExtExpandMacro lsp_ext_expand_macro = 154;
|
||||
LspExtExpandMacroResponse lsp_ext_expand_macro_response = 155; // Current max
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1619,3 +1621,14 @@ message Notification {
|
|||
bool is_read = 6;
|
||||
optional bool response = 7;
|
||||
}
|
||||
|
||||
message LspExtExpandMacro {
|
||||
uint64 project_id = 1;
|
||||
uint64 buffer_id = 2;
|
||||
Anchor position = 3;
|
||||
}
|
||||
|
||||
message LspExtExpandMacroResponse {
|
||||
string name = 1;
|
||||
string expansion = 2;
|
||||
}
|
||||
|
|
|
@ -280,6 +280,8 @@ messages!(
|
|||
(UpdateWorktree, Foreground),
|
||||
(UpdateWorktreeSettings, Foreground),
|
||||
(UsersResponse, Foreground),
|
||||
(LspExtExpandMacro, Background),
|
||||
(LspExtExpandMacroResponse, Background),
|
||||
);
|
||||
|
||||
request_messages!(
|
||||
|
@ -363,6 +365,7 @@ request_messages!(
|
|||
(UpdateParticipantLocation, Ack),
|
||||
(UpdateProject, Ack),
|
||||
(UpdateWorktree, Ack),
|
||||
(LspExtExpandMacro, LspExtExpandMacroResponse),
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
@ -415,6 +418,7 @@ entity_messages!(
|
|||
UpdateProjectCollaborator,
|
||||
UpdateWorktree,
|
||||
UpdateWorktreeSettings,
|
||||
LspExtExpandMacro,
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
|
|
@ -178,7 +178,9 @@ message Envelope {
|
|||
GetNotifications get_notifications = 150;
|
||||
GetNotificationsResponse get_notifications_response = 151;
|
||||
DeleteNotification delete_notification = 152;
|
||||
MarkNotificationRead mark_notification_read = 153; // Current max
|
||||
MarkNotificationRead mark_notification_read = 153;
|
||||
LspExtExpandMacro lsp_ext_expand_macro = 154;
|
||||
LspExtExpandMacroResponse lsp_ext_expand_macro_response = 155; // Current max
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1619,3 +1621,14 @@ message Notification {
|
|||
bool is_read = 6;
|
||||
optional bool response = 7;
|
||||
}
|
||||
|
||||
message LspExtExpandMacro {
|
||||
uint64 project_id = 1;
|
||||
uint64 buffer_id = 2;
|
||||
Anchor position = 3;
|
||||
}
|
||||
|
||||
message LspExtExpandMacroResponse {
|
||||
string name = 1;
|
||||
string expansion = 2;
|
||||
}
|
||||
|
|
|
@ -280,6 +280,8 @@ messages!(
|
|||
(UpdateWorktree, Foreground),
|
||||
(UpdateWorktreeSettings, Foreground),
|
||||
(UsersResponse, Foreground),
|
||||
(LspExtExpandMacro, Background),
|
||||
(LspExtExpandMacroResponse, Background),
|
||||
);
|
||||
|
||||
request_messages!(
|
||||
|
@ -363,6 +365,7 @@ request_messages!(
|
|||
(UpdateParticipantLocation, Ack),
|
||||
(UpdateProject, Ack),
|
||||
(UpdateWorktree, Ack),
|
||||
(LspExtExpandMacro, LspExtExpandMacroResponse),
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
@ -415,6 +418,7 @@ entity_messages!(
|
|||
UpdateProjectCollaborator,
|
||||
UpdateWorktree,
|
||||
UpdateWorktreeSettings,
|
||||
LspExtExpandMacro,
|
||||
);
|
||||
|
||||
entity_messages!(
|
||||
|
|
|
@ -1075,8 +1075,7 @@ impl ProjectSearchView {
|
|||
});
|
||||
}
|
||||
|
||||
// Re-activate the most recently activated search or the most recent if it has been closed.
|
||||
// If no search exists in the workspace, create a new one.
|
||||
// Add another search tab to the workspace.
|
||||
fn deploy(
|
||||
workspace: &mut Workspace,
|
||||
_: &workspace::NewSearch,
|
||||
|
@ -1087,19 +1086,6 @@ impl ProjectSearchView {
|
|||
state.0.retain(|project, _| project.is_upgradable(cx))
|
||||
});
|
||||
|
||||
let active_search = cx
|
||||
.global::<ActiveSearches>()
|
||||
.0
|
||||
.get(&workspace.project().downgrade());
|
||||
|
||||
let existing = active_search
|
||||
.and_then(|active_search| {
|
||||
workspace
|
||||
.items_of_type::<ProjectSearchView>(cx)
|
||||
.find(|search| search == active_search)
|
||||
})
|
||||
.or_else(|| workspace.item_of_type::<ProjectSearchView>(cx));
|
||||
|
||||
let query = workspace.active_item(cx).and_then(|item| {
|
||||
let editor = item.act_as::<Editor>(cx)?;
|
||||
let query = editor.query_suggestion(cx);
|
||||
|
@ -1110,28 +1096,22 @@ impl ProjectSearchView {
|
|||
}
|
||||
});
|
||||
|
||||
let search = if let Some(existing) = existing {
|
||||
workspace.activate_item(&existing, cx);
|
||||
existing
|
||||
let settings = cx
|
||||
.global::<ActiveSettings>()
|
||||
.0
|
||||
.get(&workspace.project().downgrade());
|
||||
|
||||
let settings = if let Some(settings) = settings {
|
||||
Some(settings.clone())
|
||||
} else {
|
||||
let settings = cx
|
||||
.global::<ActiveSettings>()
|
||||
.0
|
||||
.get(&workspace.project().downgrade());
|
||||
|
||||
let settings = if let Some(settings) = settings {
|
||||
Some(settings.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
let view = cx.add_view(|cx| ProjectSearchView::new(model, cx, settings));
|
||||
|
||||
workspace.add_item(Box::new(view.clone()), cx);
|
||||
view
|
||||
None
|
||||
};
|
||||
|
||||
let model = cx.add_model(|cx| ProjectSearch::new(workspace.project().clone(), cx));
|
||||
let search = cx.add_view(|cx| ProjectSearchView::new(model, cx, settings));
|
||||
|
||||
workspace.add_item(Box::new(search.clone()), cx);
|
||||
|
||||
search.update(cx, |search, cx| {
|
||||
if let Some(query) = query {
|
||||
search.set_query(&query, cx);
|
||||
|
@ -2306,29 +2286,86 @@ pub mod tests {
|
|||
workspace.update(cx, |workspace, cx| {
|
||||
ProjectSearchView::deploy(workspace, &workspace::NewSearch, cx)
|
||||
});
|
||||
deterministic.run_until_parked();
|
||||
let Some(search_view_2) = cx.read(|cx| {
|
||||
workspace
|
||||
.read(cx)
|
||||
.active_pane()
|
||||
.read(cx)
|
||||
.active_item()
|
||||
.and_then(|item| item.downcast::<ProjectSearchView>())
|
||||
}) else {
|
||||
panic!("Search view expected to appear after new search event trigger")
|
||||
};
|
||||
let search_view_id_2 = search_view_2.id();
|
||||
assert_ne!(
|
||||
search_view_2, search_view,
|
||||
"New search view should be open after `workspace::NewSearch` event"
|
||||
);
|
||||
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "two", "Query should be updated to first search result after search view 2nd open in a row");
|
||||
assert_eq!(search_view.query_editor.read(cx).text(cx), "TWO", "First search view should not have an updated query");
|
||||
assert_eq!(
|
||||
search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||
"\n\nconst THREE: usize = one::ONE + two::TWO;\n\n\nconst TWO: usize = one::ONE + one::ONE;",
|
||||
"Results should be unchanged after search view 2nd open in a row"
|
||||
"Results of the first search view should not update too"
|
||||
);
|
||||
assert!(
|
||||
search_view.query_editor.is_focused(cx),
|
||||
"Focus should be moved into query editor again after search view 2nd open in a row"
|
||||
!search_view.query_editor.is_focused(cx),
|
||||
"Focus should be moved away from the first search view"
|
||||
);
|
||||
});
|
||||
|
||||
search_view_2.update(cx, |search_view_2, cx| {
|
||||
assert_eq!(
|
||||
search_view_2.query_editor.read(cx).text(cx),
|
||||
"two",
|
||||
"New search view should get the query from the text cursor was at during the event spawn (first search view's first result)"
|
||||
);
|
||||
assert_eq!(
|
||||
search_view_2
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||
"",
|
||||
"No search results should be in the 2nd view yet, as we did not spawn a search for it"
|
||||
);
|
||||
assert!(
|
||||
search_view_2.query_editor.is_focused(cx),
|
||||
"Focus should be moved into query editor fo the new window"
|
||||
);
|
||||
});
|
||||
|
||||
search_view_2.update(cx, |search_view_2, cx| {
|
||||
search_view_2
|
||||
.query_editor
|
||||
.update(cx, |query_editor, cx| query_editor.set_text("FOUR", cx));
|
||||
search_view_2.search(cx);
|
||||
});
|
||||
deterministic.run_until_parked();
|
||||
search_view_2.update(cx, |search_view_2, cx| {
|
||||
assert_eq!(
|
||||
search_view_2
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.display_text(cx)),
|
||||
"\n\nconst FOUR: usize = one::ONE + three::THREE;",
|
||||
"New search view with the updated query should have new search results"
|
||||
);
|
||||
assert!(
|
||||
search_view_2.results_editor.is_focused(cx),
|
||||
"Search view with mismatching query should be focused after search results are available",
|
||||
);
|
||||
});
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
window.dispatch_action(search_view_id, &ToggleFocus, &mut cx);
|
||||
window.dispatch_action(search_view_id_2, &ToggleFocus, &mut cx);
|
||||
})
|
||||
.detach();
|
||||
deterministic.run_until_parked();
|
||||
search_view.update(cx, |search_view, cx| {
|
||||
search_view_2.update(cx, |search_view_2, cx| {
|
||||
assert!(
|
||||
search_view.results_editor.is_focused(cx),
|
||||
search_view_2.results_editor.is_focused(cx),
|
||||
"Search view with matching query should switch focus to the results editor after the toggle focus event",
|
||||
);
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ theme = { package = "theme2", path = "../theme2" }
|
|||
util = { path = "../util" }
|
||||
ui = {package = "ui2", path = "../ui2"}
|
||||
workspace = { package = "workspace2", path = "../workspace2" }
|
||||
#semantic_index = { path = "../semantic_index" }
|
||||
semantic_index = { package = "semantic_index2", path = "../semantic_index2" }
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
log.workspace = true
|
||||
|
|
|
@ -10,15 +10,15 @@ use collections::HashMap;
|
|||
use editor::{Editor, EditorMode};
|
||||
use futures::channel::oneshot;
|
||||
use gpui::{
|
||||
actions, div, red, Action, AppContext, Div, EventEmitter, FocusableView,
|
||||
InteractiveElement as _, IntoElement, ParentElement as _, Render, Styled, Subscription, Task,
|
||||
View, ViewContext, VisualContext as _, WeakView, WindowContext,
|
||||
actions, div, impl_actions, red, Action, AppContext, Div, EventEmitter, FocusableView,
|
||||
InteractiveElement as _, IntoElement, KeyContext, ParentElement as _, Render, Styled,
|
||||
Subscription, Task, View, ViewContext, VisualContext as _, WeakView, WindowContext,
|
||||
};
|
||||
use project::search::SearchQuery;
|
||||
use serde::Deserialize;
|
||||
use std::{any::Any, sync::Arc};
|
||||
|
||||
use ui::{h_stack, Clickable, Icon, IconButton, IconElement};
|
||||
use ui::{h_stack, ButtonCommon, Clickable, Icon, IconButton, IconElement, Tooltip};
|
||||
use util::ResultExt;
|
||||
use workspace::{
|
||||
item::ItemHandle,
|
||||
|
@ -26,12 +26,14 @@ use workspace::{
|
|||
ToolbarItemLocation, ToolbarItemView,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, Action)]
|
||||
#[derive(PartialEq, Clone, Deserialize)]
|
||||
pub struct Deploy {
|
||||
pub focus: bool,
|
||||
}
|
||||
|
||||
actions!(Dismiss, FocusEditor);
|
||||
impl_actions!(buffer_search, [Deploy]);
|
||||
|
||||
actions!(buffer_search, [Dismiss, FocusEditor]);
|
||||
|
||||
pub enum Event {
|
||||
UpdateLocation,
|
||||
|
@ -131,13 +133,7 @@ impl Render for BufferSearchBar {
|
|||
let search_button_for_mode = |mode| {
|
||||
let is_active = self.current_mode == mode;
|
||||
|
||||
render_search_mode_button(
|
||||
mode,
|
||||
is_active,
|
||||
cx.listener(move |this, _, cx| {
|
||||
this.activate_search_mode(mode, cx);
|
||||
}),
|
||||
)
|
||||
render_search_mode_button(mode, is_active)
|
||||
};
|
||||
let search_option_button = |option| {
|
||||
let is_active = self.search_options.contains(option);
|
||||
|
@ -163,23 +159,35 @@ impl Render for BufferSearchBar {
|
|||
});
|
||||
let should_show_replace_input = self.replace_enabled && supported_options.replacement;
|
||||
let replace_all = should_show_replace_input
|
||||
.then(|| super::render_replace_button(ReplaceAll, ui::Icon::ReplaceAll));
|
||||
let replace_next = should_show_replace_input
|
||||
.then(|| super::render_replace_button(ReplaceNext, ui::Icon::Replace));
|
||||
.then(|| super::render_replace_button(ReplaceAll, ui::Icon::ReplaceAll, "Replace all"));
|
||||
let replace_next = should_show_replace_input.then(|| {
|
||||
super::render_replace_button(ReplaceNext, ui::Icon::ReplaceNext, "Replace next")
|
||||
});
|
||||
let in_replace = self.replacement_editor.focus_handle(cx).is_focused(cx);
|
||||
|
||||
let mut key_context = KeyContext::default();
|
||||
key_context.add("BufferSearchBar");
|
||||
if in_replace {
|
||||
key_context.add("in_replace");
|
||||
}
|
||||
|
||||
h_stack()
|
||||
.key_context("BufferSearchBar")
|
||||
.key_context(key_context)
|
||||
.on_action(cx.listener(Self::previous_history_query))
|
||||
.on_action(cx.listener(Self::next_history_query))
|
||||
.on_action(cx.listener(Self::dismiss))
|
||||
.on_action(cx.listener(Self::select_next_match))
|
||||
.on_action(cx.listener(Self::select_prev_match))
|
||||
.on_action(cx.listener(|this, _: &ActivateRegexMode, cx| {
|
||||
this.activate_search_mode(SearchMode::Regex, cx);
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &ActivateTextMode, cx| {
|
||||
this.activate_search_mode(SearchMode::Text, cx);
|
||||
}))
|
||||
.when(self.supported_options().replacement, |this| {
|
||||
this.on_action(cx.listener(Self::toggle_replace))
|
||||
.when(in_replace, |this| {
|
||||
this.key_context("in_replace")
|
||||
.on_action(cx.listener(Self::replace_next))
|
||||
this.on_action(cx.listener(Self::replace_next))
|
||||
.on_action(cx.listener(Self::replace_all))
|
||||
})
|
||||
})
|
||||
|
@ -238,21 +246,19 @@ impl Render for BufferSearchBar {
|
|||
h_stack()
|
||||
.gap_0p5()
|
||||
.flex_none()
|
||||
.child(self.render_action_button(cx))
|
||||
.child(self.render_action_button())
|
||||
.children(match_count)
|
||||
.child(render_nav_button(
|
||||
ui::Icon::ChevronLeft,
|
||||
self.active_match_index.is_some(),
|
||||
cx.listener(move |this, _, cx| {
|
||||
this.select_prev_match(&Default::default(), cx);
|
||||
}),
|
||||
"Select previous match",
|
||||
&SelectPrevMatch,
|
||||
))
|
||||
.child(render_nav_button(
|
||||
ui::Icon::ChevronRight,
|
||||
self.active_match_index.is_some(),
|
||||
cx.listener(move |this, _, cx| {
|
||||
this.select_next_match(&Default::default(), cx);
|
||||
}),
|
||||
"Select next match",
|
||||
&SelectNextMatch,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
@ -597,14 +603,10 @@ impl BufferSearchBar {
|
|||
self.update_matches(cx)
|
||||
}
|
||||
|
||||
fn render_action_button(&self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
// let tooltip_style = theme.tooltip.clone();
|
||||
|
||||
// let style = theme.search.action_button.clone();
|
||||
|
||||
IconButton::new("select-all", ui::Icon::SelectAll).on_click(cx.listener(|this, _, cx| {
|
||||
this.select_all_matches(&SelectAllMatches, cx);
|
||||
}))
|
||||
fn render_action_button(&self) -> impl IntoElement {
|
||||
IconButton::new("select-all", ui::Icon::SelectAll)
|
||||
.on_click(|_, cx| cx.dispatch_action(SelectAllMatches.boxed_clone()))
|
||||
.tooltip(|cx| Tooltip::for_action("Select all matches", &SelectAllMatches, cx))
|
||||
}
|
||||
|
||||
pub fn activate_search_mode(&mut self, mode: SearchMode, cx: &mut ViewContext<Self>) {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
use gpui::{Action, SharedString};
|
||||
|
||||
use crate::{ActivateRegexMode, ActivateSemanticMode, ActivateTextMode};
|
||||
|
||||
// TODO: Update the default search mode to get from config
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub enum SearchMode {
|
||||
|
@ -15,6 +19,16 @@ impl SearchMode {
|
|||
SearchMode::Regex => "Regex",
|
||||
}
|
||||
}
|
||||
pub(crate) fn tooltip(&self) -> SharedString {
|
||||
format!("Activate {} Mode", self.label()).into()
|
||||
}
|
||||
pub(crate) fn action(&self) -> Box<dyn Action> {
|
||||
match self {
|
||||
SearchMode::Text => ActivateTextMode.boxed_clone(),
|
||||
SearchMode::Semantic => ActivateSemanticMode.boxed_clone(),
|
||||
SearchMode::Regex => ActivateRegexMode.boxed_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next_mode(mode: &SearchMode, semantic_enabled: bool) -> SearchMode {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,7 @@ pub use buffer_search::BufferSearchBar;
|
|||
use gpui::{actions, Action, AppContext, IntoElement};
|
||||
pub use mode::SearchMode;
|
||||
use project::search::SearchQuery;
|
||||
use ui::prelude::*;
|
||||
use ui::{prelude::*, Tooltip};
|
||||
use ui::{ButtonStyle, Icon, IconButton};
|
||||
//pub use project_search::{ProjectSearchBar, ProjectSearchView};
|
||||
// use theme::components::{
|
||||
|
@ -13,29 +13,32 @@ use ui::{ButtonStyle, Icon, IconButton};
|
|||
pub mod buffer_search;
|
||||
mod history;
|
||||
mod mode;
|
||||
//pub mod project_search;
|
||||
pub mod project_search;
|
||||
pub(crate) mod search_bar;
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
buffer_search::init(cx);
|
||||
//project_search::init(cx);
|
||||
project_search::init(cx);
|
||||
}
|
||||
|
||||
actions!(
|
||||
CycleMode,
|
||||
ToggleWholeWord,
|
||||
ToggleCaseSensitive,
|
||||
ToggleReplace,
|
||||
SelectNextMatch,
|
||||
SelectPrevMatch,
|
||||
SelectAllMatches,
|
||||
NextHistoryQuery,
|
||||
PreviousHistoryQuery,
|
||||
ActivateTextMode,
|
||||
ActivateSemanticMode,
|
||||
ActivateRegexMode,
|
||||
ReplaceAll,
|
||||
ReplaceNext,
|
||||
search,
|
||||
[
|
||||
CycleMode,
|
||||
ToggleWholeWord,
|
||||
ToggleCaseSensitive,
|
||||
ToggleReplace,
|
||||
SelectNextMatch,
|
||||
SelectPrevMatch,
|
||||
SelectAllMatches,
|
||||
NextHistoryQuery,
|
||||
PreviousHistoryQuery,
|
||||
ActivateTextMode,
|
||||
ActivateSemanticMode,
|
||||
ActivateRegexMode,
|
||||
ReplaceAll,
|
||||
ReplaceNext,
|
||||
]
|
||||
);
|
||||
|
||||
bitflags! {
|
||||
|
@ -44,6 +47,7 @@ bitflags! {
|
|||
const NONE = 0b000;
|
||||
const WHOLE_WORD = 0b001;
|
||||
const CASE_SENSITIVE = 0b010;
|
||||
const INCLUDE_IGNORED = 0b100;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +88,7 @@ impl SearchOptions {
|
|||
}
|
||||
|
||||
pub fn as_button(&self, active: bool) -> impl IntoElement {
|
||||
IconButton::new(0, self.icon())
|
||||
IconButton::new(self.label(), self.icon())
|
||||
.on_click({
|
||||
let action = self.to_toggle_action();
|
||||
move |_, cx| {
|
||||
|
@ -93,26 +97,38 @@ impl SearchOptions {
|
|||
})
|
||||
.style(ButtonStyle::Subtle)
|
||||
.when(active, |button| button.style(ButtonStyle::Filled))
|
||||
.tooltip({
|
||||
let action = self.to_toggle_action();
|
||||
let label: SharedString = format!("Toggle {}", self.label()).into();
|
||||
move |cx| Tooltip::for_action(label.clone(), &*action, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_replace_button(active: bool) -> impl IntoElement {
|
||||
// todo: add toggle_replace button
|
||||
IconButton::new(0, Icon::Replace)
|
||||
IconButton::new("buffer-search-bar-toggle-replace-button", Icon::Replace)
|
||||
.on_click(|_, cx| {
|
||||
cx.dispatch_action(Box::new(ToggleReplace));
|
||||
cx.notify();
|
||||
})
|
||||
.style(ButtonStyle::Subtle)
|
||||
.when(active, |button| button.style(ButtonStyle::Filled))
|
||||
.tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx))
|
||||
}
|
||||
|
||||
fn render_replace_button(
|
||||
action: impl Action + 'static + Send + Sync,
|
||||
icon: Icon,
|
||||
tooltip: &'static str,
|
||||
) -> impl IntoElement {
|
||||
// todo: add tooltip
|
||||
IconButton::new(0, icon).on_click(move |_, cx| {
|
||||
cx.dispatch_action(action.boxed_clone());
|
||||
})
|
||||
let id: SharedString = format!("search-replace-{}", action.name()).into();
|
||||
IconButton::new(id, icon)
|
||||
.tooltip({
|
||||
let action = action.boxed_clone();
|
||||
move |cx| Tooltip::for_action(tooltip, &*action, cx)
|
||||
})
|
||||
.on_click(move |_, cx| {
|
||||
cx.dispatch_action(action.boxed_clone());
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,30 +1,36 @@
|
|||
use gpui::{ClickEvent, IntoElement, WindowContext};
|
||||
use ui::prelude::*;
|
||||
use gpui::{Action, IntoElement};
|
||||
use ui::{prelude::*, Tooltip};
|
||||
use ui::{Button, IconButton};
|
||||
|
||||
use crate::mode::SearchMode;
|
||||
|
||||
pub(super) fn render_nav_button(
|
||||
icon: ui::Icon,
|
||||
_active: bool,
|
||||
on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
||||
active: bool,
|
||||
tooltip: &'static str,
|
||||
action: &'static dyn Action,
|
||||
) -> impl IntoElement {
|
||||
// let tooltip_style = cx.theme().tooltip.clone();
|
||||
// let cursor_style = if active {
|
||||
// CursorStyle::PointingHand
|
||||
// } else {
|
||||
// CursorStyle::default()
|
||||
// };
|
||||
// enum NavButton {}
|
||||
IconButton::new("search-nav-button", icon).on_click(on_click)
|
||||
IconButton::new(
|
||||
SharedString::from(format!("search-nav-button-{}", action.name())),
|
||||
icon,
|
||||
)
|
||||
.on_click(|_, cx| cx.dispatch_action(action.boxed_clone()))
|
||||
.tooltip(move |cx| Tooltip::for_action(tooltip, action, cx))
|
||||
.disabled(!active)
|
||||
}
|
||||
|
||||
pub(crate) fn render_search_mode_button(
|
||||
mode: SearchMode,
|
||||
is_active: bool,
|
||||
on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Button {
|
||||
pub(crate) fn render_search_mode_button(mode: SearchMode, is_active: bool) -> Button {
|
||||
Button::new(mode.label(), mode.label())
|
||||
.selected(is_active)
|
||||
.on_click(on_click)
|
||||
.on_click({
|
||||
let action = mode.action();
|
||||
move |_, cx| {
|
||||
cx.dispatch_action(action.boxed_clone());
|
||||
}
|
||||
})
|
||||
.tooltip({
|
||||
let action = mode.action();
|
||||
let tooltip_text = mode.tooltip();
|
||||
move |cx| Tooltip::for_action(tooltip_text.clone(), &*action, cx)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::BTreeMap;
|
||||
use gpui::{actions, Action, AppContext, KeyBinding, SharedString};
|
||||
use gpui::{Action, AppContext, KeyBinding, SharedString};
|
||||
use schemars::{
|
||||
gen::{SchemaGenerator, SchemaSettings},
|
||||
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
|
||||
|
@ -137,10 +137,8 @@ impl KeymapFile {
|
|||
}
|
||||
}
|
||||
|
||||
actions!(NoAction);
|
||||
|
||||
fn no_action() -> Box<dyn gpui::Action> {
|
||||
NoAction.boxed_clone()
|
||||
gpui::NoAction.boxed_clone()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -4,7 +4,7 @@ use gpui::{
|
|||
};
|
||||
use ui::prelude::*;
|
||||
|
||||
actions!(ActionA, ActionB, ActionC);
|
||||
actions!(focus, [ActionA, ActionB, ActionC]);
|
||||
|
||||
pub struct FocusStory {
|
||||
child_1_focus: FocusHandle,
|
||||
|
|
|
@ -59,13 +59,16 @@ use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
|
|||
use lazy_static::lazy_static;
|
||||
|
||||
actions!(
|
||||
Clear,
|
||||
Copy,
|
||||
Paste,
|
||||
ShowCharacterPalette,
|
||||
SearchTest,
|
||||
SendText,
|
||||
SendKeystroke,
|
||||
terminal,
|
||||
[
|
||||
Clear,
|
||||
Copy,
|
||||
Paste,
|
||||
ShowCharacterPalette,
|
||||
SearchTest,
|
||||
SendText,
|
||||
SendKeystroke,
|
||||
]
|
||||
);
|
||||
|
||||
///Scrolling is unbearably sluggish by default. Alacritty supports a configurable
|
||||
|
|
|
@ -5,7 +5,7 @@ use gpui::{
|
|||
FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState,
|
||||
IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton, Pixels,
|
||||
PlatformInputHandler, Point, Rgba, ShapedLine, Size, StatefulInteractiveElement, Styled,
|
||||
TextRun, TextStyle, TextSystem, UnderlineStyle, View, WhiteSpace, WindowContext,
|
||||
TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::CursorShape;
|
||||
|
@ -27,8 +27,6 @@ use ui::Tooltip;
|
|||
use std::mem;
|
||||
use std::{fmt::Debug, ops::RangeInclusive};
|
||||
|
||||
use crate::TerminalView;
|
||||
|
||||
///The information generated during layout that is necessary for painting
|
||||
pub struct LayoutState {
|
||||
cells: Vec<LayoutCell>,
|
||||
|
@ -149,7 +147,6 @@ impl LayoutRect {
|
|||
///We need to keep a reference to the view for mouse events, do we need it for any other terminal stuff, or can we move that to connection?
|
||||
pub struct TerminalElement {
|
||||
terminal: Model<Terminal>,
|
||||
terminal_view: View<TerminalView>,
|
||||
focus: FocusHandle,
|
||||
focused: bool,
|
||||
cursor_visible: bool,
|
||||
|
@ -168,7 +165,6 @@ impl StatefulInteractiveElement for TerminalElement {}
|
|||
impl TerminalElement {
|
||||
pub fn new(
|
||||
terminal: Model<Terminal>,
|
||||
terminal_view: View<TerminalView>,
|
||||
focus: FocusHandle,
|
||||
focused: bool,
|
||||
cursor_visible: bool,
|
||||
|
@ -176,7 +172,6 @@ impl TerminalElement {
|
|||
) -> TerminalElement {
|
||||
TerminalElement {
|
||||
terminal,
|
||||
terminal_view,
|
||||
focused,
|
||||
focus: focus.clone(),
|
||||
cursor_visible,
|
||||
|
@ -474,6 +469,7 @@ impl TerminalElement {
|
|||
.size_full()
|
||||
.id("terminal-element")
|
||||
.tooltip(move |cx| Tooltip::text(hovered_word.word.clone(), cx))
|
||||
.into_any_element()
|
||||
});
|
||||
|
||||
let TerminalContent {
|
||||
|
@ -575,7 +571,7 @@ impl TerminalElement {
|
|||
relative_highlighted_ranges,
|
||||
mode: *mode,
|
||||
display_offset: *display_offset,
|
||||
hyperlink_tooltip: None, // todo!(tooltips)
|
||||
hyperlink_tooltip,
|
||||
gutter,
|
||||
}
|
||||
}
|
||||
|
@ -774,18 +770,11 @@ impl Element for TerminalElement {
|
|||
(layout_id, interactive_state)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::State,
|
||||
cx: &mut WindowContext<'_>,
|
||||
) {
|
||||
fn paint(self, bounds: Bounds<Pixels>, state: &mut Self::State, cx: &mut WindowContext<'_>) {
|
||||
let mut layout = self.compute_layout(bounds, cx);
|
||||
|
||||
let theme = cx.theme();
|
||||
|
||||
let dispatch_context = self.terminal_view.read(cx).dispatch_context(cx);
|
||||
self.interactivity().key_context = Some(dispatch_context);
|
||||
cx.paint_quad(
|
||||
bounds,
|
||||
Default::default(),
|
||||
|
|
|
@ -24,7 +24,7 @@ use anyhow::Result;
|
|||
|
||||
const TERMINAL_PANEL_KEY: &'static str = "TerminalPanel";
|
||||
|
||||
actions!(ToggleFocus);
|
||||
actions!(terminal_view, [ToggleFocus]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
cx.observe_new_views(
|
||||
|
|
|
@ -9,9 +9,10 @@ pub mod terminal_panel;
|
|||
// use crate::terminal_element::TerminalElement;
|
||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
||||
use gpui::{
|
||||
div, Action, AnyElement, AppContext, Div, EventEmitter, FocusEvent, FocusHandle, Focusable,
|
||||
FocusableElement, FocusableView, KeyContext, KeyDownEvent, Keystroke, Model, MouseButton,
|
||||
MouseDownEvent, Pixels, Render, Subscription, Task, View, VisualContext, WeakView,
|
||||
div, impl_actions, overlay, AnyElement, AppContext, DismissEvent, Div, EventEmitter,
|
||||
FocusEvent, FocusHandle, Focusable, FocusableElement, FocusableView, KeyContext, KeyDownEvent,
|
||||
Keystroke, Model, MouseButton, MouseDownEvent, Pixels, Render, Styled, Subscription, Task,
|
||||
View, VisualContext, WeakView,
|
||||
};
|
||||
use language::Bias;
|
||||
use persistence::TERMINAL_DB;
|
||||
|
@ -54,12 +55,14 @@ const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
|
|||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ScrollTerminal(pub i32);
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Action)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
|
||||
pub struct SendText(String);
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Action)]
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
|
||||
pub struct SendKeystroke(String);
|
||||
|
||||
impl_actions!(terminal_view, [SendText, SendKeystroke]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
terminal_panel::init(cx);
|
||||
terminal::init(cx);
|
||||
|
@ -81,7 +84,7 @@ pub struct TerminalView {
|
|||
has_new_content: bool,
|
||||
//Currently using iTerm bell, show bell emoji in tab until input is received
|
||||
has_bell: bool,
|
||||
context_menu: Option<View<ContextMenu>>,
|
||||
context_menu: Option<(View<ContextMenu>, gpui::Point<Pixels>, Subscription)>,
|
||||
blink_state: bool,
|
||||
blinking_on: bool,
|
||||
blinking_paused: bool,
|
||||
|
@ -312,14 +315,24 @@ impl TerminalView {
|
|||
position: gpui::Point<Pixels>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.context_menu = Some(ContextMenu::build(cx, |menu, cx| {
|
||||
let context_menu = ContextMenu::build(cx, |menu, cx| {
|
||||
menu.action("Clear", Box::new(Clear))
|
||||
.action("Close", Box::new(CloseActiveItem { save_intent: None }))
|
||||
}));
|
||||
// todo!(context menus)
|
||||
// self.context_menu
|
||||
// .show(position, AnchorCorner::TopLeft, menu_entries, cx);
|
||||
// cx.notify();
|
||||
});
|
||||
|
||||
cx.focus_view(&context_menu);
|
||||
let subscription =
|
||||
cx.subscribe(&context_menu, |this, _, _: &DismissEvent, cx| {
|
||||
if this.context_menu.as_ref().is_some_and(|context_menu| {
|
||||
context_menu.0.focus_handle(cx).contains_focused(cx)
|
||||
}) {
|
||||
cx.focus_self();
|
||||
}
|
||||
this.context_menu.take();
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
self.context_menu = Some((context_menu, position, subscription));
|
||||
}
|
||||
|
||||
fn show_character_palette(&mut self, _: &ShowCharacterPalette, cx: &mut ViewContext<Self>) {
|
||||
|
@ -621,7 +634,6 @@ impl Render for TerminalView {
|
|||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let terminal_handle = self.terminal.clone();
|
||||
let this_view = cx.view().clone();
|
||||
|
||||
let self_id = cx.entity_id();
|
||||
let focused = self.focus_handle.is_focused(cx);
|
||||
|
@ -629,43 +641,41 @@ impl Render for TerminalView {
|
|||
div()
|
||||
.size_full()
|
||||
.relative()
|
||||
.child(
|
||||
div()
|
||||
.z_index(0)
|
||||
.absolute()
|
||||
.size_full()
|
||||
.on_key_down(cx.listener(Self::key_down))
|
||||
.on_action(cx.listener(TerminalView::send_text))
|
||||
.on_action(cx.listener(TerminalView::send_keystroke))
|
||||
.on_action(cx.listener(TerminalView::copy))
|
||||
.on_action(cx.listener(TerminalView::paste))
|
||||
.on_action(cx.listener(TerminalView::clear))
|
||||
.on_action(cx.listener(TerminalView::show_character_palette))
|
||||
.on_action(cx.listener(TerminalView::select_all))
|
||||
.child(TerminalElement::new(
|
||||
terminal_handle,
|
||||
this_view,
|
||||
self.focus_handle.clone(),
|
||||
focused,
|
||||
self.should_show_cursor(focused, cx),
|
||||
self.can_navigate_to_selected_word,
|
||||
))
|
||||
.on_mouse_down(
|
||||
MouseButton::Right,
|
||||
cx.listener(|this, event: &MouseDownEvent, cx| {
|
||||
this.deploy_context_menu(event.position, cx);
|
||||
cx.notify();
|
||||
}),
|
||||
),
|
||||
)
|
||||
.children(
|
||||
self.context_menu
|
||||
.clone()
|
||||
.map(|context_menu| div().z_index(1).absolute().child(context_menu)),
|
||||
)
|
||||
.track_focus(&self.focus_handle)
|
||||
.key_context(self.dispatch_context(cx))
|
||||
.on_action(cx.listener(TerminalView::send_text))
|
||||
.on_action(cx.listener(TerminalView::send_keystroke))
|
||||
.on_action(cx.listener(TerminalView::copy))
|
||||
.on_action(cx.listener(TerminalView::paste))
|
||||
.on_action(cx.listener(TerminalView::clear))
|
||||
.on_action(cx.listener(TerminalView::show_character_palette))
|
||||
.on_action(cx.listener(TerminalView::select_all))
|
||||
.on_focus_in(cx.listener(Self::focus_in))
|
||||
.on_focus_out(cx.listener(Self::focus_out))
|
||||
.on_key_down(cx.listener(Self::key_down))
|
||||
.on_mouse_down(
|
||||
MouseButton::Right,
|
||||
cx.listener(|this, event: &MouseDownEvent, cx| {
|
||||
this.deploy_context_menu(event.position, cx);
|
||||
cx.notify();
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
// TODO: Oddly this wrapper div is needed for TerminalElement to not steal events from the context menu
|
||||
div().size_full().child(TerminalElement::new(
|
||||
terminal_handle,
|
||||
self.focus_handle.clone(),
|
||||
focused,
|
||||
self.should_show_cursor(focused, cx),
|
||||
self.can_navigate_to_selected_word,
|
||||
)),
|
||||
)
|
||||
.children(self.context_menu.as_ref().map(|(menu, positon, _)| {
|
||||
overlay()
|
||||
.position(*positon)
|
||||
.anchor(gpui::AnchorCorner::TopLeft)
|
||||
.child(menu.clone())
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@ use crate::{
|
|||
SystemColors, Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThemeMeta {
|
||||
pub name: SharedString,
|
||||
pub appearance: Appearance,
|
||||
}
|
||||
|
||||
pub struct ThemeRegistry {
|
||||
themes: HashMap<SharedString, Arc<Theme>>,
|
||||
}
|
||||
|
@ -94,8 +100,11 @@ impl ThemeRegistry {
|
|||
self.themes.keys().cloned()
|
||||
}
|
||||
|
||||
pub fn list(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
|
||||
self.themes.values().map(|theme| theme.name.clone())
|
||||
pub fn list(&self, _staff: bool) -> impl Iterator<Item = ThemeMeta> + '_ {
|
||||
self.themes.values().map(|theme| ThemeMeta {
|
||||
name: theme.name.clone(),
|
||||
appearance: theme.appearance(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
|
||||
|
|
|
@ -31,6 +31,15 @@ pub enum Appearance {
|
|||
Dark,
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
pub fn is_light(&self) -> bool {
|
||||
match self {
|
||||
Self::Light => true,
|
||||
Self::Dark => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum LoadThemes {
|
||||
/// Only load the base theme.
|
||||
|
|
|
@ -25,7 +25,7 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x1b1d23ff).into()),
|
||||
border_transparent: Some(rgba(0x1b1d23ff).into()),
|
||||
border_disabled: Some(rgba(0x1b1d23ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x23262eff).into()),
|
||||
elevated_surface_background: Some(rgba(0x2b303bff).into()),
|
||||
surface_background: Some(rgba(0x23262eff).into()),
|
||||
background: Some(rgba(0x23262eff).into()),
|
||||
element_background: Some(rgba(0x00e8c5cc).into()),
|
||||
|
@ -33,6 +33,7 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
element_selected: Some(rgba(0x23262eff).into()),
|
||||
drop_target_background: Some(rgba(0x3a404eff).into()),
|
||||
ghost_element_hover: Some(rgba(0x23262eff).into()),
|
||||
ghost_element_selected: Some(rgba(0x23262eff).into()),
|
||||
text: Some(rgba(0xd5ced9ff).into()),
|
||||
status_bar_background: Some(rgba(0x23262eff).into()),
|
||||
title_bar_background: Some(rgba(0x23262eff).into()),
|
||||
|
@ -59,10 +60,13 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0xfc644dff).into()),
|
||||
created: Some(rgba(0x9bc53dbb).into()),
|
||||
deleted: Some(rgba(0xfc644dbb).into()),
|
||||
error: Some(rgba(0xfc644dff).into()),
|
||||
hidden: Some(rgba(0x746f77ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x555555ff).into()),
|
||||
modified: Some(rgba(0x5bc0ebbb).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -88,6 +92,20 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xee5d43ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf92672ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -137,6 +155,34 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -154,7 +200,7 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xee5d43ff).into()),
|
||||
color: Some(rgba(0xffe66dff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -187,7 +233,7 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x1b1d23ff).into()),
|
||||
border_transparent: Some(rgba(0x1b1d23ff).into()),
|
||||
border_disabled: Some(rgba(0x1b1d23ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x23262eff).into()),
|
||||
elevated_surface_background: Some(rgba(0x2b303bff).into()),
|
||||
surface_background: Some(rgba(0x23262eff).into()),
|
||||
background: Some(rgba(0x262a33ff).into()),
|
||||
element_background: Some(rgba(0x00e8c5cc).into()),
|
||||
|
@ -195,10 +241,11 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
element_selected: Some(rgba(0x23262eff).into()),
|
||||
drop_target_background: Some(rgba(0x3a404eff).into()),
|
||||
ghost_element_hover: Some(rgba(0x23262eff).into()),
|
||||
ghost_element_selected: Some(rgba(0x23262eff).into()),
|
||||
text: Some(rgba(0xd5ced9ff).into()),
|
||||
status_bar_background: Some(rgba(0x23262eff).into()),
|
||||
title_bar_background: Some(rgba(0x23262eff).into()),
|
||||
toolbar_background: Some(rgba(0x23262eff).into()),
|
||||
toolbar_background: Some(rgba(0x262a33ff).into()),
|
||||
tab_bar_background: Some(rgba(0x23262eff).into()),
|
||||
tab_inactive_background: Some(rgba(0x23262eff).into()),
|
||||
tab_active_background: Some(rgba(0x262a33ff).into()),
|
||||
|
@ -221,10 +268,13 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0xfc644dff).into()),
|
||||
created: Some(rgba(0x9bc53dbb).into()),
|
||||
deleted: Some(rgba(0xfc644dbb).into()),
|
||||
error: Some(rgba(0xfc644dff).into()),
|
||||
hidden: Some(rgba(0x746f77ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x555555ff).into()),
|
||||
modified: Some(rgba(0x5bc0ebbb).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -250,6 +300,20 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xee5d43ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf92672ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -299,6 +363,34 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x96e072ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -316,7 +408,7 @@ pub fn andromeda() -> UserThemeFamily {
|
|||
(
|
||||
"type".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xee5d43ff).into()),
|
||||
color: Some(rgba(0xffe66dff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
|
|
@ -25,13 +25,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x6b7d8f1f).into()),
|
||||
border_transparent: Some(rgba(0x6b7d8f1f).into()),
|
||||
border_disabled: Some(rgba(0x6b7d8f1f).into()),
|
||||
elevated_surface_background: Some(rgba(0xf8f9faff).into()),
|
||||
elevated_surface_background: Some(rgba(0xfcfcfcff).into()),
|
||||
surface_background: Some(rgba(0xf8f9faff).into()),
|
||||
background: Some(rgba(0xf8f9faff).into()),
|
||||
element_background: Some(rgba(0xffaa33ff).into()),
|
||||
element_hover: Some(rgba(0x56728f1f).into()),
|
||||
element_selected: Some(rgba(0x56728f1f).into()),
|
||||
ghost_element_hover: Some(rgba(0x56728f1f).into()),
|
||||
ghost_element_selected: Some(rgba(0x56728f1f).into()),
|
||||
text: Some(rgba(0x8a9199ff).into()),
|
||||
status_bar_background: Some(rgba(0xf8f9faff).into()),
|
||||
title_bar_background: Some(rgba(0xf8f9faff).into()),
|
||||
|
@ -63,10 +64,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0xe65050ff).into()),
|
||||
created: Some(rgba(0x6cbf43cc).into()),
|
||||
deleted: Some(rgba(0xff7383cc).into()),
|
||||
error: Some(rgba(0xe65050ff).into()),
|
||||
hidden: Some(rgba(0x8a9199ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x8a919980).into()),
|
||||
modified: Some(rgba(0x478acccc).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -104,7 +108,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4cbf99ff).into()),
|
||||
color: Some(rgba(0xa37accff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x55b4d4ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -134,14 +145,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf07171ff).into()),
|
||||
color: Some(rgba(0xf2ae49ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x55b4d4ff).into()),
|
||||
color: Some(rgba(0xfa8d3eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -183,14 +194,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x55b4d4ff).into()),
|
||||
color: Some(rgba(0xf07171ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c6166b3).into()),
|
||||
color: Some(rgba(0x5c6166ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -215,6 +226,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5c6166ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -229,6 +247,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x86b300ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -281,7 +306,7 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"variable.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf2ae49ff).into()),
|
||||
color: Some(rgba(0xf07171ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -300,13 +325,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x171b24ff).into()),
|
||||
border_transparent: Some(rgba(0x171b24ff).into()),
|
||||
border_disabled: Some(rgba(0x171b24ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x1f2430ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x242936ff).into()),
|
||||
surface_background: Some(rgba(0x1f2430ff).into()),
|
||||
background: Some(rgba(0x1f2430ff).into()),
|
||||
element_background: Some(rgba(0xffcc66ff).into()),
|
||||
element_hover: Some(rgba(0x63759926).into()),
|
||||
element_selected: Some(rgba(0x63759926).into()),
|
||||
ghost_element_hover: Some(rgba(0x63759926).into()),
|
||||
ghost_element_selected: Some(rgba(0x63759926).into()),
|
||||
text: Some(rgba(0x707a8cff).into()),
|
||||
status_bar_background: Some(rgba(0x1f2430ff).into()),
|
||||
title_bar_background: Some(rgba(0x1f2430ff).into()),
|
||||
|
@ -338,10 +364,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0xff6666ff).into()),
|
||||
created: Some(rgba(0x87d96ccc).into()),
|
||||
deleted: Some(rgba(0xf27983cc).into()),
|
||||
error: Some(rgba(0xff6666ff).into()),
|
||||
hidden: Some(rgba(0x707a8cff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x707a8c80).into()),
|
||||
modified: Some(rgba(0x80bfffcc).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -379,7 +408,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x95e6cbff).into()),
|
||||
color: Some(rgba(0xdfbfffff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5ccfe6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -409,14 +445,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf28779ff).into()),
|
||||
color: Some(rgba(0xffd173ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5ccfe6ff).into()),
|
||||
color: Some(rgba(0xffad66ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -458,14 +494,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x5ccfe6ff).into()),
|
||||
color: Some(rgba(0xf28779ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcccac2b3).into()),
|
||||
color: Some(rgba(0xcccac2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -490,6 +526,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcccac2ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -504,6 +547,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd5ff80ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -556,7 +606,7 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"variable.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xffd173ff).into()),
|
||||
color: Some(rgba(0xf28779ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -575,13 +625,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x1e232bff).into()),
|
||||
border_transparent: Some(rgba(0x1e232bff).into()),
|
||||
border_disabled: Some(rgba(0x1e232bff).into()),
|
||||
elevated_surface_background: Some(rgba(0x0b0e14ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x0d1017ff).into()),
|
||||
surface_background: Some(rgba(0x0b0e14ff).into()),
|
||||
background: Some(rgba(0x0b0e14ff).into()),
|
||||
element_background: Some(rgba(0xe6b450ff).into()),
|
||||
element_hover: Some(rgba(0x47526640).into()),
|
||||
element_selected: Some(rgba(0x47526640).into()),
|
||||
ghost_element_hover: Some(rgba(0x47526640).into()),
|
||||
ghost_element_selected: Some(rgba(0x47526640).into()),
|
||||
text: Some(rgba(0x565b66ff).into()),
|
||||
status_bar_background: Some(rgba(0x0b0e14ff).into()),
|
||||
title_bar_background: Some(rgba(0x0b0e14ff).into()),
|
||||
|
@ -613,10 +664,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0xd95757ff).into()),
|
||||
created: Some(rgba(0x7fd962cc).into()),
|
||||
deleted: Some(rgba(0xf26d78cc).into()),
|
||||
error: Some(rgba(0xd95757ff).into()),
|
||||
hidden: Some(rgba(0x565b66ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x565b6680).into()),
|
||||
modified: Some(rgba(0x73b8ffcc).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -654,7 +708,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x95e6cbff).into()),
|
||||
color: Some(rgba(0xd2a6ffff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x39bae6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -684,14 +745,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf07178ff).into()),
|
||||
color: Some(rgba(0xffb454ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"keyword".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x39bae6ff).into()),
|
||||
color: Some(rgba(0xff8f40ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -733,14 +794,14 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x39bae6ff).into()),
|
||||
color: Some(rgba(0xf07178ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xbfbdb6b3).into()),
|
||||
color: Some(rgba(0xbfbdb6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -765,6 +826,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xbfbdb6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -779,6 +847,13 @@ pub fn ayu() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xaad94cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -831,7 +906,7 @@ pub fn ayu() -> UserThemeFamily {
|
|||
(
|
||||
"variable.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xffb454ff).into()),
|
||||
color: Some(rgba(0xf07178ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
|
|
@ -24,7 +24,7 @@ pub fn dracula() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0xbd93f9ff).into()),
|
||||
border_transparent: Some(rgba(0xbd93f9ff).into()),
|
||||
border_disabled: Some(rgba(0xbd93f9ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x282a36ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x343746ff).into()),
|
||||
surface_background: Some(rgba(0x282a36ff).into()),
|
||||
background: Some(rgba(0x282a36ff).into()),
|
||||
element_background: Some(rgba(0x44475aff).into()),
|
||||
|
@ -32,11 +32,12 @@ pub fn dracula() -> UserThemeFamily {
|
|||
element_selected: Some(rgba(0x44475aff).into()),
|
||||
drop_target_background: Some(rgba(0x44475aff).into()),
|
||||
ghost_element_hover: Some(rgba(0x44475a75).into()),
|
||||
ghost_element_selected: Some(rgba(0x44475aff).into()),
|
||||
text: Some(rgba(0xf8f8f2ff).into()),
|
||||
status_bar_background: Some(rgba(0x191a21ff).into()),
|
||||
title_bar_background: Some(rgba(0x21222cff).into()),
|
||||
toolbar_background: Some(rgba(0x282a36ff).into()),
|
||||
tab_bar_background: Some(rgba(0x282a36ff).into()),
|
||||
tab_bar_background: Some(rgba(0x191a21ff).into()),
|
||||
tab_inactive_background: Some(rgba(0x21222cff).into()),
|
||||
tab_active_background: Some(rgba(0x282a36ff).into()),
|
||||
editor_background: Some(rgba(0x282a36ff).into()),
|
||||
|
@ -63,10 +64,14 @@ pub fn dracula() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0xff5555ff).into()),
|
||||
conflict: Some(rgba(0xffb86cff).into()),
|
||||
created: Some(rgba(0x50fa7b80).into()),
|
||||
deleted: Some(rgba(0xff555580).into()),
|
||||
error: Some(rgba(0xff5555ff).into()),
|
||||
hidden: Some(rgba(0x6272a4ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x6272a4ff).into()),
|
||||
modified: Some(rgba(0x8be9fd80).into()),
|
||||
warning: Some(rgba(0xffb86cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -94,6 +99,20 @@ pub fn dracula() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xbd93f9ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xff79c6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -138,6 +157,13 @@ pub fn dracula() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xbd93f9ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -145,6 +171,34 @@ pub fn dracula() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.escape".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf1fa8cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf1fa8cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf1fa8cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xf1fa8cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
|
|
@ -25,15 +25,19 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x3c3836ff).into()),
|
||||
border_transparent: Some(rgba(0x3c3836ff).into()),
|
||||
border_disabled: Some(rgba(0x3c3836ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x1d2021ff).into()),
|
||||
background: Some(rgba(0x1d2021ff).into()),
|
||||
element_background: Some(rgba(0x45858880).into()),
|
||||
element_hover: Some(rgba(0x3c383680).into()),
|
||||
element_selected: Some(rgba(0x3c383680).into()),
|
||||
drop_target_background: Some(rgba(0x3c3836ff).into()),
|
||||
ghost_element_hover: Some(rgba(0x3c383680).into()),
|
||||
ghost_element_selected: Some(rgba(0x3c383680).into()),
|
||||
text: Some(rgba(0xebdbb2ff).into()),
|
||||
status_bar_background: Some(rgba(0x1d2021ff).into()),
|
||||
title_bar_background: Some(rgba(0x1d2021ff).into()),
|
||||
toolbar_background: Some(rgba(0x1d2021ff).into()),
|
||||
tab_bar_background: Some(rgba(0x1d2021ff).into()),
|
||||
tab_inactive_background: Some(rgba(0x1d2021ff).into()),
|
||||
tab_active_background: Some(rgba(0x32302fff).into()),
|
||||
editor_background: Some(rgba(0x1d2021ff).into()),
|
||||
|
@ -60,10 +64,14 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb16286ff).into()),
|
||||
created: Some(rgba(0xb8bb26ff).into()),
|
||||
deleted: Some(rgba(0xfb4934ff).into()),
|
||||
error: Some(rgba(0xfb4934ff).into()),
|
||||
hidden: Some(rgba(0xa89984ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x7c6f64ff).into()),
|
||||
modified: Some(rgba(0x83a598ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -91,6 +99,20 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd3869bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8ec07cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -102,7 +124,7 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xfabd2fff).into()),
|
||||
color: Some(rgba(0x8ec07cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -134,6 +156,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd3869bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -141,6 +170,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xfe8019ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -151,7 +187,35 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x83a598ff).into()),
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -169,6 +233,27 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -226,15 +311,19 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x3c3836ff).into()),
|
||||
border_transparent: Some(rgba(0x3c3836ff).into()),
|
||||
border_disabled: Some(rgba(0x3c3836ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x282828ff).into()),
|
||||
background: Some(rgba(0x282828ff).into()),
|
||||
element_background: Some(rgba(0x45858880).into()),
|
||||
element_hover: Some(rgba(0x3c383680).into()),
|
||||
element_selected: Some(rgba(0x3c383680).into()),
|
||||
drop_target_background: Some(rgba(0x3c3836ff).into()),
|
||||
ghost_element_hover: Some(rgba(0x3c383680).into()),
|
||||
ghost_element_selected: Some(rgba(0x3c383680).into()),
|
||||
text: Some(rgba(0xebdbb2ff).into()),
|
||||
status_bar_background: Some(rgba(0x282828ff).into()),
|
||||
title_bar_background: Some(rgba(0x282828ff).into()),
|
||||
toolbar_background: Some(rgba(0x282828ff).into()),
|
||||
tab_bar_background: Some(rgba(0x282828ff).into()),
|
||||
tab_inactive_background: Some(rgba(0x282828ff).into()),
|
||||
tab_active_background: Some(rgba(0x3c3836ff).into()),
|
||||
editor_background: Some(rgba(0x282828ff).into()),
|
||||
|
@ -261,10 +350,14 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb16286ff).into()),
|
||||
created: Some(rgba(0xb8bb26ff).into()),
|
||||
deleted: Some(rgba(0xfb4934ff).into()),
|
||||
error: Some(rgba(0xfb4934ff).into()),
|
||||
hidden: Some(rgba(0xa89984ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x7c6f64ff).into()),
|
||||
modified: Some(rgba(0x83a598ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -292,6 +385,20 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd3869bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8ec07cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -303,7 +410,7 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xfabd2fff).into()),
|
||||
color: Some(rgba(0x8ec07cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -335,6 +442,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd3869bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -342,6 +456,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xfe8019ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -352,7 +473,35 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x83a598ff).into()),
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -370,6 +519,27 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -427,15 +597,19 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0x3c3836ff).into()),
|
||||
border_transparent: Some(rgba(0x3c3836ff).into()),
|
||||
border_disabled: Some(rgba(0x3c3836ff).into()),
|
||||
elevated_surface_background: Some(rgba(0x32302fff).into()),
|
||||
background: Some(rgba(0x32302fff).into()),
|
||||
element_background: Some(rgba(0x45858880).into()),
|
||||
element_hover: Some(rgba(0x3c383680).into()),
|
||||
element_selected: Some(rgba(0x3c383680).into()),
|
||||
drop_target_background: Some(rgba(0x3c3836ff).into()),
|
||||
ghost_element_hover: Some(rgba(0x3c383680).into()),
|
||||
ghost_element_selected: Some(rgba(0x3c383680).into()),
|
||||
text: Some(rgba(0xebdbb2ff).into()),
|
||||
status_bar_background: Some(rgba(0x32302fff).into()),
|
||||
title_bar_background: Some(rgba(0x32302fff).into()),
|
||||
toolbar_background: Some(rgba(0x32302fff).into()),
|
||||
tab_bar_background: Some(rgba(0x32302fff).into()),
|
||||
tab_inactive_background: Some(rgba(0x32302fff).into()),
|
||||
tab_active_background: Some(rgba(0x504945ff).into()),
|
||||
editor_background: Some(rgba(0x32302fff).into()),
|
||||
|
@ -462,10 +636,14 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb16286ff).into()),
|
||||
created: Some(rgba(0xb8bb26ff).into()),
|
||||
deleted: Some(rgba(0xfb4934ff).into()),
|
||||
error: Some(rgba(0xfb4934ff).into()),
|
||||
hidden: Some(rgba(0xa89984ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x7c6f64ff).into()),
|
||||
modified: Some(rgba(0x83a598ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -493,6 +671,20 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd3869bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8ec07cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -504,7 +696,7 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xfabd2fff).into()),
|
||||
color: Some(rgba(0x8ec07cff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -536,6 +728,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd3869bff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -543,6 +742,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xfe8019ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -553,7 +759,35 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x83a598ff).into()),
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xa89984ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -571,6 +805,27 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb8bb26ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -628,15 +883,19 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0xebdbb2ff).into()),
|
||||
border_transparent: Some(rgba(0xebdbb2ff).into()),
|
||||
border_disabled: Some(rgba(0xebdbb2ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
background: Some(rgba(0xf9f5d7ff).into()),
|
||||
element_background: Some(rgba(0x45858880).into()),
|
||||
element_hover: Some(rgba(0xebdbb280).into()),
|
||||
element_selected: Some(rgba(0xebdbb280).into()),
|
||||
drop_target_background: Some(rgba(0xebdbb2ff).into()),
|
||||
ghost_element_hover: Some(rgba(0xebdbb280).into()),
|
||||
ghost_element_selected: Some(rgba(0xebdbb280).into()),
|
||||
text: Some(rgba(0x3c3836ff).into()),
|
||||
status_bar_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
title_bar_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
toolbar_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
tab_bar_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
tab_inactive_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
tab_active_background: Some(rgba(0xf2e5bcff).into()),
|
||||
editor_background: Some(rgba(0xf9f5d7ff).into()),
|
||||
|
@ -663,10 +922,14 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb16286ff).into()),
|
||||
created: Some(rgba(0x79740eff).into()),
|
||||
deleted: Some(rgba(0x9d0006ff).into()),
|
||||
error: Some(rgba(0x9d0006ff).into()),
|
||||
hidden: Some(rgba(0x7c6f64ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0xa89984ff).into()),
|
||||
modified: Some(rgba(0x076678ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -694,6 +957,20 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8f3f71ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x427b58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -705,7 +982,7 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb57614ff).into()),
|
||||
color: Some(rgba(0x427b58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -737,6 +1014,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8f3f71ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -744,6 +1028,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xaf3a03ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -754,7 +1045,35 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x076678ff).into()),
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -772,6 +1091,27 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -829,15 +1169,19 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0xebdbb2ff).into()),
|
||||
border_transparent: Some(rgba(0xebdbb2ff).into()),
|
||||
border_disabled: Some(rgba(0xebdbb2ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
background: Some(rgba(0xfbf1c7ff).into()),
|
||||
element_background: Some(rgba(0x45858880).into()),
|
||||
element_hover: Some(rgba(0xebdbb280).into()),
|
||||
element_selected: Some(rgba(0xebdbb280).into()),
|
||||
drop_target_background: Some(rgba(0xebdbb2ff).into()),
|
||||
ghost_element_hover: Some(rgba(0xebdbb280).into()),
|
||||
ghost_element_selected: Some(rgba(0xebdbb280).into()),
|
||||
text: Some(rgba(0x3c3836ff).into()),
|
||||
status_bar_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
title_bar_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
toolbar_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
tab_bar_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
tab_inactive_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
tab_active_background: Some(rgba(0xebdbb2ff).into()),
|
||||
editor_background: Some(rgba(0xfbf1c7ff).into()),
|
||||
|
@ -864,10 +1208,14 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb16286ff).into()),
|
||||
created: Some(rgba(0x79740eff).into()),
|
||||
deleted: Some(rgba(0x9d0006ff).into()),
|
||||
error: Some(rgba(0x9d0006ff).into()),
|
||||
hidden: Some(rgba(0x7c6f64ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0xa89984ff).into()),
|
||||
modified: Some(rgba(0x076678ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -895,6 +1243,20 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8f3f71ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x427b58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -906,7 +1268,7 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb57614ff).into()),
|
||||
color: Some(rgba(0x427b58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -938,6 +1300,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8f3f71ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -945,6 +1314,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xaf3a03ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -955,7 +1331,35 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x076678ff).into()),
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -973,6 +1377,27 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -1030,15 +1455,19 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
border_selected: Some(rgba(0xebdbb2ff).into()),
|
||||
border_transparent: Some(rgba(0xebdbb2ff).into()),
|
||||
border_disabled: Some(rgba(0xebdbb2ff).into()),
|
||||
elevated_surface_background: Some(rgba(0xf2e5bcff).into()),
|
||||
background: Some(rgba(0xf2e5bcff).into()),
|
||||
element_background: Some(rgba(0x45858880).into()),
|
||||
element_hover: Some(rgba(0xebdbb280).into()),
|
||||
element_selected: Some(rgba(0xebdbb280).into()),
|
||||
drop_target_background: Some(rgba(0xebdbb2ff).into()),
|
||||
ghost_element_hover: Some(rgba(0xebdbb280).into()),
|
||||
ghost_element_selected: Some(rgba(0xebdbb280).into()),
|
||||
text: Some(rgba(0x3c3836ff).into()),
|
||||
status_bar_background: Some(rgba(0xf2e5bcff).into()),
|
||||
title_bar_background: Some(rgba(0xf2e5bcff).into()),
|
||||
toolbar_background: Some(rgba(0xf2e5bcff).into()),
|
||||
tab_bar_background: Some(rgba(0xf2e5bcff).into()),
|
||||
tab_inactive_background: Some(rgba(0xf2e5bcff).into()),
|
||||
tab_active_background: Some(rgba(0xd5c4a1ff).into()),
|
||||
editor_background: Some(rgba(0xf2e5bcff).into()),
|
||||
|
@ -1065,10 +1494,14 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xb16286ff).into()),
|
||||
created: Some(rgba(0x79740eff).into()),
|
||||
deleted: Some(rgba(0x9d0006ff).into()),
|
||||
error: Some(rgba(0x9d0006ff).into()),
|
||||
hidden: Some(rgba(0x7c6f64ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0xa89984ff).into()),
|
||||
modified: Some(rgba(0x076678ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -1096,6 +1529,20 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constant".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8f3f71ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x427b58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -1107,7 +1554,7 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xb57614ff).into()),
|
||||
color: Some(rgba(0x427b58ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -1139,6 +1586,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"number".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x8f3f71ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"operator".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -1146,6 +1600,13 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"preproc".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xaf3a03ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"property".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -1156,7 +1617,35 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x076678ff).into()),
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7c6f64ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -1174,6 +1663,27 @@ pub fn gruvbox() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x79740eff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
|
|
@ -33,6 +33,7 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
element_selected: Some(rgba(0x234d708c).into()),
|
||||
drop_target_background: Some(rgba(0x011627ff).into()),
|
||||
ghost_element_hover: Some(rgba(0x011627ff).into()),
|
||||
ghost_element_selected: Some(rgba(0x234d708c).into()),
|
||||
text: Some(rgba(0xd6deebff).into()),
|
||||
status_bar_background: Some(rgba(0x011627ff).into()),
|
||||
title_bar_background: Some(rgba(0x011627ff).into()),
|
||||
|
@ -63,10 +64,14 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
conflict: Some(rgba(0xffeb95cc).into()),
|
||||
created: Some(rgba(0x9ccc65ff).into()),
|
||||
deleted: Some(rgba(0xef5350ff).into()),
|
||||
error: Some(rgba(0xef5350ff).into()),
|
||||
hidden: Some(rgba(0x5f7e97ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
ignored: Some(rgba(0x395a75ff).into()),
|
||||
modified: Some(rgba(0xe2b93dff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
syntax: Some(UserSyntaxTheme {
|
||||
|
@ -109,10 +114,18 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xcaece6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x82aaffff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -148,7 +161,40 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
(
|
||||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x7fdbcaff).into()),
|
||||
color: Some(rgba(0xc792eaff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc792eaff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc792eaff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc792eaff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xc792eaff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -166,6 +212,27 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xecc48dff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xecc48dff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xecc48dff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -190,7 +257,7 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0xd7dbe0ff).into()),
|
||||
color: Some(rgba(0xc5e478ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -223,10 +290,11 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
element_hover: Some(rgba(0xd3e8f8ff).into()),
|
||||
element_selected: Some(rgba(0xd3e8f8ff).into()),
|
||||
ghost_element_hover: Some(rgba(0xd3e8f8ff).into()),
|
||||
ghost_element_selected: Some(rgba(0xd3e8f8ff).into()),
|
||||
text: Some(rgba(0x403f53ff).into()),
|
||||
status_bar_background: Some(rgba(0xf0f0f0ff).into()),
|
||||
title_bar_background: Some(rgba(0xf0f0f0ff).into()),
|
||||
toolbar_background: Some(rgba(0xf0f0f0ff).into()),
|
||||
toolbar_background: Some(rgba(0xfbfbfbff).into()),
|
||||
tab_bar_background: Some(rgba(0xf0f0f0ff).into()),
|
||||
tab_inactive_background: Some(rgba(0xf0f0f0ff).into()),
|
||||
tab_active_background: Some(rgba(0xf6f6f6ff).into()),
|
||||
|
@ -254,10 +322,12 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
status: StatusColorsRefinement {
|
||||
deleted: Some(rgba(0x403f53ff).into()),
|
||||
created: Some(rgba(0x49d0c5ff).into()),
|
||||
deleted: Some(rgba(0xf76e6eff).into()),
|
||||
error: Some(rgba(0x403f53ff).into()),
|
||||
hidden: Some(rgba(0x403f53ff).into()),
|
||||
hint: Some(rgba(0x969696ff).into()),
|
||||
modified: Some(rgba(0x6fbef6ff).into()),
|
||||
warning: Some(rgba(0xdaaa01ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -301,10 +371,18 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"constructor".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x994cc3ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"function".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4876d6ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -341,6 +419,39 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
"punctuation".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x994cc3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x994cc3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x994cc3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x994cc3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"punctuation.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x994cc3ff).into()),
|
||||
font_style: Some(UserFontStyle::Italic),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
@ -358,6 +469,27 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.regex".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4876d6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4876d6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x4876d6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"tag".into(),
|
||||
UserHighlightStyle {
|
||||
|
@ -382,7 +514,7 @@ pub fn night_owl() -> UserThemeFamily {
|
|||
(
|
||||
"variable".into(),
|
||||
UserHighlightStyle {
|
||||
color: Some(rgba(0x403f53ff).into()),
|
||||
color: Some(rgba(0x4876d6ff).into()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue