diff --git a/Cargo.lock b/Cargo.lock index f68cd22ae7de47e94b488ae6c69871793f596191..30326c452299fae030526e881aeef78fdef964ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1501,6 +1501,7 @@ dependencies = [ "log", "lsp", "nanoid", + "node_runtime", "parking_lot 0.11.2", "pretty_assertions", "project", @@ -2404,7 +2405,6 @@ dependencies = [ "parking_lot 0.11.2", "postage", "project", - "pulldown-cmark", "rand 0.8.5", "rich_text", "rpc", @@ -3989,6 +3989,7 @@ dependencies = [ "lsp", "parking_lot 0.11.2", "postage", + "pulldown-cmark", "rand 0.8.5", "regex", "rpc", @@ -5518,6 +5519,26 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettier" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "collections", + "fs", + "futures 0.3.28", + "gpui", + "language", + "log", + "lsp", + "node_runtime", + "serde", + "serde_derive", + "serde_json", + "util", +] + [[package]] name = "pretty_assertions" version = "1.4.0" @@ -5630,8 +5651,10 @@ dependencies = [ "lazy_static", "log", "lsp", + "node_runtime", "parking_lot 0.11.2", "postage", + "prettier", "pretty_assertions", "rand 0.8.5", "regex", @@ -6601,12 +6624,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "rustybuzz" version = "0.3.0" @@ -6940,7 +6957,6 @@ dependencies = [ "unindent", "util", "workspace", - "zed", ] [[package]] @@ -7660,28 +7676,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "storybook" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "clap 4.4.4", - "fs", - "futures 0.3.28", - "gpui2", - "itertools 0.11.0", - "log", - "rust-embed", - "serde", - "settings", - "simplelog", - "strum", - "theme", - "ui", - "util", -] - [[package]] name = "stringprep" version = "0.1.4" @@ -7704,22 +7698,6 @@ name = "strum" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.37", -] [[package]] name = "subtle" @@ -8815,6 +8793,15 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-vue" +version = "0.0.1" +source = "git+https://github.com/zed-industries/tree-sitter-vue?rev=95b2890#95b28908d90e928c308866f7631e73ef6e1d4b5f" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-yaml" version = "0.0.1" @@ -8886,21 +8873,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ui" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "gpui2", - "rand 0.8.5", - "serde", - "settings", - "smallvec", - "strum", - "theme", -] - [[package]] name = "unicase" version = "2.7.0" @@ -9987,6 +9959,7 @@ dependencies = [ "lazy_static", "log", "menu", + "node_runtime", "parking_lot 0.11.2", "postage", "project", @@ -10085,6 +10058,7 @@ name = "zed" version = "0.109.0" dependencies = [ "activity_indicator", + "ai", "anyhow", "assistant", "async-compression", @@ -10197,6 +10171,7 @@ dependencies = [ "tree-sitter-svelte", "tree-sitter-toml", "tree-sitter-typescript", + "tree-sitter-vue", "tree-sitter-yaml", "unindent", "url", diff --git a/Cargo.toml b/Cargo.toml index 7dae3bd81f7213ad34f5b51621471678436d6161..995cd15edd45d6c983fa20c19c6ba82749300260 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ members = [ "crates/plugin", "crates/plugin_macros", "crates/plugin_runtime", + "crates/prettier", "crates/project", "crates/project_panel", "crates/project_symbols", @@ -65,13 +66,11 @@ members = [ "crates/sqlez_macros", "crates/feature_flags", "crates/rich_text", - "crates/storybook", "crates/sum_tree", "crates/terminal", "crates/text", "crates/theme", "crates/theme_selector", - "crates/ui", "crates/util", "crates/semantic_index", "crates/vim", @@ -150,7 +149,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 = "95b2890"} [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "35a6052fbcafc5e5fc0f9415b8652be7dcaf7222" } async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" } diff --git a/assets/settings/default.json b/assets/settings/default.json index cc724657c0c20411b17d0ad0ea293b0a08ddc6f5..4143e5dd41f1a1596ea532433d91cfaffe35f92a 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -50,6 +50,9 @@ // Whether to pop the completions menu while typing in an editor without // explicitly requesting it. "show_completions_on_input": true, + // Whether to display inline and alongside documentation for items in the + // completions menu + "show_completion_documentation": true, // Whether to show wrap guides in the editor. Setting this to true will // show a guide at the 'preferred_line_length' value if softwrap is set to // 'preferred_line_length', and will show any additional guides as specified @@ -199,7 +202,12 @@ // "arguments": ["--stdin-filepath", "{buffer_path}"] // } // } - "formatter": "language_server", + // 3. Format code using Zed's Prettier integration: + // "formatter": "prettier" + // 4. Default. Format files using Zed's Prettier integration (if applicable), + // or falling back to formatting via language server: + // "formatter": "auto" + "formatter": "auto", // How to soft-wrap long lines of text. This setting can take // three values: // @@ -429,6 +437,16 @@ "tab_size": 2 } }, + // Zed's Prettier integration settings. + // If Prettier is enabled, Zed will use this its Prettier instance for any applicable file, if + // project has no other Prettier installed. + "prettier": { + // Use regular Prettier json configuration: + // "trailingComma": "es5", + // "tabWidth": 4, + // "semi": false, + // "singleQuote": true + }, // LSP Specific settings. "lsp": { // Specify the LSP name as a key here. diff --git a/crates/channel/src/channel_buffer.rs b/crates/channel/src/channel_buffer.rs index 7de8b956f1e897b4293274441689570f63780dcb..ab7ea78ac1b70e5c5b0c028289f2b9f55fe924f9 100644 --- a/crates/channel/src/channel_buffer.rs +++ b/crates/channel/src/channel_buffer.rs @@ -99,6 +99,10 @@ impl ChannelBuffer { })) } + pub fn remote_id(&self, cx: &AppContext) -> u64 { + self.buffer.read(cx).remote_id() + } + pub fn user_store(&self) -> &ModelHandle { &self.user_store } diff --git a/crates/channel/src/channel_store.rs b/crates/channel/src/channel_store.rs index 5fb7ddc72ccded0bf326032c9393becb1f2ca672..9c80dcc2b742b07430a752286a2008bc5cfd05b2 100644 --- a/crates/channel/src/channel_store.rs +++ b/crates/channel/src/channel_store.rs @@ -140,12 +140,21 @@ impl ChannelStore { let watch_connection_status = cx.spawn_weak(|this, mut cx| async move { while let Some(status) = connection_status.next().await { let this = this.upgrade(&cx)?; + match status { + client::Status::Connected { .. } => { + this.update(&mut cx, |this, cx| this.handle_connect(cx)) + .await + .log_err()?; + } + client::Status::SignedOut | client::Status::UpgradeRequired => { + this.update(&mut cx, |this, cx| this.handle_disconnect(false, cx)); + } + _ => { + this.update(&mut cx, |this, cx| this.handle_disconnect(true, cx)); + } + } if status.is_connected() { - this.update(&mut cx, |this, cx| this.handle_connect(cx)) - .await - .log_err()?; } else { - this.update(&mut cx, |this, cx| this.handle_disconnect(cx)); } } Some(()) @@ -868,7 +877,7 @@ impl ChannelStore { }) } - fn handle_disconnect(&mut self, cx: &mut ModelContext) { + fn handle_disconnect(&mut self, wait_for_reconnect: bool, cx: &mut ModelContext) { self.channel_index.clear(); self.channel_invitations.clear(); self.channel_participants.clear(); @@ -879,7 +888,10 @@ impl ChannelStore { self.disconnect_channel_buffers_task.get_or_insert_with(|| { cx.spawn_weak(|this, mut cx| async move { - cx.background().timer(RECONNECT_TIMEOUT).await; + if wait_for_reconnect { + cx.background().timer(RECONNECT_TIMEOUT).await; + } + if let Some(this) = this.upgrade(&cx) { this.update(&mut cx, |this, cx| { for (_, buffer) in this.opened_buffers.drain() { diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 70878bf2e46117c787cc0074f77b30494ae6f544..fd93aaeec890437968d50854ca83534c62ed4d36 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -4,7 +4,9 @@ use lazy_static::lazy_static; use parking_lot::Mutex; use serde::Serialize; use std::{env, io::Write, mem, path::PathBuf, sync::Arc, time::Duration}; -use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt}; +use sysinfo::{ + CpuRefreshKind, Pid, PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt, +}; use tempfile::NamedTempFile; use util::http::HttpClient; use util::{channel::ReleaseChannel, TryFutureExt}; @@ -166,8 +168,16 @@ impl Telemetry { let this = self.clone(); cx.spawn(|mut cx| async move { - let mut system = System::new_all(); - system.refresh_all(); + // Avoiding calling `System::new_all()`, as there have been crashes related to it + let refresh_kind = RefreshKind::new() + .with_memory() // For memory usage + .with_processes(ProcessRefreshKind::everything()) // For process usage + .with_cpu(CpuRefreshKind::everything()); // For core count + + let mut system = System::new_with_specifics(refresh_kind); + + // Avoiding calling `refresh_all()`, just update what we need + system.refresh_specifics(refresh_kind); loop { // Waiting some amount of time before the first query is important to get a reasonable value @@ -175,8 +185,7 @@ impl Telemetry { const DURATION_BETWEEN_SYSTEM_EVENTS: Duration = Duration::from_secs(60); smol::Timer::after(DURATION_BETWEEN_SYSTEM_EVENTS).await; - system.refresh_memory(); - system.refresh_processes(); + system.refresh_specifics(refresh_kind); let current_process = Pid::from_u32(std::process::id()); let Some(process) = system.processes().get(¤t_process) else { diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 8fd1cd438085740a4cac2448903b3d38ce6c0864..b91f0e1a5ff7d3b5d1c7061fc65738dc4a5a6321 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -72,6 +72,7 @@ fs = { path = "../fs", features = ["test-support"] } git = { path = "../git", features = ["test-support"] } live_kit_client = { path = "../live_kit_client", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } +node_runtime = { path = "../node_runtime" } project = { path = "../project", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } diff --git a/crates/collab/src/db/queries/messages.rs b/crates/collab/src/db/queries/messages.rs index 7b389197750f0a2423a714e85fc389c35f9d2dc6..06e954103d18c0916b2dc76ba474a06283a93cf9 100644 --- a/crates/collab/src/db/queries/messages.rs +++ b/crates/collab/src/db/queries/messages.rs @@ -339,8 +339,22 @@ impl Database { .filter(channel_message::Column::SenderId.eq(user_id)) .exec(&*tx) .await?; + if result.rows_affected == 0 { - Err(anyhow!("no such message"))?; + if self + .check_user_is_channel_admin(channel_id, user_id, &*tx) + .await + .is_ok() + { + let result = channel_message::Entity::delete_by_id(message_id) + .exec(&*tx) + .await?; + if result.rows_affected == 0 { + Err(anyhow!("no such message"))?; + } + } else { + Err(anyhow!("operation could not be completed"))?; + } } Ok(participant_connection_ids) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 4b33550c39e66a0f8e62b3757c535fb6988a68a8..575c9d88717f79bb933c63f18b6605c152b96985 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -225,6 +225,7 @@ impl Server { .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) + .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) .add_request_handler(forward_project_request::) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 4008a941dd2e76be691e8a9d54b5cb66f1f8c5a2..d6d449fd476b0d6f967641268bf1798a21ccf81d 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -15,12 +15,14 @@ use gpui::{executor::Deterministic, test::EmptyView, AppContext, ModelHandle, Te use indoc::indoc; use language::{ language_settings::{AllLanguageSettings, Formatter, InlayHintSettings}, - tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, - LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope, + tree_sitter_rust, Anchor, BundledFormatter, Diagnostic, DiagnosticEntry, FakeLspAdapter, + Language, LanguageConfig, LineEnding, OffsetRangeExt, Point, Rope, }; use live_kit_client::MacOSDisplay; use lsp::LanguageServerId; -use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath}; +use project::{ + search::SearchQuery, DiagnosticSummary, FormatTrigger, HoverBlockKind, Project, ProjectPath, +}; use rand::prelude::*; use serde_json::json; use settings::SettingsStore; @@ -4407,8 +4409,6 @@ async fn test_formatting_buffer( cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, ) { - use project::FormatTrigger; - let mut server = TestServer::start(&deterministic).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -4511,6 +4511,134 @@ async fn test_formatting_buffer( ); } +#[gpui::test(iterations = 10)] +async fn test_prettier_formatting_buffer( + deterministic: Arc, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + let mut server = TestServer::start(&deterministic).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .await; + let active_call_a = cx_a.read(ActiveCall::global); + + // Set up a fake language server. + let mut language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + let test_plugin = "test_plugin"; + let mut fake_language_servers = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + enabled_formatters: vec![BundledFormatter::Prettier { + parser_name: Some("test_parser"), + plugin_names: vec![test_plugin], + }], + ..Default::default() + })) + .await; + let language = Arc::new(language); + client_a.language_registry().add(Arc::clone(&language)); + + // Here we insert a fake tree with a directory that exists on disk. This is needed + // because later we'll invoke a command, which requires passing a working directory + // that points to a valid location on disk. + let directory = env::current_dir().unwrap(); + let buffer_text = "let one = \"two\""; + client_a + .fs() + .insert_tree(&directory, json!({ "a.rs": buffer_text })) + .await; + let (project_a, worktree_id) = client_a.build_local_project(&directory, cx_a).await; + let prettier_format_suffix = project_a.update(cx_a, |project, _| { + let suffix = project.enable_test_prettier(&[test_plugin]); + project.languages().add(language); + suffix + }); + let buffer_a = cx_a + .background() + .spawn(project_a.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))) + .await + .unwrap(); + + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); + let project_b = client_b.build_remote_project(project_id, cx_b).await; + let buffer_b = cx_b + .background() + .spawn(project_b.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.rs"), cx))) + .await + .unwrap(); + + cx_a.update(|cx| { + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |file| { + file.defaults.formatter = Some(Formatter::Auto); + }); + }); + }); + cx_b.update(|cx| { + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings::(cx, |file| { + file.defaults.formatter = Some(Formatter::LanguageServer); + }); + }); + }); + let fake_language_server = fake_language_servers.next().await.unwrap(); + fake_language_server.handle_request::(|_, _| async move { + panic!( + "Unexpected: prettier should be preferred since it's enabled and language supports it" + ) + }); + + project_b + .update(cx_b, |project, cx| { + project.format( + HashSet::from_iter([buffer_b.clone()]), + true, + FormatTrigger::Save, + cx, + ) + }) + .await + .unwrap(); + cx_a.foreground().run_until_parked(); + cx_b.foreground().run_until_parked(); + assert_eq!( + buffer_b.read_with(cx_b, |buffer, _| buffer.text()), + buffer_text.to_string() + "\n" + prettier_format_suffix, + "Prettier formatting was not applied to client buffer after client's request" + ); + + project_a + .update(cx_a, |project, cx| { + project.format( + HashSet::from_iter([buffer_a.clone()]), + true, + FormatTrigger::Manual, + cx, + ) + }) + .await + .unwrap(); + cx_a.foreground().run_until_parked(); + cx_b.foreground().run_until_parked(); + assert_eq!( + buffer_b.read_with(cx_b, |buffer, _| buffer.text()), + buffer_text.to_string() + "\n" + prettier_format_suffix + "\n" + prettier_format_suffix, + "Prettier formatting was not applied to client buffer after host's request" + ); +} + #[gpui::test(iterations = 10)] async fn test_definition( deterministic: Arc, diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 54a59c0c000011a329f64ed5fab36a1a091d309c..c37ea19d528d9f30ef4cc69de31b3c3d2370a86a 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -15,6 +15,7 @@ use fs::FakeFs; use futures::{channel::oneshot, StreamExt as _}; use gpui::{executor::Deterministic, ModelHandle, Task, TestAppContext, WindowHandle}; use language::LanguageRegistry; +use node_runtime::FakeNodeRuntime; use parking_lot::Mutex; use project::{Project, WorktreeId}; use rpc::{proto::ChannelRole, RECEIVE_TIMEOUT}; @@ -218,6 +219,7 @@ impl TestServer { build_window_options: |_, _, _| Default::default(), initialize_workspace: |_, _, _, _| Task::ready(Ok(())), background_actions: || &[], + node_runtime: FakeNodeRuntime::new(), }); cx.update(|cx| { @@ -567,6 +569,7 @@ impl TestClient { cx.update(|cx| { Project::local( self.client().clone(), + self.app_state.node_runtime.clone(), self.app_state.user_store.clone(), self.app_state.languages.clone(), self.app_state.fs.clone(), diff --git a/crates/collab_ui/src/channel_view.rs b/crates/collab_ui/src/channel_view.rs index b2e65eb2fa1bd5e92ddf5436115b938958f1383e..e62ee8ef4b7b0c091000cdfd5b272e62c6fee7f7 100644 --- a/crates/collab_ui/src/channel_view.rs +++ b/crates/collab_ui/src/channel_view.rs @@ -24,7 +24,7 @@ use workspace::{ item::{FollowableItem, Item, ItemHandle}, register_followable_item, searchable::SearchableItemHandle, - ItemNavHistory, Pane, ViewId, Workspace, WorkspaceId, + ItemNavHistory, Pane, SaveIntent, ViewId, Workspace, WorkspaceId, }; actions!(channel_view, [Deploy]); @@ -93,15 +93,36 @@ impl ChannelView { } pane.update(&mut cx, |pane, cx| { - pane.items_of_type::() - .find(|channel_view| channel_view.read(cx).channel_buffer == channel_buffer) - .unwrap_or_else(|| { - cx.add_view(|cx| { - let mut this = Self::new(project, channel_store, channel_buffer, cx); - this.acknowledge_buffer_version(cx); - this - }) - }) + let buffer_id = channel_buffer.read(cx).remote_id(cx); + + let existing_view = pane + .items_of_type::() + .find(|view| view.read(cx).channel_buffer.read(cx).remote_id(cx) == buffer_id); + + // If this channel buffer is already open in this pane, just return it. + if let Some(existing_view) = existing_view.clone() { + if existing_view.read(cx).channel_buffer == channel_buffer { + return existing_view; + } + } + + let view = cx.add_view(|cx| { + let mut this = Self::new(project, channel_store, channel_buffer, cx); + this.acknowledge_buffer_version(cx); + this + }); + + // If the pane contained a disconnected view for this channel buffer, + // replace that. + if let Some(existing_item) = existing_view { + if let Some(ix) = pane.index_for_item(&existing_item) { + pane.close_item_by_id(existing_item.id(), SaveIntent::Skip, cx) + .detach(); + pane.add_item(Box::new(view.clone()), true, true, Some(ix), cx); + } + } + + view }) .ok_or_else(|| anyhow!("pane was dropped")) }) @@ -285,10 +306,14 @@ impl FollowableItem for ChannelView { } fn to_state_proto(&self, cx: &AppContext) -> Option { - let channel = self.channel_buffer.read(cx).channel(); + let channel_buffer = self.channel_buffer.read(cx); + if !channel_buffer.is_connected() { + return None; + } + Some(proto::view::Variant::ChannelView( proto::view::ChannelView { - channel_id: channel.id, + channel_id: channel_buffer.channel().id, editor: if let Some(proto::view::Variant::Editor(proto)) = self.editor.read(cx).to_state_proto(cx) { diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 1a17b48f19c303d9cd26915ff331c2fcc340cc89..a8c4006cb8ce7c18e3a2eaa09eb1dafc98becd26 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -355,8 +355,12 @@ impl ChatPanel { } fn render_message(&mut self, ix: usize, cx: &mut ViewContext) -> AnyElement { - let (message, is_continuation, is_last) = { + let (message, is_continuation, is_last, is_admin) = { let active_chat = self.active_chat.as_ref().unwrap().0.read(cx); + let is_admin = self + .channel_store + .read(cx) + .is_user_admin(active_chat.channel().id); let last_message = active_chat.message(ix.saturating_sub(1)); let this_message = active_chat.message(ix); let is_continuation = last_message.id != this_message.id @@ -366,6 +370,7 @@ impl ChatPanel { active_chat.message(ix).clone(), is_continuation, active_chat.message_count() == ix + 1, + is_admin, ) }; @@ -386,12 +391,13 @@ impl ChatPanel { }; let belongs_to_user = Some(message.sender.id) == self.client.user_id(); - let message_id_to_remove = - if let (ChannelMessageId::Saved(id), true) = (message.id, belongs_to_user) { - Some(id) - } else { - None - }; + let message_id_to_remove = if let (ChannelMessageId::Saved(id), true) = + (message.id, belongs_to_user || is_admin) + { + Some(id) + } else { + None + }; enum MessageBackgroundHighlight {} MouseEventHandler::new::(ix, cx, |state, cx| { diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index c3733018b67e0142115c3baac2d8a068d5f6e328..8d3c2fedd6d9d65d7d28e0a88f05f1e85161d117 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -38,6 +38,10 @@ impl DiagnosticIndicator { this.in_progress_checks.remove(language_server_id); cx.notify(); } + project::Event::DiagnosticsUpdated { .. } => { + this.summary = project.read(cx).diagnostic_summary(cx); + cx.notify(); + } _ => {} }) .detach(); diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 2c3d6227a9abe0828e410d4ca5a1c2c82b8ef701..d03e1c110649013d6acf34e64ba69d6231003878 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -57,7 +57,6 @@ log.workspace = true ordered-float.workspace = true parking_lot.workspace = true postage.workspace = true -pulldown-cmark = { version = "0.9.2", default-features = false } rand.workspace = true schemars.workspace = true serde.workspace = true diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 24ffa64a6af73aef827cea17757e5c0e6a4e94c2..7aca4ab98fff05a73dcacdb55bf97913c164146f 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -25,7 +25,7 @@ use ::git::diff::DiffHunk; use aho_corasick::AhoCorasick; use anyhow::{anyhow, Context, Result}; use blink_manager::BlinkManager; -use client::{ClickhouseEvent, Collaborator, ParticipantIndex, TelemetrySettings}; +use client::{ClickhouseEvent, Client, Collaborator, ParticipantIndex, TelemetrySettings}; use clock::{Global, ReplicaId}; use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque}; use convert_case::{Case, Casing}; @@ -48,9 +48,9 @@ use gpui::{ impl_actions, keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton}, - serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, - Entity, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, - WindowContext, + serde_json, AnyElement, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, + CursorRegion, Element, Entity, ModelHandle, MouseRegion, Subscription, Task, View, ViewContext, + ViewHandle, WeakViewHandle, WindowContext, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; @@ -60,10 +60,10 @@ use itertools::Itertools; pub use language::{char_kind, CharKind}; use language::{ language_settings::{self, all_language_settings, InlayHintSettings}, - point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, - CursorShape, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, - LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, - TransactionId, + markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, + Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation, File, IndentKind, + IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, + Selection, SelectionGoal, TransactionId, }; use link_go_to_definition::{ hide_link_definition, show_link_definition, GoToDefinitionLink, InlayHighlight, @@ -77,9 +77,10 @@ pub use multi_buffer::{ ToPoint, }; use ordered_float::OrderedFloat; +use parking_lot::RwLock; use project::{FormatTrigger, Location, Project, ProjectPath, ProjectTransaction}; use rand::{seq::SliceRandom, thread_rng}; -use rpc::proto::PeerId; +use rpc::proto::{self, PeerId}; use scroll::{ autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide, }; @@ -118,6 +119,67 @@ pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2); +pub fn render_parsed_markdown( + parsed: &language::ParsedMarkdown, + editor_style: &EditorStyle, + workspace: Option>, + cx: &mut ViewContext, +) -> Text { + enum RenderedMarkdown {} + + let parsed = parsed.clone(); + let view_id = cx.view_id(); + let code_span_background_color = editor_style.document_highlight_read_background; + + let mut region_id = 0; + + Text::new(parsed.text, editor_style.text.clone()) + .with_highlights( + parsed + .highlights + .iter() + .filter_map(|(range, highlight)| { + let highlight = highlight.to_highlight_style(&editor_style.syntax)?; + Some((range.clone(), highlight)) + }) + .collect::>(), + ) + .with_custom_runs(parsed.region_ranges, move |ix, bounds, cx| { + region_id += 1; + let region = parsed.regions[ix].clone(); + + if let Some(link) = region.link { + cx.scene().push_cursor_region(CursorRegion { + bounds, + style: CursorStyle::PointingHand, + }); + cx.scene().push_mouse_region( + MouseRegion::new::<(RenderedMarkdown, Tag)>(view_id, region_id, bounds) + .on_down::(MouseButton::Left, move |_, _, cx| match &link { + markdown::Link::Web { url } => cx.platform().open_url(url), + markdown::Link::Path { path } => { + if let Some(workspace) = &workspace { + _ = workspace.update(cx, |workspace, cx| { + workspace.open_abs_path(path.clone(), false, cx).detach(); + }); + } + } + }), + ); + } + + if region.code { + cx.scene().push_quad(gpui::Quad { + bounds, + background: Some(code_span_background_color), + border: Default::default(), + corner_radii: (2.0).into(), + }); + } + }) + .with_soft_wrap(true) +} + #[derive(Clone, Deserialize, PartialEq, Default)] pub struct SelectNext { #[serde(default)] @@ -594,7 +656,7 @@ pub struct Editor { background_highlights: BTreeMap, inlay_background_highlights: TreeMap, InlayBackgroundHighlight>, nav_history: Option, - context_menu: Option, + context_menu: RwLock>, mouse_context_menu: ViewHandle, completion_tasks: Vec<(CompletionId, Task>)>, next_completion_id: CompletionId, @@ -787,10 +849,14 @@ enum ContextMenu { } impl ContextMenu { - fn select_first(&mut self, cx: &mut ViewContext) -> bool { + fn select_first( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) -> bool { if self.visible() { match self { - ContextMenu::Completions(menu) => menu.select_first(cx), + ContextMenu::Completions(menu) => menu.select_first(project, cx), ContextMenu::CodeActions(menu) => menu.select_first(cx), } true @@ -799,10 +865,14 @@ impl ContextMenu { } } - fn select_prev(&mut self, cx: &mut ViewContext) -> bool { + fn select_prev( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) -> bool { if self.visible() { match self { - ContextMenu::Completions(menu) => menu.select_prev(cx), + ContextMenu::Completions(menu) => menu.select_prev(project, cx), ContextMenu::CodeActions(menu) => menu.select_prev(cx), } true @@ -811,10 +881,14 @@ impl ContextMenu { } } - fn select_next(&mut self, cx: &mut ViewContext) -> bool { + fn select_next( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) -> bool { if self.visible() { match self { - ContextMenu::Completions(menu) => menu.select_next(cx), + ContextMenu::Completions(menu) => menu.select_next(project, cx), ContextMenu::CodeActions(menu) => menu.select_next(cx), } true @@ -823,10 +897,14 @@ impl ContextMenu { } } - fn select_last(&mut self, cx: &mut ViewContext) -> bool { + fn select_last( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) -> bool { if self.visible() { match self { - ContextMenu::Completions(menu) => menu.select_last(cx), + ContextMenu::Completions(menu) => menu.select_last(project, cx), ContextMenu::CodeActions(menu) => menu.select_last(cx), } true @@ -846,99 +924,350 @@ impl ContextMenu { &self, cursor_position: DisplayPoint, style: EditorStyle, + workspace: Option>, cx: &mut ViewContext, ) -> (DisplayPoint, AnyElement) { match self { - ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)), + ContextMenu::Completions(menu) => (cursor_position, menu.render(style, workspace, cx)), ContextMenu::CodeActions(menu) => menu.render(cursor_position, style, cx), } } } +#[derive(Clone)] struct CompletionsMenu { id: CompletionId, initial_position: Anchor, buffer: ModelHandle, - project: Option>, - completions: Arc<[Completion]>, - match_candidates: Vec, + completions: Arc>>, + match_candidates: Arc<[StringMatchCandidate]>, matches: Arc<[StringMatch]>, selected_item: usize, list: UniformListState, } impl CompletionsMenu { - fn select_first(&mut self, cx: &mut ViewContext) { + fn select_first( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) { self.selected_item = 0; self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } - fn select_prev(&mut self, cx: &mut ViewContext) { + fn select_prev( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) { if self.selected_item > 0 { self.selected_item -= 1; self.list.scroll_to(ScrollTarget::Show(self.selected_item)); } + self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } - fn select_next(&mut self, cx: &mut ViewContext) { + fn select_next( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) { if self.selected_item + 1 < self.matches.len() { self.selected_item += 1; self.list.scroll_to(ScrollTarget::Show(self.selected_item)); } + self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } - fn select_last(&mut self, cx: &mut ViewContext) { + fn select_last( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) { self.selected_item = self.matches.len() - 1; self.list.scroll_to(ScrollTarget::Show(self.selected_item)); + self.attempt_resolve_selected_completion_documentation(project, cx); cx.notify(); } + fn pre_resolve_completion_documentation( + &self, + project: Option>, + cx: &mut ViewContext, + ) { + let settings = settings::get::(cx); + if !settings.show_completion_documentation { + return; + } + + let Some(project) = project else { + return; + }; + let client = project.read(cx).client(); + let language_registry = project.read(cx).languages().clone(); + + let is_remote = project.read(cx).is_remote(); + let project_id = project.read(cx).remote_id(); + + let completions = self.completions.clone(); + let completion_indices: Vec<_> = self.matches.iter().map(|m| m.candidate_id).collect(); + + cx.spawn(move |this, mut cx| async move { + if is_remote { + let Some(project_id) = project_id else { + log::error!("Remote project without remote_id"); + return; + }; + + for completion_index in completion_indices { + let completions_guard = completions.read(); + let completion = &completions_guard[completion_index]; + if completion.documentation.is_some() { + continue; + } + + let server_id = completion.server_id; + let completion = completion.lsp_completion.clone(); + drop(completions_guard); + + Self::resolve_completion_documentation_remote( + project_id, + server_id, + completions.clone(), + completion_index, + completion, + client.clone(), + language_registry.clone(), + ) + .await; + + _ = this.update(&mut cx, |_, cx| cx.notify()); + } + } else { + for completion_index in completion_indices { + let completions_guard = completions.read(); + let completion = &completions_guard[completion_index]; + if completion.documentation.is_some() { + continue; + } + + let server_id = completion.server_id; + let completion = completion.lsp_completion.clone(); + drop(completions_guard); + + let server = project.read_with(&mut cx, |project, _| { + project.language_server_for_id(server_id) + }); + let Some(server) = server else { + return; + }; + + Self::resolve_completion_documentation_local( + server, + completions.clone(), + completion_index, + completion, + language_registry.clone(), + ) + .await; + + _ = this.update(&mut cx, |_, cx| cx.notify()); + } + } + }) + .detach(); + } + + fn attempt_resolve_selected_completion_documentation( + &mut self, + project: Option<&ModelHandle>, + cx: &mut ViewContext, + ) { + let settings = settings::get::(cx); + if !settings.show_completion_documentation { + return; + } + + let completion_index = self.matches[self.selected_item].candidate_id; + let Some(project) = project else { + return; + }; + let language_registry = project.read(cx).languages().clone(); + + let completions = self.completions.clone(); + let completions_guard = completions.read(); + let completion = &completions_guard[completion_index]; + if completion.documentation.is_some() { + return; + } + + let server_id = completion.server_id; + let completion = completion.lsp_completion.clone(); + drop(completions_guard); + + if project.read(cx).is_remote() { + let Some(project_id) = project.read(cx).remote_id() else { + log::error!("Remote project without remote_id"); + return; + }; + + let client = project.read(cx).client(); + + cx.spawn(move |this, mut cx| async move { + Self::resolve_completion_documentation_remote( + project_id, + server_id, + completions.clone(), + completion_index, + completion, + client, + language_registry.clone(), + ) + .await; + + _ = this.update(&mut cx, |_, cx| cx.notify()); + }) + .detach(); + } else { + let Some(server) = project.read(cx).language_server_for_id(server_id) else { + return; + }; + + cx.spawn(move |this, mut cx| async move { + Self::resolve_completion_documentation_local( + server, + completions, + completion_index, + completion, + language_registry, + ) + .await; + + _ = this.update(&mut cx, |_, cx| cx.notify()); + }) + .detach(); + } + } + + async fn resolve_completion_documentation_remote( + project_id: u64, + server_id: LanguageServerId, + completions: Arc>>, + completion_index: usize, + completion: lsp::CompletionItem, + client: Arc, + language_registry: Arc, + ) { + let request = proto::ResolveCompletionDocumentation { + project_id, + language_server_id: server_id.0 as u64, + lsp_completion: serde_json::to_string(&completion).unwrap().into_bytes(), + }; + + let Some(response) = client + .request(request) + .await + .context("completion documentation resolve proto request") + .log_err() + else { + return; + }; + + if response.text.is_empty() { + let mut completions = completions.write(); + let completion = &mut completions[completion_index]; + completion.documentation = Some(Documentation::Undocumented); + } + + let documentation = if response.is_markdown { + Documentation::MultiLineMarkdown( + markdown::parse_markdown(&response.text, &language_registry, None).await, + ) + } else if response.text.lines().count() <= 1 { + Documentation::SingleLine(response.text) + } else { + Documentation::MultiLinePlainText(response.text) + }; + + let mut completions = completions.write(); + let completion = &mut completions[completion_index]; + completion.documentation = Some(documentation); + } + + async fn resolve_completion_documentation_local( + server: Arc, + completions: Arc>>, + completion_index: usize, + completion: lsp::CompletionItem, + language_registry: Arc, + ) { + let can_resolve = server + .capabilities() + .completion_provider + .as_ref() + .and_then(|options| options.resolve_provider) + .unwrap_or(false); + if !can_resolve { + return; + } + + let request = server.request::(completion); + let Some(completion_item) = request.await.log_err() else { + return; + }; + + if let Some(lsp_documentation) = completion_item.documentation { + let documentation = language::prepare_completion_documentation( + &lsp_documentation, + &language_registry, + None, // TODO: Try to reasonably work out which language the completion is for + ) + .await; + + let mut completions = completions.write(); + let completion = &mut completions[completion_index]; + completion.documentation = Some(documentation); + } else { + let mut completions = completions.write(); + let completion = &mut completions[completion_index]; + completion.documentation = Some(Documentation::Undocumented); + } + } + fn visible(&self) -> bool { !self.matches.is_empty() } - fn render(&self, style: EditorStyle, cx: &mut ViewContext) -> AnyElement { + fn render( + &self, + style: EditorStyle, + workspace: Option>, + cx: &mut ViewContext, + ) -> AnyElement { enum CompletionTag {} - let language_servers = self.project.as_ref().map(|project| { - project - .read(cx) - .language_servers_for_buffer(self.buffer.read(cx), cx) - .filter(|(_, server)| server.capabilities().completion_provider.is_some()) - .map(|(adapter, server)| (server.server_id(), adapter.short_name)) - .collect::>() - }); - let needs_server_name = language_servers - .as_ref() - .map_or(false, |servers| servers.len() > 1); - - let get_server_name = - move |lookup_server_id: lsp::LanguageServerId| -> Option<&'static str> { - language_servers - .iter() - .flatten() - .find_map(|(server_id, server_name)| { - if *server_id == lookup_server_id { - Some(*server_name) - } else { - None - } - }) - }; + let settings = settings::get::(cx); + let show_completion_documentation = settings.show_completion_documentation; let widest_completion_ix = self .matches .iter() .enumerate() .max_by_key(|(_, mat)| { - let completion = &self.completions[mat.candidate_id]; - let mut len = completion.label.text.chars().count(); + let completions = self.completions.read(); + let completion = &completions[mat.candidate_id]; + let documentation = &completion.documentation; - if let Some(server_name) = get_server_name(completion.server_id) { - len += server_name.chars().count(); + let mut len = completion.label.text.chars().count(); + if let Some(Documentation::SingleLine(text)) = documentation { + if show_completion_documentation { + len += text.chars().count(); + } } len @@ -948,16 +1277,24 @@ impl CompletionsMenu { let completions = self.completions.clone(); let matches = self.matches.clone(); let selected_item = self.selected_item; - let container_style = style.autocomplete.container; - UniformList::new( - self.list.clone(), - matches.len(), - cx, + + let list = UniformList::new(self.list.clone(), matches.len(), cx, { + let style = style.clone(); move |_, range, items, cx| { let start_ix = range.start; + let completions_guard = completions.read(); + for (ix, mat) in matches[range].iter().enumerate() { - let completion = &completions[mat.candidate_id]; let item_ix = start_ix + ix; + let candidate_id = mat.candidate_id; + let completion = &completions_guard[candidate_id]; + + let documentation = if show_completion_documentation { + &completion.documentation + } else { + &None + }; + items.push( MouseEventHandler::new::( mat.candidate_id, @@ -986,22 +1323,18 @@ impl CompletionsMenu { ), ); - if let Some(server_name) = get_server_name(completion.server_id) { + if let Some(Documentation::SingleLine(text)) = documentation { Flex::row() .with_child(completion_label) .with_children((|| { - if !needs_server_name { - return None; - } - let text_style = TextStyle { - color: style.autocomplete.server_name_color, + color: style.autocomplete.inline_docs_color, font_size: style.text.font_size - * style.autocomplete.server_name_size_percent, + * style.autocomplete.inline_docs_size_percent, ..style.text.clone() }; - let label = Text::new(server_name, text_style) + let label = Text::new(text.clone(), text_style) .aligned() .constrained() .dynamically(move |constraint, _, _| { @@ -1021,7 +1354,7 @@ impl CompletionsMenu { .with_style( style .autocomplete - .server_name_container, + .inline_docs_container, ) .into_any(), ) @@ -1060,15 +1393,59 @@ impl CompletionsMenu { ) .map(|task| task.detach()); }) + .constrained() + .with_min_width(style.autocomplete.completion_min_width) + .with_max_width(style.autocomplete.completion_max_width) .into_any(), ); } - }, - ) - .with_width_from_item(widest_completion_ix) - .contained() - .with_style(container_style) - .into_any() + } + }) + .with_width_from_item(widest_completion_ix); + + enum MultiLineDocumentation {} + + Flex::row() + .with_child(list.flex(1., false)) + .with_children({ + let mat = &self.matches[selected_item]; + let completions = self.completions.read(); + let completion = &completions[mat.candidate_id]; + let documentation = &completion.documentation; + + match documentation { + Some(Documentation::MultiLinePlainText(text)) => Some( + Flex::column() + .scrollable::(0, None, cx) + .with_child( + Text::new(text.clone(), style.text.clone()).with_soft_wrap(true), + ) + .contained() + .with_style(style.autocomplete.alongside_docs_container) + .constrained() + .with_max_width(style.autocomplete.alongside_docs_max_width) + .flex(1., false), + ), + + Some(Documentation::MultiLineMarkdown(parsed)) => Some( + Flex::column() + .scrollable::(0, None, cx) + .with_child(render_parsed_markdown::( + parsed, &style, workspace, cx, + )) + .contained() + .with_style(style.autocomplete.alongside_docs_container) + .constrained() + .with_max_width(style.autocomplete.alongside_docs_max_width) + .flex(1., false), + ), + + _ => None, + } + }) + .contained() + .with_style(style.autocomplete.container) + .into_any() } pub async fn filter(&mut self, query: Option<&str>, executor: Arc) { @@ -1095,13 +1472,13 @@ impl CompletionsMenu { .collect() }; - //Remove all candidates where the query's start does not match the start of any word in the candidate + // Remove all candidates where the query's start does not match the start of any word in the candidate if let Some(query) = query { if let Some(query_start) = query.chars().next() { matches.retain(|string_match| { split_words(&string_match.string).any(|word| { - //Check that the first codepoint of the word as lowercase matches the first - //codepoint of the query as lowercase + // Check that the first codepoint of the word as lowercase matches the first + // codepoint of the query as lowercase word.chars() .flat_map(|codepoint| codepoint.to_lowercase()) .zip(query_start.to_lowercase()) @@ -1111,23 +1488,27 @@ impl CompletionsMenu { } } + let completions = self.completions.read(); matches.sort_unstable_by_key(|mat| { - let completion = &self.completions[mat.candidate_id]; + let completion = &completions[mat.candidate_id]; ( completion.lsp_completion.sort_text.as_ref(), Reverse(OrderedFloat(mat.score)), completion.sort_key(), ) }); + drop(completions); for mat in &mut matches { - let filter_start = self.completions[mat.candidate_id].label.filter_range.start; + let completions = self.completions.read(); + let filter_start = completions[mat.candidate_id].label.filter_range.start; for position in &mut mat.positions { *position += filter_start; } } self.matches = matches.into(); + self.selected_item = 0; } } @@ -1563,7 +1944,7 @@ impl Editor { background_highlights: Default::default(), inlay_background_highlights: Default::default(), nav_history: None, - context_menu: None, + context_menu: RwLock::new(None), mouse_context_menu: cx .add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)), completion_tasks: Default::default(), @@ -1858,10 +2239,12 @@ impl Editor { if local { let new_cursor_position = self.selections.newest_anchor().head(); - let completion_menu = match self.context_menu.as_mut() { + let mut context_menu = self.context_menu.write(); + let completion_menu = match context_menu.as_ref() { Some(ContextMenu::Completions(menu)) => Some(menu), + _ => { - self.context_menu.take(); + *context_menu = None; None } }; @@ -1873,13 +2256,39 @@ impl Editor { if kind == Some(CharKind::Word) && word_range.to_inclusive().contains(&cursor_position) { + let mut completion_menu = completion_menu.clone(); + drop(context_menu); + let query = Self::completion_query(buffer, cursor_position); - cx.background() - .block(completion_menu.filter(query.as_deref(), cx.background().clone())); + cx.spawn(move |this, mut cx| async move { + completion_menu + .filter(query.as_deref(), cx.background().clone()) + .await; + + this.update(&mut cx, |this, cx| { + let mut context_menu = this.context_menu.write(); + let Some(ContextMenu::Completions(menu)) = context_menu.as_ref() else { + return; + }; + + if menu.id > completion_menu.id { + return; + } + + *context_menu = Some(ContextMenu::Completions(completion_menu)); + drop(context_menu); + cx.notify(); + }) + }) + .detach(); + self.show_completions(&ShowCompletions, cx); } else { + drop(context_menu); self.hide_context_menu(cx); } + } else { + drop(context_menu); } hide_hover(self, cx); @@ -2912,6 +3321,7 @@ impl Editor { false }); } + fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option { let offset = position.to_offset(buffer); let (word_range, kind) = buffer.surrounding_word(offset); @@ -3150,7 +3560,6 @@ impl Editor { }); let id = post_inc(&mut self.next_completion_id); - let project = self.project.clone(); let task = cx.spawn(|this, mut cx| { async move { let menu = if let Some(completions) = completions.await.log_err() { @@ -3169,8 +3578,7 @@ impl Editor { }) .collect(), buffer, - project, - completions: completions.into(), + completions: Arc::new(RwLock::new(completions.into())), matches: Vec::new().into(), selected_item: 0, list: Default::default(), @@ -3179,6 +3587,9 @@ impl Editor { if menu.matches.is_empty() { None } else { + _ = this.update(&mut cx, |editor, cx| { + menu.pre_resolve_completion_documentation(editor.project.clone(), cx); + }); Some(menu) } } else { @@ -3188,23 +3599,30 @@ impl Editor { this.update(&mut cx, |this, cx| { this.completion_tasks.retain(|(task_id, _)| *task_id > id); - match this.context_menu.as_ref() { + let mut context_menu = this.context_menu.write(); + match context_menu.as_ref() { None => {} + Some(ContextMenu::Completions(prev_menu)) => { if prev_menu.id > id { return; } } + _ => return, } if this.focused && menu.is_some() { let menu = menu.unwrap(); - this.show_context_menu(ContextMenu::Completions(menu), cx); + *context_menu = Some(ContextMenu::Completions(menu)); + drop(context_menu); + this.discard_copilot_suggestion(cx); + cx.notify(); } else if this.completion_tasks.is_empty() { // If there are no more completion tasks and the last menu was // empty, we should hide it. If it was already hidden, we should // also show the copilot suggestion when available. + drop(context_menu); if this.hide_context_menu(cx).is_none() { this.update_visible_copilot_suggestion(cx); } @@ -3235,7 +3653,8 @@ impl Editor { .matches .get(action.item_ix.unwrap_or(completions_menu.selected_item))?; let buffer_handle = completions_menu.buffer; - let completion = completions_menu.completions.get(mat.candidate_id)?; + let completions = completions_menu.completions.read(); + let completion = completions.get(mat.candidate_id)?; let snippet; let text; @@ -3348,14 +3767,13 @@ impl Editor { } pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext) { - if matches!( - self.context_menu.as_ref(), - Some(ContextMenu::CodeActions(_)) - ) { - self.context_menu.take(); + let mut context_menu = self.context_menu.write(); + if matches!(context_menu.as_ref(), Some(ContextMenu::CodeActions(_))) { + *context_menu = None; cx.notify(); return; } + drop(context_menu); let deployed_from_indicator = action.deployed_from_indicator; let mut task = self.code_actions_task.take(); @@ -3368,16 +3786,16 @@ impl Editor { this.update(&mut cx, |this, cx| { if this.focused { if let Some((buffer, actions)) = this.available_code_actions.clone() { - this.show_context_menu( - ContextMenu::CodeActions(CodeActionsMenu { + this.completion_tasks.clear(); + this.discard_copilot_suggestion(cx); + *this.context_menu.write() = + Some(ContextMenu::CodeActions(CodeActionsMenu { buffer, actions, selected_item: Default::default(), list: Default::default(), deployed_from_indicator, - }), - cx, - ); + })); } } })?; @@ -3740,7 +4158,10 @@ impl Editor { if self.has_active_copilot_suggestion(cx) { self.cycle_copilot_suggestions(Direction::Next, cx); } else { - self.refresh_copilot_suggestions(false, cx); + let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); + if is_copilot_disabled { + cx.propagate_action(); + } } } @@ -3752,7 +4173,10 @@ impl Editor { if self.has_active_copilot_suggestion(cx) { self.cycle_copilot_suggestions(Direction::Prev, cx); } else { - self.refresh_copilot_suggestions(false, cx); + let is_copilot_disabled = self.refresh_copilot_suggestions(false, cx).is_none(); + if is_copilot_disabled { + cx.propagate_action(); + } } } @@ -3841,7 +4265,7 @@ impl Editor { let selection = self.selections.newest_anchor(); let cursor = selection.head(); - if self.context_menu.is_some() + if self.context_menu.read().is_some() || !self.completion_tasks.is_empty() || selection.start != selection.end { @@ -3975,6 +4399,7 @@ impl Editor { pub fn context_menu_visible(&self) -> bool { self.context_menu + .read() .as_ref() .map_or(false, |menu| menu.visible()) } @@ -3985,24 +4410,20 @@ impl Editor { style: EditorStyle, cx: &mut ViewContext, ) -> Option<(DisplayPoint, AnyElement)> { - self.context_menu - .as_ref() - .map(|menu| menu.render(cursor_position, style, cx)) - } - - fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext) { - if !matches!(menu, ContextMenu::Completions(_)) { - self.completion_tasks.clear(); - } - self.context_menu = Some(menu); - self.discard_copilot_suggestion(cx); - cx.notify(); + self.context_menu.read().as_ref().map(|menu| { + menu.render( + cursor_position, + style, + self.workspace.as_ref().map(|(w, _)| w.clone()), + cx, + ) + }) } fn hide_context_menu(&mut self, cx: &mut ViewContext) -> Option { cx.notify(); self.completion_tasks.clear(); - let context_menu = self.context_menu.take(); + let context_menu = self.context_menu.write().take(); if context_menu.is_some() { self.update_visible_copilot_suggestion(cx); } @@ -5354,8 +5775,9 @@ impl Editor { if self .context_menu + .write() .as_mut() - .map(|menu| menu.select_last(cx)) + .map(|menu| menu.select_last(self.project.as_ref(), cx)) .unwrap_or(false) { return; @@ -5398,26 +5820,26 @@ impl Editor { } pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext) { - if let Some(context_menu) = self.context_menu.as_mut() { - context_menu.select_first(cx); + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_first(self.project.as_ref(), cx); } } pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext) { - if let Some(context_menu) = self.context_menu.as_mut() { - context_menu.select_prev(cx); + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_prev(self.project.as_ref(), cx); } } pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext) { - if let Some(context_menu) = self.context_menu.as_mut() { - context_menu.select_next(cx); + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_next(self.project.as_ref(), cx); } } pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext) { - if let Some(context_menu) = self.context_menu.as_mut() { - context_menu.select_last(cx); + if let Some(context_menu) = self.context_menu.write().as_mut() { + context_menu.select_last(self.project.as_ref(), cx); } } @@ -8914,7 +9336,7 @@ impl View for Editor { keymap.add_identifier("renaming"); } if self.context_menu_visible() { - match self.context_menu.as_ref() { + match self.context_menu.read().as_ref() { Some(ContextMenu::Completions(_)) => { keymap.add_identifier("menu"); keymap.add_identifier("showing_completions") diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index b06f23429a15b17368c8da23a755ad2fe3c637c5..75f8b800f93757bec3bcbbf01b68226880267d57 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -7,6 +7,7 @@ pub struct EditorSettings { pub cursor_blink: bool, pub hover_popover_enabled: bool, pub show_completions_on_input: bool, + pub show_completion_documentation: bool, pub use_on_type_format: bool, pub scrollbar: Scrollbar, pub relative_line_numbers: bool, @@ -33,6 +34,7 @@ pub struct EditorSettingsContent { pub cursor_blink: Option, pub hover_popover_enabled: Option, pub show_completions_on_input: Option, + pub show_completion_documentation: Option, pub use_on_type_format: Option, pub scrollbar: Option, pub relative_line_numbers: Option, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index dc723c70127496cef84b6667799ee04e4e558326..435e05018cd4fa905f0dff4637fed29f847625e9 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -19,8 +19,8 @@ use gpui::{ use indoc::indoc; use language::{ language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent}, - BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, LanguageRegistry, - Override, Point, + BracketPairConfig, BundledFormatter, FakeLspAdapter, LanguageConfig, LanguageConfigOverride, + LanguageRegistry, Override, Point, }; use parking_lot::Mutex; use project::project_settings::{LspSettings, ProjectSettings}; @@ -5076,7 +5076,9 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { - init_test(cx, |_| {}); + init_test(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::LanguageServer) + }); let mut language = Language::new( LanguageConfig { @@ -5092,6 +5094,12 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { document_formatting_provider: Some(lsp::OneOf::Left(true)), ..Default::default() }, + // Enable Prettier formatting for the same buffer, and ensure + // LSP is called instead of Prettier. + enabled_formatters: vec![BundledFormatter::Prettier { + parser_name: Some("test_parser"), + plugin_names: Vec::new(), + }], ..Default::default() })) .await; @@ -5100,7 +5108,10 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { fs.insert_file("/file.rs", Default::default()).await; let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - project.update(cx, |project, _| project.languages().add(Arc::new(language))); + project.update(cx, |project, _| { + project.enable_test_prettier(&[]); + project.languages().add(Arc::new(language)); + }); let buffer = project .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) .await @@ -5218,7 +5229,9 @@ async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) { - init_test(cx, |_| {}); + init_test(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::Auto) + }); let mut cx = EditorLspTestContext::new_rust( lsp::ServerCapabilities { @@ -5417,9 +5430,9 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { additional edit "}); cx.simulate_keystroke(" "); - assert!(cx.editor(|e, _| e.context_menu.is_none())); + assert!(cx.editor(|e, _| e.context_menu.read().is_none())); cx.simulate_keystroke("s"); - assert!(cx.editor(|e, _| e.context_menu.is_none())); + assert!(cx.editor(|e, _| e.context_menu.read().is_none())); cx.assert_editor_state(indoc! {" one.second_completion @@ -5481,12 +5494,12 @@ async fn test_completion(cx: &mut gpui::TestAppContext) { }); cx.set_state("editorˇ"); cx.simulate_keystroke("."); - assert!(cx.editor(|e, _| e.context_menu.is_none())); + assert!(cx.editor(|e, _| e.context_menu.read().is_none())); cx.simulate_keystroke("c"); cx.simulate_keystroke("l"); cx.simulate_keystroke("o"); cx.assert_editor_state("editor.cloˇ"); - assert!(cx.editor(|e, _| e.context_menu.is_none())); + assert!(cx.editor(|e, _| e.context_menu.read().is_none())); cx.update_editor(|editor, cx| { editor.show_completions(&ShowCompletions, cx); }); @@ -7775,7 +7788,7 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui: cx.simulate_keystroke("-"); cx.foreground().run_until_parked(); cx.update_editor(|editor, _| { - if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { + if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { assert_eq!( menu.matches.iter().map(|m| &m.string).collect::>(), &["bg-red", "bg-blue", "bg-yellow"] @@ -7788,7 +7801,7 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui: cx.simulate_keystroke("l"); cx.foreground().run_until_parked(); cx.update_editor(|editor, _| { - if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { + if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { assert_eq!( menu.matches.iter().map(|m| &m.string).collect::>(), &["bg-blue", "bg-yellow"] @@ -7804,7 +7817,7 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui: cx.simulate_keystroke("l"); cx.foreground().run_until_parked(); cx.update_editor(|editor, _| { - if let Some(ContextMenu::Completions(menu)) = &editor.context_menu { + if let Some(ContextMenu::Completions(menu)) = editor.context_menu.read().as_ref() { assert_eq!( menu.matches.iter().map(|m| &m.string).collect::>(), &["bg-yellow"] @@ -7815,6 +7828,75 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut gpui: }); } +#[gpui::test] +async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { + init_test(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::Prettier) + }); + + let mut language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + + let test_plugin = "test_plugin"; + let _ = language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + enabled_formatters: vec![BundledFormatter::Prettier { + parser_name: Some("test_parser"), + plugin_names: vec![test_plugin], + }], + ..Default::default() + })) + .await; + + let fs = FakeFs::new(cx.background()); + fs.insert_file("/file.rs", Default::default()).await; + + let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + let prettier_format_suffix = project.update(cx, |project, _| { + let suffix = project.enable_test_prettier(&[test_plugin]); + project.languages().add(Arc::new(language)); + suffix + }); + let buffer = project + .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .await + .unwrap(); + + let buffer_text = "one\ntwo\nthree\n"; + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + let editor = cx.add_window(|cx| build_editor(buffer, cx)).root(cx); + editor.update(cx, |editor, cx| editor.set_text(buffer_text, cx)); + + let format = editor.update(cx, |editor, cx| { + editor.perform_format(project.clone(), FormatTrigger::Manual, cx) + }); + format.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + buffer_text.to_string() + prettier_format_suffix, + "Test prettier formatting was not applied to the original buffer text", + ); + + update_test_language_settings(cx, |settings| { + settings.defaults.formatter = Some(language_settings::Formatter::Auto) + }); + let format = editor.update(cx, |editor, cx| { + editor.perform_format(project.clone(), FormatTrigger::Manual, cx) + }); + format.await.unwrap(); + assert_eq!( + editor.read_with(cx, |editor, cx| editor.text(cx)), + buffer_text.to_string() + prettier_format_suffix + "\n" + prettier_format_suffix, + "Autoformatting (via test prettier) was not applied to the original buffer text", + ); +} + fn empty_range(row: usize, column: usize) -> Range { let point = DisplayPoint::new(row as u32, column as u32); point..point diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 924d66c21c5532fa79a2abb0af5ac1116cde463e..00c8508b6c3800662baf4728bf94c3e167c76dbf 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2428,7 +2428,7 @@ impl Element for EditorElement { } let active = matches!( - editor.context_menu, + editor.context_menu.read().as_ref(), Some(crate::ContextMenu::CodeActions(_)) ); @@ -2439,9 +2439,13 @@ impl Element for EditorElement { } let visible_rows = start_row..start_row + line_layouts.len() as u32; - let mut hover = editor - .hover_state - .render(&snapshot, &style, visible_rows, cx); + let mut hover = editor.hover_state.render( + &snapshot, + &style, + visible_rows, + editor.workspace.as_ref().map(|(w, _)| w.clone()), + cx, + ); let mode = editor.mode; let mut fold_indicators = editor.render_fold_indicators( diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index 553cb321c399b4edd75c650b0b186cefd55517b5..5b3985edf933b75deaaa3ba49803a6151442be37 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -9,13 +9,15 @@ use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Text}, platform::{CursorStyle, MouseButton}, - AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, + AnyElement, AppContext, Element, ModelHandle, Task, ViewContext, WeakViewHandle, +}; +use language::{ + markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown, }; -use language::{Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry}; use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project}; -use rich_text::{new_paragraph, render_code, render_markdown_mut, RichText}; use std::{ops::Range, sync::Arc, time::Duration}; use util::TryFutureExt; +use workspace::Workspace; pub const HOVER_DELAY_MILLIS: u64 = 350; pub const HOVER_REQUEST_DELAY_MILLIS: u64 = 200; @@ -105,12 +107,15 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie this.hover_state.diagnostic_popover = None; })?; + let language_registry = project.update(&mut cx, |p, _| p.languages().clone()); + let blocks = vec![inlay_hover.tooltip]; + let parsed_content = parse_blocks(&blocks, &language_registry, None).await; + let hover_popover = InfoPopover { project: project.clone(), symbol_range: RangeInEditor::Inlay(inlay_hover.range.clone()), - blocks: vec![inlay_hover.tooltip], - language: None, - rendered_content: None, + blocks, + parsed_content, }; this.update(&mut cx, |this, cx| { @@ -288,35 +293,38 @@ fn show_hover( }); })?; - // Construct new hover popover from hover request - let hover_popover = hover_request.await.ok().flatten().and_then(|hover_result| { - if hover_result.is_empty() { - return None; + let hover_result = hover_request.await.ok().flatten(); + let hover_popover = match hover_result { + Some(hover_result) if !hover_result.is_empty() => { + // Create symbol range of anchors for highlighting and filtering of future requests. + let range = if let Some(range) = hover_result.range { + let start = snapshot + .buffer_snapshot + .anchor_in_excerpt(excerpt_id.clone(), range.start); + let end = snapshot + .buffer_snapshot + .anchor_in_excerpt(excerpt_id.clone(), range.end); + + start..end + } else { + anchor..anchor + }; + + let language_registry = project.update(&mut cx, |p, _| p.languages().clone()); + let blocks = hover_result.contents; + let language = hover_result.language; + let parsed_content = parse_blocks(&blocks, &language_registry, language).await; + + Some(InfoPopover { + project: project.clone(), + symbol_range: RangeInEditor::Text(range), + blocks, + parsed_content, + }) } - // Create symbol range of anchors for highlighting and filtering - // of future requests. - let range = if let Some(range) = hover_result.range { - let start = snapshot - .buffer_snapshot - .anchor_in_excerpt(excerpt_id.clone(), range.start); - let end = snapshot - .buffer_snapshot - .anchor_in_excerpt(excerpt_id.clone(), range.end); - - start..end - } else { - anchor..anchor - }; - - Some(InfoPopover { - project: project.clone(), - symbol_range: RangeInEditor::Text(range), - blocks: hover_result.contents, - language: hover_result.language, - rendered_content: None, - }) - }); + _ => None, + }; this.update(&mut cx, |this, cx| { if let Some(symbol_range) = hover_popover @@ -345,44 +353,56 @@ fn show_hover( editor.hover_state.info_task = Some(task); } -fn render_blocks( +async fn parse_blocks( blocks: &[HoverBlock], language_registry: &Arc, - language: Option<&Arc>, -) -> RichText { - let mut data = RichText { - text: Default::default(), - highlights: Default::default(), - region_ranges: Default::default(), - regions: Default::default(), - }; + language: Option>, +) -> markdown::ParsedMarkdown { + let mut text = String::new(); + let mut highlights = Vec::new(); + let mut region_ranges = Vec::new(); + let mut regions = Vec::new(); for block in blocks { match &block.kind { HoverBlockKind::PlainText => { - new_paragraph(&mut data.text, &mut Vec::new()); - data.text.push_str(&block.text); + markdown::new_paragraph(&mut text, &mut Vec::new()); + text.push_str(&block.text); } + HoverBlockKind::Markdown => { - render_markdown_mut(&block.text, language_registry, language, &mut data) + markdown::parse_markdown_block( + &block.text, + language_registry, + language.clone(), + &mut text, + &mut highlights, + &mut region_ranges, + &mut regions, + ) + .await } + HoverBlockKind::Code { language } => { if let Some(language) = language_registry .language_for_name(language) .now_or_never() .and_then(Result::ok) { - render_code(&mut data.text, &mut data.highlights, &block.text, &language); + markdown::highlight_code(&mut text, &mut highlights, &block.text, &language); } else { - data.text.push_str(&block.text); + text.push_str(&block.text); } } } } - data.text = data.text.trim().to_string(); - - data + ParsedMarkdown { + text: text.trim().to_string(), + highlights, + region_ranges, + regions, + } } #[derive(Default)] @@ -403,6 +423,7 @@ impl HoverState { snapshot: &EditorSnapshot, style: &EditorStyle, visible_rows: Range, + workspace: Option>, cx: &mut ViewContext, ) -> Option<(DisplayPoint, Vec>)> { // If there is a diagnostic, position the popovers based on that. @@ -432,7 +453,7 @@ impl HoverState { elements.push(diagnostic_popover.render(style, cx)); } if let Some(info_popover) = self.info_popover.as_mut() { - elements.push(info_popover.render(style, cx)); + elements.push(info_popover.render(style, workspace, cx)); } Some((point, elements)) @@ -444,32 +465,23 @@ pub struct InfoPopover { pub project: ModelHandle, symbol_range: RangeInEditor, pub blocks: Vec, - language: Option>, - rendered_content: Option, + parsed_content: ParsedMarkdown, } impl InfoPopover { pub fn render( &mut self, style: &EditorStyle, + workspace: Option>, cx: &mut ViewContext, ) -> AnyElement { - let rendered_content = self.rendered_content.get_or_insert_with(|| { - render_blocks( - &self.blocks, - self.project.read(cx).languages(), - self.language.as_ref(), - ) - }); - - MouseEventHandler::new::(0, cx, move |_, cx| { - let code_span_background_color = style.document_highlight_read_background; + MouseEventHandler::new::(0, cx, |_, cx| { Flex::column() - .scrollable::(1, None, cx) - .with_child(rendered_content.element( - style.syntax.clone(), - style.text.clone(), - code_span_background_color, + .scrollable::(0, None, cx) + .with_child(crate::render_parsed_markdown::( + &self.parsed_content, + style, + workspace, cx, )) .contained() @@ -572,7 +584,6 @@ mod tests { use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet}; use lsp::LanguageServerId; use project::{HoverBlock, HoverBlockKind}; - use rich_text::Highlight; use smol::stream::StreamExt; use unindent::Unindent; use util::test::marked_text_ranges; @@ -793,7 +804,7 @@ mod tests { }], ); - let rendered = render_blocks(&blocks, &Default::default(), None); + let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None)); assert_eq!( rendered.text, code_str.trim(), @@ -900,7 +911,7 @@ mod tests { // Links Row { blocks: vec![HoverBlock { - text: "one [two](the-url) three".to_string(), + text: "one [two](https://the-url) three".to_string(), kind: HoverBlockKind::Markdown, }], expected_marked_text: "one «two» three".to_string(), @@ -921,7 +932,7 @@ mod tests { - a - b * two - - [c](the-url) + - [c](https://the-url) - d" .unindent(), kind: HoverBlockKind::Markdown, @@ -985,7 +996,7 @@ mod tests { expected_styles, } in &rows[0..] { - let rendered = render_blocks(&blocks, &Default::default(), None); + let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None)); let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false); let expected_highlights = ranges @@ -1001,11 +1012,8 @@ mod tests { .highlights .iter() .filter_map(|(range, highlight)| { - let style = match highlight { - Highlight::Id(id) => id.style(&style.syntax)?, - Highlight::Highlight(style) => style.clone(), - }; - Some((range.clone(), style)) + let highlight = highlight.to_highlight_style(&style.syntax)?; + Some((range.clone(), highlight)) }) .collect(); @@ -1258,11 +1266,7 @@ mod tests { "Popover range should match the new type label part" ); assert_eq!( - popover - .rendered_content - .as_ref() - .expect("should have label text for new type hint") - .text, + popover.parsed_content.text, format!("A tooltip for `{new_type_label}`"), "Rendered text should not anyhow alter backticks" ); @@ -1316,11 +1320,7 @@ mod tests { "Popover range should match the struct label part" ); assert_eq!( - popover - .rendered_content - .as_ref() - .expect("should have label text for struct hint") - .text, + popover.parsed_content.text, format!("A tooltip for {struct_label}"), "Rendered markdown element should remove backticks from text" ); diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index c5d17dfd2eb8ac67f81a19f63ccbb7f32f9c1c62..23a117405cda1a42c4b16663edc7dc0ccf84c3dd 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -498,77 +498,91 @@ impl MultiBuffer { } } - for (buffer_id, mut edits) in buffer_edits { - edits.sort_unstable_by_key(|edit| edit.range.start); - self.buffers.borrow()[&buffer_id] - .buffer - .update(cx, |buffer, cx| { - let mut edits = edits.into_iter().peekable(); - let mut insertions = Vec::new(); - let mut original_indent_columns = Vec::new(); - let mut deletions = Vec::new(); - let empty_str: Arc = "".into(); - while let Some(BufferEdit { - mut range, - new_text, - mut is_insertion, - original_indent_column, - }) = edits.next() - { + drop(cursor); + drop(snapshot); + // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR. + fn tail( + this: &mut MultiBuffer, + buffer_edits: HashMap>, + autoindent_mode: Option, + edited_excerpt_ids: Vec, + cx: &mut ModelContext, + ) { + for (buffer_id, mut edits) in buffer_edits { + edits.sort_unstable_by_key(|edit| edit.range.start); + this.buffers.borrow()[&buffer_id] + .buffer + .update(cx, |buffer, cx| { + let mut edits = edits.into_iter().peekable(); + let mut insertions = Vec::new(); + let mut original_indent_columns = Vec::new(); + let mut deletions = Vec::new(); + let empty_str: Arc = "".into(); while let Some(BufferEdit { - range: next_range, - is_insertion: next_is_insertion, - .. - }) = edits.peek() + mut range, + new_text, + mut is_insertion, + original_indent_column, + }) = edits.next() { - if range.end >= next_range.start { - range.end = cmp::max(next_range.end, range.end); - is_insertion |= *next_is_insertion; - edits.next(); - } else { - break; + while let Some(BufferEdit { + range: next_range, + is_insertion: next_is_insertion, + .. + }) = edits.peek() + { + if range.end >= next_range.start { + range.end = cmp::max(next_range.end, range.end); + is_insertion |= *next_is_insertion; + edits.next(); + } else { + break; + } } - } - if is_insertion { - original_indent_columns.push(original_indent_column); - insertions.push(( - buffer.anchor_before(range.start)..buffer.anchor_before(range.end), - new_text.clone(), - )); - } else if !range.is_empty() { - deletions.push(( - buffer.anchor_before(range.start)..buffer.anchor_before(range.end), - empty_str.clone(), - )); + if is_insertion { + original_indent_columns.push(original_indent_column); + insertions.push(( + buffer.anchor_before(range.start) + ..buffer.anchor_before(range.end), + new_text.clone(), + )); + } else if !range.is_empty() { + deletions.push(( + buffer.anchor_before(range.start) + ..buffer.anchor_before(range.end), + empty_str.clone(), + )); + } } - } - let deletion_autoindent_mode = - if let Some(AutoindentMode::Block { .. }) = autoindent_mode { - Some(AutoindentMode::Block { - original_indent_columns: Default::default(), - }) - } else { - None - }; - let insertion_autoindent_mode = - if let Some(AutoindentMode::Block { .. }) = autoindent_mode { - Some(AutoindentMode::Block { - original_indent_columns, - }) - } else { - None - }; + let deletion_autoindent_mode = + if let Some(AutoindentMode::Block { .. }) = autoindent_mode { + Some(AutoindentMode::Block { + original_indent_columns: Default::default(), + }) + } else { + None + }; + let insertion_autoindent_mode = + if let Some(AutoindentMode::Block { .. }) = autoindent_mode { + Some(AutoindentMode::Block { + original_indent_columns, + }) + } else { + None + }; - buffer.edit(deletions, deletion_autoindent_mode, cx); - buffer.edit(insertions, insertion_autoindent_mode, cx); - }) - } + buffer.edit(deletions, deletion_autoindent_mode, cx); + buffer.edit(insertions, insertion_autoindent_mode, cx); + }) + } - cx.emit(Event::ExcerptsEdited { - ids: edited_excerpt_ids, - }); + cx.emit(Event::ExcerptsEdited { + ids: edited_excerpt_ids, + }); + } + tail(self, buffer_edits, autoindent_mode, edited_excerpt_ids, cx); } pub fn start_transaction(&mut self, cx: &mut ModelContext) -> Option { diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 1d95db9b6cf20ddbe1cf87c5c94c73eb0e666d62..1bc8fa9a241b8d2bf2a1da48b23760fdca0c66a0 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -85,7 +85,7 @@ pub struct RemoveOptions { pub ignore_if_not_exists: bool, } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct Metadata { pub inode: u64, pub mtime: SystemTime, diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index cdce0423fd6d9eef4e0e5be11e584b02d58fec76..ba387c5e48a7068856e4f8bfb31bdf36443d9c68 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -2,7 +2,8 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc}; use crate::{ json::{self, ToJson, Value}, - AnyElement, Axis, Element, ElementStateHandle, SizeConstraint, Vector2FExt, ViewContext, + AnyElement, Axis, Element, ElementStateHandle, SizeConstraint, TypeTag, Vector2FExt, + ViewContext, }; use pathfinder_geometry::{ rect::RectF, @@ -10,10 +11,10 @@ use pathfinder_geometry::{ }; use serde_json::json; -#[derive(Default)] struct ScrollState { scroll_to: Cell>, scroll_position: Cell, + type_tag: TypeTag, } pub struct Flex { @@ -66,8 +67,14 @@ impl Flex { where Tag: 'static, { - let scroll_state = cx.default_element_state::>(element_id); - scroll_state.read(cx).scroll_to.set(scroll_to); + let scroll_state = cx.element_state::>( + element_id, + Rc::new(ScrollState { + scroll_to: Cell::new(scroll_to), + scroll_position: Default::default(), + type_tag: TypeTag::new::(), + }), + ); self.scroll_state = Some((scroll_state, cx.handle().id())); self } @@ -276,38 +283,44 @@ impl Element for Flex { if let Some((scroll_state, id)) = &self.scroll_state { let scroll_state = scroll_state.read(cx).clone(); cx.scene().push_mouse_region( - crate::MouseRegion::new::(*id, 0, bounds) - .on_scroll({ - let axis = self.axis; - move |e, _: &mut V, cx| { - if remaining_space < 0. { - let scroll_delta = e.delta.raw(); - - let mut delta = match axis { - Axis::Horizontal => { - if scroll_delta.x().abs() >= scroll_delta.y().abs() { - scroll_delta.x() - } else { - scroll_delta.y() - } + crate::MouseRegion::from_handlers( + scroll_state.type_tag, + *id, + 0, + bounds, + Default::default(), + ) + .on_scroll({ + let axis = self.axis; + move |e, _: &mut V, cx| { + if remaining_space < 0. { + let scroll_delta = e.delta.raw(); + + let mut delta = match axis { + Axis::Horizontal => { + if scroll_delta.x().abs() >= scroll_delta.y().abs() { + scroll_delta.x() + } else { + scroll_delta.y() } - Axis::Vertical => scroll_delta.y(), - }; - if !e.delta.precise() { - delta *= 20.; } + Axis::Vertical => scroll_delta.y(), + }; + if !e.delta.precise() { + delta *= 20.; + } - scroll_state - .scroll_position - .set(scroll_state.scroll_position.get() - delta); + scroll_state + .scroll_position + .set(scroll_state.scroll_position.get() - delta); - cx.notify(); - } else { - cx.propagate_event(); - } + cx.notify(); + } else { + cx.propagate_event(); } - }) - .on_move(|_, _: &mut V, _| { /* Capture move events */ }), + } + }) + .on_move(|_, _: &mut V, _| { /* Capture move events */ }), ) } diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index cf468020ceef0bc2c97b976beb018117a03585ac..f152d34919949e21c68a115c82aa7bec6a390f2c 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -45,6 +45,7 @@ lazy_static.workspace = true log.workspace = true parking_lot.workspace = true postage.workspace = true +pulldown-cmark = { version = "0.9.2", default-features = false } regex.workspace = true schemars.workspace = true serde.workspace = true diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 207c41e7cdf21a8ec4d61232e071296184b52a5f..78562ba8c4408d4d1803d5fe6a5a9e39a6905c0c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1,11 +1,13 @@ pub use crate::{ diagnostic_set::DiagnosticSet, highlight_map::{HighlightId, HighlightMap}, + markdown::ParsedMarkdown, proto, BracketPair, Grammar, Language, LanguageConfig, LanguageRegistry, PLAIN_TEXT, }; use crate::{ diagnostic_set::{DiagnosticEntry, DiagnosticGroup}, language_settings::{language_settings, LanguageSettings}, + markdown::parse_markdown, outline::OutlineItem, syntax_map::{ SyntaxLayerInfo, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches, @@ -143,11 +145,51 @@ pub struct Diagnostic { pub is_unnecessary: bool, } +pub async fn prepare_completion_documentation( + documentation: &lsp::Documentation, + language_registry: &Arc, + language: Option>, +) -> Documentation { + match documentation { + lsp::Documentation::String(text) => { + if text.lines().count() <= 1 { + Documentation::SingleLine(text.clone()) + } else { + Documentation::MultiLinePlainText(text.clone()) + } + } + + lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value }) => match kind { + lsp::MarkupKind::PlainText => { + if value.lines().count() <= 1 { + Documentation::SingleLine(value.clone()) + } else { + Documentation::MultiLinePlainText(value.clone()) + } + } + + lsp::MarkupKind::Markdown => { + let parsed = parse_markdown(value, language_registry, language).await; + Documentation::MultiLineMarkdown(parsed) + } + }, + } +} + +#[derive(Clone, Debug)] +pub enum Documentation { + Undocumented, + SingleLine(String), + MultiLinePlainText(String), + MultiLineMarkdown(ParsedMarkdown), +} + #[derive(Clone, Debug)] pub struct Completion { pub old_range: Range, pub new_text: String, pub label: CodeLabel, + pub documentation: Option, pub server_id: LanguageServerId, pub lsp_completion: lsp::CompletionItem, } @@ -1406,82 +1448,95 @@ impl Buffer { return None; } - self.start_transaction(); - self.pending_autoindent.take(); - let autoindent_request = autoindent_mode - .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode))); - - let edit_operation = self.text.edit(edits.iter().cloned()); - let edit_id = edit_operation.timestamp(); + // Non-generic part hoisted out to reduce LLVM IR size. + fn tail( + this: &mut Buffer, + edits: Vec<(Range, Arc)>, + autoindent_mode: Option, + cx: &mut ModelContext, + ) -> Option { + this.start_transaction(); + this.pending_autoindent.take(); + let autoindent_request = autoindent_mode + .and_then(|mode| this.language.as_ref().map(|_| (this.snapshot(), mode))); + + let edit_operation = this.text.edit(edits.iter().cloned()); + let edit_id = edit_operation.timestamp(); + + if let Some((before_edit, mode)) = autoindent_request { + let mut delta = 0isize; + let entries = edits + .into_iter() + .enumerate() + .zip(&edit_operation.as_edit().unwrap().new_text) + .map(|((ix, (range, _)), new_text)| { + let new_text_length = new_text.len(); + let old_start = range.start.to_point(&before_edit); + let new_start = (delta + range.start as isize) as usize; + delta += + new_text_length as isize - (range.end as isize - range.start as isize); + + let mut range_of_insertion_to_indent = 0..new_text_length; + let mut first_line_is_new = false; + let mut original_indent_column = None; + + // When inserting an entire line at the beginning of an existing line, + // treat the insertion as new. + if new_text.contains('\n') + && old_start.column + <= before_edit.indent_size_for_line(old_start.row).len + { + first_line_is_new = true; + } - if let Some((before_edit, mode)) = autoindent_request { - let mut delta = 0isize; - let entries = edits - .into_iter() - .enumerate() - .zip(&edit_operation.as_edit().unwrap().new_text) - .map(|((ix, (range, _)), new_text)| { - let new_text_length = new_text.len(); - let old_start = range.start.to_point(&before_edit); - let new_start = (delta + range.start as isize) as usize; - delta += new_text_length as isize - (range.end as isize - range.start as isize); - - let mut range_of_insertion_to_indent = 0..new_text_length; - let mut first_line_is_new = false; - let mut original_indent_column = None; - - // When inserting an entire line at the beginning of an existing line, - // treat the insertion as new. - if new_text.contains('\n') - && old_start.column <= before_edit.indent_size_for_line(old_start.row).len - { - first_line_is_new = true; - } + // When inserting text starting with a newline, avoid auto-indenting the + // previous line. + if new_text.starts_with('\n') { + range_of_insertion_to_indent.start += 1; + first_line_is_new = true; + } - // When inserting text starting with a newline, avoid auto-indenting the - // previous line. - if new_text.starts_with('\n') { - range_of_insertion_to_indent.start += 1; - first_line_is_new = true; - } + // Avoid auto-indenting after the insertion. + if let AutoindentMode::Block { + original_indent_columns, + } = &mode + { + original_indent_column = Some( + original_indent_columns.get(ix).copied().unwrap_or_else(|| { + indent_size_for_text( + new_text[range_of_insertion_to_indent.clone()].chars(), + ) + .len + }), + ); + if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') { + range_of_insertion_to_indent.end -= 1; + } + } - // Avoid auto-indenting after the insertion. - if let AutoindentMode::Block { - original_indent_columns, - } = &mode - { - original_indent_column = - Some(original_indent_columns.get(ix).copied().unwrap_or_else(|| { - indent_size_for_text( - new_text[range_of_insertion_to_indent.clone()].chars(), - ) - .len - })); - if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') { - range_of_insertion_to_indent.end -= 1; + AutoindentRequestEntry { + first_line_is_new, + original_indent_column, + indent_size: before_edit.language_indent_size_at(range.start, cx), + range: this + .anchor_before(new_start + range_of_insertion_to_indent.start) + ..this.anchor_after(new_start + range_of_insertion_to_indent.end), } - } + }) + .collect(); - AutoindentRequestEntry { - first_line_is_new, - original_indent_column, - indent_size: before_edit.language_indent_size_at(range.start, cx), - range: self.anchor_before(new_start + range_of_insertion_to_indent.start) - ..self.anchor_after(new_start + range_of_insertion_to_indent.end), - } - }) - .collect(); + this.autoindent_requests.push(Arc::new(AutoindentRequest { + before_edit, + entries, + is_block_mode: matches!(mode, AutoindentMode::Block { .. }), + })); + } - self.autoindent_requests.push(Arc::new(AutoindentRequest { - before_edit, - entries, - is_block_mode: matches!(mode, AutoindentMode::Block { .. }), - })); + this.end_transaction(cx); + this.send_operation(Operation::Buffer(edit_operation), cx); + Some(edit_id) } - - self.end_transaction(cx); - self.send_operation(Operation::Buffer(edit_operation), cx); - Some(edit_id) + tail(self, edits, autoindent_mode, cx) } fn did_edit( diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 7d113a88af1de8e7b0b243bf54c15565de544730..0b49d92125c31a1c01f24d2dcac7f0b335213f05 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -2,6 +2,7 @@ mod buffer; mod diagnostic_set; mod highlight_map; pub mod language_settings; +pub mod markdown; mod outline; pub mod proto; mod syntax_map; @@ -110,7 +111,6 @@ pub struct LanguageServerName(pub Arc); pub struct CachedLspAdapter { pub name: LanguageServerName, pub short_name: &'static str, - pub initialization_options: Option, pub disk_based_diagnostic_sources: Vec, pub disk_based_diagnostics_progress_token: Option, pub language_ids: HashMap, @@ -121,7 +121,6 @@ impl CachedLspAdapter { pub async fn new(adapter: Arc) -> Arc { let name = adapter.name().await; let short_name = adapter.short_name(); - let initialization_options = adapter.initialization_options().await; let disk_based_diagnostic_sources = adapter.disk_based_diagnostic_sources().await; let disk_based_diagnostics_progress_token = adapter.disk_based_diagnostics_progress_token().await; @@ -130,7 +129,6 @@ impl CachedLspAdapter { Arc::new(CachedLspAdapter { name, short_name, - initialization_options, disk_based_diagnostic_sources, disk_based_diagnostics_progress_token, language_ids, @@ -227,6 +225,10 @@ impl CachedLspAdapter { ) -> Option { self.adapter.label_for_symbol(name, kind, language).await } + + pub fn enabled_formatters(&self) -> Vec { + self.adapter.enabled_formatters() + } } pub trait LspAdapterDelegate: Send + Sync { @@ -333,6 +335,33 @@ pub trait LspAdapter: 'static + Send + Sync { async fn language_ids(&self) -> HashMap { Default::default() } + + fn enabled_formatters(&self) -> Vec { + Vec::new() + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum BundledFormatter { + Prettier { + // See https://prettier.io/docs/en/options.html#parser for a list of valid values. + // Usually, every language has a single parser (standard or plugin-provided), hence `Some("parser_name")` can be used. + // There can not be multiple parsers for a single language, in case of a conflict, we would attempt to select the one with most plugins. + // + // But exceptions like Tailwind CSS exist, which uses standard parsers for CSS/JS/HTML/etc. but require an extra plugin to be installed. + // For those cases, `None` will install the plugin but apply other, regular parser defined for the language, and this would not be a conflict. + parser_name: Option<&'static str>, + plugin_names: Vec<&'static str>, + }, +} + +impl BundledFormatter { + pub fn prettier(parser_name: &'static str) -> Self { + Self::Prettier { + parser_name: Some(parser_name), + plugin_names: Vec::new(), + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -467,6 +496,7 @@ pub struct FakeLspAdapter { pub initializer: Option>, pub disk_based_diagnostics_progress_token: Option, pub disk_based_diagnostics_sources: Vec, + pub enabled_formatters: Vec, } #[derive(Clone, Debug, Default)] @@ -1729,6 +1759,7 @@ impl Default for FakeLspAdapter { disk_based_diagnostics_progress_token: None, initialization_options: None, disk_based_diagnostics_sources: Vec::new(), + enabled_formatters: Vec::new(), } } } @@ -1785,6 +1816,10 @@ impl LspAdapter for Arc { async fn initialization_options(&self) -> Option { self.initialization_options.clone() } + + fn enabled_formatters(&self) -> Vec { + self.enabled_formatters.clone() + } } fn get_capture_indices(query: &Query, captures: &mut [(&str, &mut Option)]) { diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index c3f706802a5c15446304e490d70cd14f6c7f2c86..9cac5c523ea64320381d7f60d1d144020ff2599a 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -50,6 +50,7 @@ pub struct LanguageSettings { pub remove_trailing_whitespace_on_save: bool, pub ensure_final_newline_on_save: bool, pub formatter: Formatter, + pub prettier: HashMap, pub enable_language_server: bool, pub show_copilot_suggestions: bool, pub show_whitespaces: ShowWhitespaceSetting, @@ -98,6 +99,8 @@ pub struct LanguageSettingsContent { #[serde(default)] pub formatter: Option, #[serde(default)] + pub prettier: Option>, + #[serde(default)] pub enable_language_server: Option, #[serde(default)] pub show_copilot_suggestions: Option, @@ -149,10 +152,13 @@ pub enum ShowWhitespaceSetting { All, } -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Formatter { + #[default] + Auto, LanguageServer, + Prettier, External { command: Arc, arguments: Arc<[String]>, @@ -392,6 +398,7 @@ fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent src.preferred_line_length, ); merge(&mut settings.formatter, src.formatter.clone()); + merge(&mut settings.prettier, src.prettier.clone()); merge(&mut settings.format_on_save, src.format_on_save.clone()); merge( &mut settings.remove_trailing_whitespace_on_save, diff --git a/crates/language/src/markdown.rs b/crates/language/src/markdown.rs new file mode 100644 index 0000000000000000000000000000000000000000..7f57eba3091b21faf482adc4eb2a595d9e855693 --- /dev/null +++ b/crates/language/src/markdown.rs @@ -0,0 +1,301 @@ +use std::sync::Arc; +use std::{ops::Range, path::PathBuf}; + +use crate::{HighlightId, Language, LanguageRegistry}; +use gpui::fonts::{self, HighlightStyle, Weight}; +use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; + +#[derive(Debug, Clone)] +pub struct ParsedMarkdown { + pub text: String, + pub highlights: Vec<(Range, MarkdownHighlight)>, + pub region_ranges: Vec>, + pub regions: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MarkdownHighlight { + Style(MarkdownHighlightStyle), + Code(HighlightId), +} + +impl MarkdownHighlight { + pub fn to_highlight_style(&self, theme: &theme::SyntaxTheme) -> Option { + match self { + MarkdownHighlight::Style(style) => { + let mut highlight = HighlightStyle::default(); + + if style.italic { + highlight.italic = Some(true); + } + + if style.underline { + highlight.underline = Some(fonts::Underline { + thickness: 1.0.into(), + ..Default::default() + }); + } + + if style.weight != fonts::Weight::default() { + highlight.weight = Some(style.weight); + } + + Some(highlight) + } + + MarkdownHighlight::Code(id) => id.style(theme), + } + } +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct MarkdownHighlightStyle { + pub italic: bool, + pub underline: bool, + pub weight: Weight, +} + +#[derive(Debug, Clone)] +pub struct ParsedRegion { + pub code: bool, + pub link: Option, +} + +#[derive(Debug, Clone)] +pub enum Link { + Web { url: String }, + Path { path: PathBuf }, +} + +impl Link { + fn identify(text: String) -> Option { + if text.starts_with("http") { + return Some(Link::Web { url: text }); + } + + let path = PathBuf::from(text); + if path.is_absolute() { + return Some(Link::Path { path }); + } + + None + } +} + +pub async fn parse_markdown( + markdown: &str, + language_registry: &Arc, + language: Option>, +) -> ParsedMarkdown { + let mut text = String::new(); + let mut highlights = Vec::new(); + let mut region_ranges = Vec::new(); + let mut regions = Vec::new(); + + parse_markdown_block( + markdown, + language_registry, + language, + &mut text, + &mut highlights, + &mut region_ranges, + &mut regions, + ) + .await; + + ParsedMarkdown { + text, + highlights, + region_ranges, + regions, + } +} + +pub async fn parse_markdown_block( + markdown: &str, + language_registry: &Arc, + language: Option>, + text: &mut String, + highlights: &mut Vec<(Range, MarkdownHighlight)>, + region_ranges: &mut Vec>, + regions: &mut Vec, +) { + let mut bold_depth = 0; + let mut italic_depth = 0; + let mut link_url = None; + let mut current_language = None; + let mut list_stack = Vec::new(); + + for event in Parser::new_ext(&markdown, Options::all()) { + let prev_len = text.len(); + match event { + Event::Text(t) => { + if let Some(language) = ¤t_language { + highlight_code(text, highlights, t.as_ref(), language); + } else { + text.push_str(t.as_ref()); + + let mut style = MarkdownHighlightStyle::default(); + + if bold_depth > 0 { + style.weight = Weight::BOLD; + } + + if italic_depth > 0 { + style.italic = true; + } + + if let Some(link) = link_url.clone().and_then(|u| Link::identify(u)) { + region_ranges.push(prev_len..text.len()); + regions.push(ParsedRegion { + code: false, + link: Some(link), + }); + style.underline = true; + } + + if style != MarkdownHighlightStyle::default() { + let mut new_highlight = true; + if let Some((last_range, MarkdownHighlight::Style(last_style))) = + highlights.last_mut() + { + if last_range.end == prev_len && last_style == &style { + last_range.end = text.len(); + new_highlight = false; + } + } + if new_highlight { + let range = prev_len..text.len(); + highlights.push((range, MarkdownHighlight::Style(style))); + } + } + } + } + + Event::Code(t) => { + text.push_str(t.as_ref()); + region_ranges.push(prev_len..text.len()); + + let link = link_url.clone().and_then(|u| Link::identify(u)); + if link.is_some() { + highlights.push(( + prev_len..text.len(), + MarkdownHighlight::Style(MarkdownHighlightStyle { + underline: true, + ..Default::default() + }), + )); + } + regions.push(ParsedRegion { code: true, link }); + } + + Event::Start(tag) => match tag { + Tag::Paragraph => new_paragraph(text, &mut list_stack), + + Tag::Heading(_, _, _) => { + new_paragraph(text, &mut list_stack); + bold_depth += 1; + } + + Tag::CodeBlock(kind) => { + new_paragraph(text, &mut list_stack); + current_language = if let CodeBlockKind::Fenced(language) = kind { + language_registry + .language_for_name(language.as_ref()) + .await + .ok() + } else { + language.clone() + } + } + + Tag::Emphasis => italic_depth += 1, + + Tag::Strong => bold_depth += 1, + + Tag::Link(_, url, _) => link_url = Some(url.to_string()), + + Tag::List(number) => { + list_stack.push((number, false)); + } + + Tag::Item => { + let len = list_stack.len(); + if let Some((list_number, has_content)) = list_stack.last_mut() { + *has_content = false; + if !text.is_empty() && !text.ends_with('\n') { + text.push('\n'); + } + for _ in 0..len - 1 { + text.push_str(" "); + } + if let Some(number) = list_number { + text.push_str(&format!("{}. ", number)); + *number += 1; + *has_content = false; + } else { + text.push_str("- "); + } + } + } + + _ => {} + }, + + Event::End(tag) => match tag { + Tag::Heading(_, _, _) => bold_depth -= 1, + Tag::CodeBlock(_) => current_language = None, + Tag::Emphasis => italic_depth -= 1, + Tag::Strong => bold_depth -= 1, + Tag::Link(_, _, _) => link_url = None, + Tag::List(_) => drop(list_stack.pop()), + _ => {} + }, + + Event::HardBreak => text.push('\n'), + + Event::SoftBreak => text.push(' '), + + _ => {} + } + } +} + +pub fn highlight_code( + text: &mut String, + highlights: &mut Vec<(Range, MarkdownHighlight)>, + content: &str, + language: &Arc, +) { + let prev_len = text.len(); + text.push_str(content); + for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) { + let highlight = MarkdownHighlight::Code(highlight_id); + highlights.push((prev_len + range.start..prev_len + range.end, highlight)); + } +} + +pub fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option, bool)>) { + let mut is_subsequent_paragraph_of_list = false; + if let Some((_, has_content)) = list_stack.last_mut() { + if *has_content { + is_subsequent_paragraph_of_list = true; + } else { + *has_content = true; + return; + } + } + + if !text.is_empty() { + if !text.ends_with('\n') { + text.push('\n'); + } + text.push('\n'); + } + for _ in 0..list_stack.len().saturating_sub(1) { + text.push_str(" "); + } + if is_subsequent_paragraph_of_list { + text.push_str(" "); + } +} diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index c4abe39d4782aafbe90594e3a0bc5de70787fa03..957f4ee7fbc4da251698fd640d33d37d5cf4a06b 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -482,6 +482,7 @@ pub async fn deserialize_completion( lsp_completion.filter_text.as_deref(), ) }), + documentation: None, server_id: LanguageServerId(completion.server_id as usize), lsp_completion, }) diff --git a/crates/language_tools/src/lsp_log.rs b/crates/language_tools/src/lsp_log.rs index faed37a97cfd5bf9d3a15c218b82c9f2be960ef4..383ca94851409cfd28530c62e3d786e1b1c72061 100644 --- a/crates/language_tools/src/lsp_log.rs +++ b/crates/language_tools/src/lsp_log.rs @@ -685,6 +685,7 @@ impl View for LspLogToolbarItemView { }); let server_selected = current_server.is_some(); + enum LspLogScroll {} enum Menu {} let lsp_menu = Stack::new() .with_child(Self::render_language_server_menu_header( @@ -697,7 +698,7 @@ impl View for LspLogToolbarItemView { Overlay::new( MouseEventHandler::new::(0, cx, move |_, cx| { Flex::column() - .scrollable::(0, None, cx) + .scrollable::(0, None, cx) .with_children(menu_rows.into_iter().map(|row| { Self::render_language_server_menu_item( row.server_id, @@ -876,6 +877,7 @@ impl LspLogToolbarItemView { ) -> impl Element { enum ActivateLog {} enum ActivateRpcTrace {} + enum LanguageServerCheckbox {} Flex::column() .with_child({ @@ -921,7 +923,7 @@ impl LspLogToolbarItemView { .with_height(theme.toolbar_dropdown_menu.row_height), ) .with_child( - ui::checkbox_with_label::( + ui::checkbox_with_label::( Empty::new(), &theme.welcome.checkbox, rpc_trace_enabled, diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 33581721ae8fff8bc32f085ad3b7359209ee6cf2..b4099e2f6ea629e13343f0a022bf45cfd0712083 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -466,7 +466,10 @@ impl LanguageServer { completion_item: Some(CompletionItemCapability { snippet_support: Some(true), resolve_support: Some(CompletionItemCapabilityResolveSupport { - properties: vec!["additionalTextEdits".to_string()], + properties: vec![ + "documentation".to_string(), + "additionalTextEdits".to_string(), + ], }), ..Default::default() }), @@ -748,6 +751,15 @@ impl LanguageServer { ) } + // some child of string literal (be it "" or ``) which is the child of an attribute + + // + // + // + // + // const classes = "awesome "; + // + fn request_internal( next_id: &AtomicUsize, response_handlers: &Mutex>>, diff --git a/crates/node_runtime/src/node_runtime.rs b/crates/node_runtime/src/node_runtime.rs index 820a8b6f818f3a2aac1bbf451d566e778fc1d5a4..dcb8833f8c65a364f5ed4240846f60897e1115c2 100644 --- a/crates/node_runtime/src/node_runtime.rs +++ b/crates/node_runtime/src/node_runtime.rs @@ -220,29 +220,129 @@ impl NodeRuntime for RealNodeRuntime { } } -pub struct FakeNodeRuntime; +pub struct FakeNodeRuntime(Option); + +struct PrettierSupport { + plugins: Vec<&'static str>, +} impl FakeNodeRuntime { pub fn new() -> Arc { - Arc::new(FakeNodeRuntime) + Arc::new(FakeNodeRuntime(None)) + } + + pub fn with_prettier_support(plugins: &[&'static str]) -> Arc { + Arc::new(FakeNodeRuntime(Some(PrettierSupport::new(plugins)))) } } #[async_trait::async_trait] impl NodeRuntime for FakeNodeRuntime { - async fn binary_path(&self) -> Result { - unreachable!() + async fn binary_path(&self) -> anyhow::Result { + if let Some(prettier_support) = &self.0 { + prettier_support.binary_path().await + } else { + unreachable!() + } + } + + async fn run_npm_subcommand( + &self, + directory: Option<&Path>, + subcommand: &str, + args: &[&str], + ) -> anyhow::Result { + if let Some(prettier_support) = &self.0 { + prettier_support + .run_npm_subcommand(directory, subcommand, args) + .await + } else { + unreachable!() + } + } + + async fn npm_package_latest_version(&self, name: &str) -> anyhow::Result { + if let Some(prettier_support) = &self.0 { + prettier_support.npm_package_latest_version(name).await + } else { + unreachable!() + } + } + + async fn npm_install_packages( + &self, + directory: &Path, + packages: &[(&str, &str)], + ) -> anyhow::Result<()> { + if let Some(prettier_support) = &self.0 { + prettier_support + .npm_install_packages(directory, packages) + .await + } else { + unreachable!() + } + } +} + +impl PrettierSupport { + const PACKAGE_VERSION: &str = "0.0.1"; + + fn new(plugins: &[&'static str]) -> Self { + Self { + plugins: plugins.to_vec(), + } + } +} + +#[async_trait::async_trait] +impl NodeRuntime for PrettierSupport { + async fn binary_path(&self) -> anyhow::Result { + Ok(PathBuf::from("prettier_fake_node")) } async fn run_npm_subcommand(&self, _: Option<&Path>, _: &str, _: &[&str]) -> Result { unreachable!() } - async fn npm_package_latest_version(&self, _: &str) -> Result { - unreachable!() + async fn npm_package_latest_version(&self, name: &str) -> anyhow::Result { + if name == "prettier" || self.plugins.contains(&name) { + Ok(Self::PACKAGE_VERSION.to_string()) + } else { + panic!("Unexpected package name: {name}") + } } - async fn npm_install_packages(&self, _: &Path, _: &[(&str, &str)]) -> Result<()> { - unreachable!() + async fn npm_install_packages( + &self, + _: &Path, + packages: &[(&str, &str)], + ) -> anyhow::Result<()> { + assert_eq!( + packages.len(), + self.plugins.len() + 1, + "Unexpected packages length to install: {:?}, expected `prettier` + {:?}", + packages, + self.plugins + ); + for (name, version) in packages { + assert!( + name == &"prettier" || self.plugins.contains(name), + "Unexpected package `{}` to install in packages {:?}, expected {} for `prettier` + {:?}", + name, + packages, + Self::PACKAGE_VERSION, + self.plugins + ); + assert_eq!( + version, + &Self::PACKAGE_VERSION, + "Unexpected package version `{}` to install in packages {:?}, expected {} for `prettier` + {:?}", + version, + packages, + Self::PACKAGE_VERSION, + self.plugins + ); + } + Ok(()) } } diff --git a/crates/prettier/Cargo.toml b/crates/prettier/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..997fa87126502575b83dcbd18d15c355f68999f3 --- /dev/null +++ b/crates/prettier/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "prettier" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/prettier.rs" +doctest = false + +[features] +test-support = [] + +[dependencies] +client = { path = "../client" } +collections = { path = "../collections"} +language = { path = "../language" } +gpui = { path = "../gpui" } +fs = { path = "../fs" } +lsp = { path = "../lsp" } +node_runtime = { path = "../node_runtime"} +util = { path = "../util" } + +log.workspace = true +serde.workspace = true +serde_derive.workspace = true +serde_json.workspace = true +anyhow.workspace = true +futures.workspace = true + +[dev-dependencies] +language = { path = "../language", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs new file mode 100644 index 0000000000000000000000000000000000000000..c3811b567b590c632fd3f33ccf0df3c7b9d1b0f3 --- /dev/null +++ b/crates/prettier/src/prettier.rs @@ -0,0 +1,513 @@ +use std::collections::VecDeque; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use anyhow::Context; +use collections::{HashMap, HashSet}; +use fs::Fs; +use gpui::{AsyncAppContext, ModelHandle}; +use language::language_settings::language_settings; +use language::{Buffer, BundledFormatter, Diff}; +use lsp::{LanguageServer, LanguageServerId}; +use node_runtime::NodeRuntime; +use serde::{Deserialize, Serialize}; +use util::paths::DEFAULT_PRETTIER_DIR; + +pub enum Prettier { + Real(RealPrettier), + #[cfg(any(test, feature = "test-support"))] + Test(TestPrettier), +} + +pub struct RealPrettier { + worktree_id: Option, + default: bool, + prettier_dir: PathBuf, + server: Arc, +} + +#[cfg(any(test, feature = "test-support"))] +pub struct TestPrettier { + worktree_id: Option, + prettier_dir: PathBuf, + default: bool, +} + +#[derive(Debug)] +pub struct LocateStart { + pub worktree_root_path: Arc, + pub starting_path: Arc, +} + +pub const PRETTIER_SERVER_FILE: &str = "prettier_server.js"; +pub const PRETTIER_SERVER_JS: &str = include_str!("./prettier_server.js"); +const PRETTIER_PACKAGE_NAME: &str = "prettier"; +const TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME: &str = "prettier-plugin-tailwindcss"; + +impl Prettier { + pub const CONFIG_FILE_NAMES: &'static [&'static str] = &[ + ".prettierrc", + ".prettierrc.json", + ".prettierrc.json5", + ".prettierrc.yaml", + ".prettierrc.yml", + ".prettierrc.toml", + ".prettierrc.js", + ".prettierrc.cjs", + "package.json", + "prettier.config.js", + "prettier.config.cjs", + ".editorconfig", + ]; + + #[cfg(any(test, feature = "test-support"))] + pub const FORMAT_SUFFIX: &str = "\nformatted by test prettier"; + + pub async fn locate( + starting_path: Option, + fs: Arc, + ) -> anyhow::Result { + let paths_to_check = match starting_path.as_ref() { + Some(starting_path) => { + let worktree_root = starting_path + .worktree_root_path + .components() + .into_iter() + .take_while(|path_component| { + path_component.as_os_str().to_string_lossy() != "node_modules" + }) + .collect::(); + + if worktree_root != starting_path.worktree_root_path.as_ref() { + vec![worktree_root] + } else { + let (worktree_root_metadata, start_path_metadata) = if starting_path + .starting_path + .as_ref() + == Path::new("") + { + let worktree_root_data = + fs.metadata(&worktree_root).await.with_context(|| { + format!( + "FS metadata fetch for worktree root path {worktree_root:?}", + ) + })?; + (worktree_root_data.unwrap_or_else(|| { + panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}") + }), None) + } else { + let full_starting_path = worktree_root.join(&starting_path.starting_path); + let (worktree_root_data, start_path_data) = futures::try_join!( + fs.metadata(&worktree_root), + fs.metadata(&full_starting_path), + ) + .with_context(|| { + format!("FS metadata fetch for starting path {full_starting_path:?}",) + })?; + ( + worktree_root_data.unwrap_or_else(|| { + panic!("cannot query prettier for non existing worktree root at {worktree_root_data:?}") + }), + start_path_data, + ) + }; + + match start_path_metadata { + Some(start_path_metadata) => { + anyhow::ensure!(worktree_root_metadata.is_dir, + "For non-empty start path, worktree root {starting_path:?} should be a directory"); + anyhow::ensure!( + !start_path_metadata.is_dir, + "For non-empty start path, it should not be a directory {starting_path:?}" + ); + anyhow::ensure!( + !start_path_metadata.is_symlink, + "For non-empty start path, it should not be a symlink {starting_path:?}" + ); + + let file_to_format = starting_path.starting_path.as_ref(); + let mut paths_to_check = VecDeque::from(vec![worktree_root.clone()]); + let mut current_path = worktree_root; + for path_component in file_to_format.components().into_iter() { + current_path = current_path.join(path_component); + paths_to_check.push_front(current_path.clone()); + if path_component.as_os_str().to_string_lossy() == "node_modules" { + break; + } + } + paths_to_check.pop_front(); // last one is the file itself or node_modules, skip it + Vec::from(paths_to_check) + } + None => { + anyhow::ensure!( + !worktree_root_metadata.is_dir, + "For empty start path, worktree root should not be a directory {starting_path:?}" + ); + anyhow::ensure!( + !worktree_root_metadata.is_symlink, + "For empty start path, worktree root should not be a symlink {starting_path:?}" + ); + worktree_root + .parent() + .map(|path| vec![path.to_path_buf()]) + .unwrap_or_default() + } + } + } + } + None => Vec::new(), + }; + + match find_closest_prettier_dir(paths_to_check, fs.as_ref()) + .await + .with_context(|| format!("finding prettier starting with {starting_path:?}"))? + { + Some(prettier_dir) => Ok(prettier_dir), + None => Ok(DEFAULT_PRETTIER_DIR.to_path_buf()), + } + } + + #[cfg(any(test, feature = "test-support"))] + pub async fn start( + worktree_id: Option, + _: LanguageServerId, + prettier_dir: PathBuf, + _: Arc, + _: AsyncAppContext, + ) -> anyhow::Result { + Ok( + #[cfg(any(test, feature = "test-support"))] + Self::Test(TestPrettier { + worktree_id, + default: prettier_dir == DEFAULT_PRETTIER_DIR.as_path(), + prettier_dir, + }), + ) + } + + #[cfg(not(any(test, feature = "test-support")))] + pub async fn start( + worktree_id: Option, + server_id: LanguageServerId, + prettier_dir: PathBuf, + node: Arc, + cx: AsyncAppContext, + ) -> anyhow::Result { + use lsp::LanguageServerBinary; + + let backgroud = cx.background(); + anyhow::ensure!( + prettier_dir.is_dir(), + "Prettier dir {prettier_dir:?} is not a directory" + ); + let prettier_server = DEFAULT_PRETTIER_DIR.join(PRETTIER_SERVER_FILE); + anyhow::ensure!( + prettier_server.is_file(), + "no prettier server package found at {prettier_server:?}" + ); + + let node_path = backgroud + .spawn(async move { node.binary_path().await }) + .await?; + let server = LanguageServer::new( + server_id, + LanguageServerBinary { + path: node_path, + arguments: vec![prettier_server.into(), prettier_dir.as_path().into()], + }, + Path::new("/"), + None, + cx, + ) + .context("prettier server creation")?; + let server = backgroud + .spawn(server.initialize(None)) + .await + .context("prettier server initialization")?; + Ok(Self::Real(RealPrettier { + worktree_id, + server, + default: prettier_dir == DEFAULT_PRETTIER_DIR.as_path(), + prettier_dir, + })) + } + + pub async fn format( + &self, + buffer: &ModelHandle, + buffer_path: Option, + cx: &AsyncAppContext, + ) -> anyhow::Result { + match self { + Self::Real(local) => { + let params = buffer.read_with(cx, |buffer, cx| { + let buffer_language = buffer.language(); + let parsers_with_plugins = buffer_language + .into_iter() + .flat_map(|language| { + language + .lsp_adapters() + .iter() + .flat_map(|adapter| adapter.enabled_formatters()) + .filter_map(|formatter| match formatter { + BundledFormatter::Prettier { + parser_name, + plugin_names, + } => Some((parser_name, plugin_names)), + }) + }) + .fold( + HashMap::default(), + |mut parsers_with_plugins, (parser_name, plugins)| { + match parser_name { + Some(parser_name) => parsers_with_plugins + .entry(parser_name) + .or_insert_with(HashSet::default) + .extend(plugins), + None => parsers_with_plugins.values_mut().for_each(|existing_plugins| { + existing_plugins.extend(plugins.iter()); + }), + } + parsers_with_plugins + }, + ); + + let selected_parser_with_plugins = parsers_with_plugins.iter().max_by_key(|(_, plugins)| plugins.len()); + if parsers_with_plugins.len() > 1 { + log::warn!("Found multiple parsers with plugins {parsers_with_plugins:?}, will select only one: {selected_parser_with_plugins:?}"); + } + + let prettier_node_modules = self.prettier_dir().join("node_modules"); + anyhow::ensure!(prettier_node_modules.is_dir(), "Prettier node_modules dir does not exist: {prettier_node_modules:?}"); + let plugin_name_into_path = |plugin_name: &str| { + let prettier_plugin_dir = prettier_node_modules.join(plugin_name); + for possible_plugin_path in [ + prettier_plugin_dir.join("dist").join("index.mjs"), + prettier_plugin_dir.join("dist").join("index.js"), + prettier_plugin_dir.join("dist").join("plugin.js"), + prettier_plugin_dir.join("index.mjs"), + prettier_plugin_dir.join("index.js"), + prettier_plugin_dir.join("plugin.js"), + prettier_plugin_dir, + ] { + if possible_plugin_path.is_file() { + return Some(possible_plugin_path); + } + } + None + }; + let (parser, located_plugins) = match selected_parser_with_plugins { + Some((parser, plugins)) => { + // Tailwind plugin requires being added last + // https://github.com/tailwindlabs/prettier-plugin-tailwindcss#compatibility-with-other-prettier-plugins + let mut add_tailwind_back = false; + + let mut plugins = plugins.into_iter().filter(|&&plugin_name| { + if plugin_name == TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME { + add_tailwind_back = true; + false + } else { + true + } + }).map(|plugin_name| (plugin_name, plugin_name_into_path(plugin_name))).collect::>(); + if add_tailwind_back { + plugins.push((&TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME, plugin_name_into_path(TAILWIND_PRETTIER_PLUGIN_PACKAGE_NAME))); + } + (Some(parser.to_string()), plugins) + }, + None => (None, Vec::new()), + }; + + let prettier_options = if self.is_default() { + let language_settings = language_settings(buffer_language, buffer.file(), cx); + let mut options = language_settings.prettier.clone(); + if !options.contains_key("tabWidth") { + options.insert( + "tabWidth".to_string(), + serde_json::Value::Number(serde_json::Number::from( + language_settings.tab_size.get(), + )), + ); + } + if !options.contains_key("printWidth") { + options.insert( + "printWidth".to_string(), + serde_json::Value::Number(serde_json::Number::from( + language_settings.preferred_line_length, + )), + ); + } + Some(options) + } else { + None + }; + + let plugins = located_plugins.into_iter().filter_map(|(plugin_name, located_plugin_path)| { + match located_plugin_path { + Some(path) => Some(path), + None => { + log::error!("Have not found plugin path for {plugin_name:?} inside {prettier_node_modules:?}"); + None}, + } + }).collect(); + log::debug!("Formatting file {:?} with prettier, plugins :{plugins:?}, options: {prettier_options:?}", buffer.file().map(|f| f.full_path(cx))); + + anyhow::Ok(FormatParams { + text: buffer.text(), + options: FormatOptions { + parser, + plugins, + path: buffer_path, + prettier_options, + }, + }) + }).context("prettier params calculation")?; + let response = local + .server + .request::(params) + .await + .context("prettier format request")?; + let diff_task = buffer.read_with(cx, |buffer, cx| buffer.diff(response.text, cx)); + Ok(diff_task.await) + } + #[cfg(any(test, feature = "test-support"))] + Self::Test(_) => Ok(buffer + .read_with(cx, |buffer, cx| { + let formatted_text = buffer.text() + Self::FORMAT_SUFFIX; + buffer.diff(formatted_text, cx) + }) + .await), + } + } + + pub async fn clear_cache(&self) -> anyhow::Result<()> { + match self { + Self::Real(local) => local + .server + .request::(()) + .await + .context("prettier clear cache"), + #[cfg(any(test, feature = "test-support"))] + Self::Test(_) => Ok(()), + } + } + + pub fn server(&self) -> Option<&Arc> { + match self { + Self::Real(local) => Some(&local.server), + #[cfg(any(test, feature = "test-support"))] + Self::Test(_) => None, + } + } + + pub fn is_default(&self) -> bool { + match self { + Self::Real(local) => local.default, + #[cfg(any(test, feature = "test-support"))] + Self::Test(test_prettier) => test_prettier.default, + } + } + + pub fn prettier_dir(&self) -> &Path { + match self { + Self::Real(local) => &local.prettier_dir, + #[cfg(any(test, feature = "test-support"))] + Self::Test(test_prettier) => &test_prettier.prettier_dir, + } + } + + pub fn worktree_id(&self) -> Option { + match self { + Self::Real(local) => local.worktree_id, + #[cfg(any(test, feature = "test-support"))] + Self::Test(test_prettier) => test_prettier.worktree_id, + } + } +} + +async fn find_closest_prettier_dir( + paths_to_check: Vec, + fs: &dyn Fs, +) -> anyhow::Result> { + for path in paths_to_check { + let possible_package_json = path.join("package.json"); + if let Some(package_json_metadata) = fs + .metadata(&possible_package_json) + .await + .with_context(|| format!("Fetching metadata for {possible_package_json:?}"))? + { + if !package_json_metadata.is_dir && !package_json_metadata.is_symlink { + let package_json_contents = fs + .load(&possible_package_json) + .await + .with_context(|| format!("reading {possible_package_json:?} file contents"))?; + if let Ok(json_contents) = serde_json::from_str::>( + &package_json_contents, + ) { + if let Some(serde_json::Value::Object(o)) = json_contents.get("dependencies") { + if o.contains_key(PRETTIER_PACKAGE_NAME) { + return Ok(Some(path)); + } + } + if let Some(serde_json::Value::Object(o)) = json_contents.get("devDependencies") + { + if o.contains_key(PRETTIER_PACKAGE_NAME) { + return Ok(Some(path)); + } + } + } + } + } + + let possible_node_modules_location = path.join("node_modules").join(PRETTIER_PACKAGE_NAME); + if let Some(node_modules_location_metadata) = fs + .metadata(&possible_node_modules_location) + .await + .with_context(|| format!("fetching metadata for {possible_node_modules_location:?}"))? + { + if node_modules_location_metadata.is_dir { + return Ok(Some(path)); + } + } + } + Ok(None) +} + +enum Format {} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct FormatParams { + text: String, + options: FormatOptions, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct FormatOptions { + plugins: Vec, + parser: Option, + #[serde(rename = "filepath")] + path: Option, + prettier_options: Option>, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct FormatResult { + text: String, +} + +impl lsp::request::Request for Format { + type Params = FormatParams; + type Result = FormatResult; + const METHOD: &'static str = "prettier/format"; +} + +enum ClearCache {} + +impl lsp::request::Request for ClearCache { + type Params = (); + type Result = (); + const METHOD: &'static str = "prettier/clear_cache"; +} diff --git a/crates/prettier/src/prettier_server.js b/crates/prettier/src/prettier_server.js new file mode 100644 index 0000000000000000000000000000000000000000..a56c220f208607d00d1ef413c8529dd4244008b4 --- /dev/null +++ b/crates/prettier/src/prettier_server.js @@ -0,0 +1,217 @@ +const { Buffer } = require('buffer'); +const fs = require("fs"); +const path = require("path"); +const { once } = require('events'); + +const prettierContainerPath = process.argv[2]; +if (prettierContainerPath == null || prettierContainerPath.length == 0) { + process.stderr.write(`Prettier path argument was not specified or empty.\nUsage: ${process.argv[0]} ${process.argv[1]} prettier/path\n`); + process.exit(1); +} +fs.stat(prettierContainerPath, (err, stats) => { + if (err) { + process.stderr.write(`Path '${prettierContainerPath}' does not exist\n`); + process.exit(1); + } + + if (!stats.isDirectory()) { + process.stderr.write(`Path '${prettierContainerPath}' exists but is not a directory\n`); + process.exit(1); + } +}); +const prettierPath = path.join(prettierContainerPath, 'node_modules/prettier'); + +class Prettier { + constructor(path, prettier, config) { + this.path = path; + this.prettier = prettier; + this.config = config; + } +} + +(async () => { + let prettier; + let config; + try { + prettier = await loadPrettier(prettierPath); + config = await prettier.resolveConfig(prettierPath) || {}; + } catch (e) { + process.stderr.write(`Failed to load prettier: ${e}\n`); + process.exit(1); + } + process.stderr.write(`Prettier at path '${prettierPath}' loaded successfully, config: ${JSON.stringify(config)}\n`); + process.stdin.resume(); + handleBuffer(new Prettier(prettierPath, prettier, config)); +})() + +async function handleBuffer(prettier) { + for await (const messageText of readStdin()) { + let message; + try { + message = JSON.parse(messageText); + } catch (e) { + sendResponse(makeError(`Failed to parse message '${messageText}': ${e}`)); + continue; + } + // allow concurrent request handling by not `await`ing the message handling promise (async function) + handleMessage(message, prettier).catch(e => { + sendResponse({ id: message.id, ...makeError(`error during message handling: ${e}`) }); + }); + } +} + +const headerSeparator = "\r\n"; +const contentLengthHeaderName = 'Content-Length'; + +async function* readStdin() { + let buffer = Buffer.alloc(0); + let streamEnded = false; + process.stdin.on('end', () => { + streamEnded = true; + }); + process.stdin.on('data', (data) => { + buffer = Buffer.concat([buffer, data]); + }); + + async function handleStreamEnded(errorMessage) { + sendResponse(makeError(errorMessage)); + buffer = Buffer.alloc(0); + messageLength = null; + await once(process.stdin, 'readable'); + streamEnded = false; + } + + try { + let headersLength = null; + let messageLength = null; + main_loop: while (true) { + if (messageLength === null) { + while (buffer.indexOf(`${headerSeparator}${headerSeparator}`) === -1) { + if (streamEnded) { + await handleStreamEnded('Unexpected end of stream: headers not found'); + continue main_loop; + } else if (buffer.length > contentLengthHeaderName.length * 10) { + await handleStreamEnded(`Unexpected stream of bytes: no headers end found after ${buffer.length} bytes of input`); + continue main_loop; + } + await once(process.stdin, 'readable'); + } + const headers = buffer.subarray(0, buffer.indexOf(`${headerSeparator}${headerSeparator}`)).toString('ascii'); + const contentLengthHeader = headers.split(headerSeparator) + .map(header => header.split(':')) + .filter(header => header[2] === undefined) + .filter(header => (header[1] || '').length > 0) + .find(header => (header[0] || '').trim() === contentLengthHeaderName); + const contentLength = (contentLengthHeader || [])[1]; + if (contentLength === undefined) { + await handleStreamEnded(`Missing or incorrect ${contentLengthHeaderName} header: ${headers}`); + continue main_loop; + } + headersLength = headers.length + headerSeparator.length * 2; + messageLength = parseInt(contentLength, 10); + } + + while (buffer.length < (headersLength + messageLength)) { + if (streamEnded) { + await handleStreamEnded( + `Unexpected end of stream: buffer length ${buffer.length} does not match expected header length ${headersLength} + body length ${messageLength}`); + continue main_loop; + } + await once(process.stdin, 'readable'); + } + + const messageEnd = headersLength + messageLength; + const message = buffer.subarray(headersLength, messageEnd); + buffer = buffer.subarray(messageEnd); + headersLength = null; + messageLength = null; + yield message.toString('utf8'); + } + } catch (e) { + sendResponse(makeError(`Error reading stdin: ${e}`)); + } finally { + process.stdin.off('data', () => { }); + } +} + +async function handleMessage(message, prettier) { + const { method, id, params } = message; + if (method === undefined) { + throw new Error(`Message method is undefined: ${JSON.stringify(message)}`); + } + if (id === undefined) { + throw new Error(`Message id is undefined: ${JSON.stringify(message)}`); + } + + if (method === 'prettier/format') { + if (params === undefined || params.text === undefined) { + throw new Error(`Message params.text is undefined: ${JSON.stringify(message)}`); + } + if (params.options === undefined) { + throw new Error(`Message params.options is undefined: ${JSON.stringify(message)}`); + } + + let resolvedConfig = {}; + if (params.options.filepath !== undefined) { + resolvedConfig = await prettier.prettier.resolveConfig(params.options.filepath) || {}; + } + + const options = { + ...(params.options.prettierOptions || prettier.config), + ...resolvedConfig, + parser: params.options.parser, + plugins: params.options.plugins, + path: params.options.filepath + }; + process.stderr.write(`Resolved config: ${JSON.stringify(resolvedConfig)}, will format file '${params.options.filepath || ''}' with options: ${JSON.stringify(options)}\n`); + const formattedText = await prettier.prettier.format(params.text, options); + sendResponse({ id, result: { text: formattedText } }); + } else if (method === 'prettier/clear_cache') { + prettier.prettier.clearConfigCache(); + prettier.config = await prettier.prettier.resolveConfig(prettier.path) || {}; + sendResponse({ id, result: null }); + } else if (method === 'initialize') { + sendResponse({ + id, + result: { + "capabilities": {} + } + }); + } else { + throw new Error(`Unknown method: ${method}`); + } +} + +function makeError(message) { + return { + error: { + "code": -32600, // invalid request code + message, + } + }; +} + +function sendResponse(response) { + const responsePayloadString = JSON.stringify({ + jsonrpc: "2.0", + ...response + }); + const headers = `${contentLengthHeaderName}: ${Buffer.byteLength(responsePayloadString)}${headerSeparator}${headerSeparator}`; + process.stdout.write(headers + responsePayloadString); +} + +function loadPrettier(prettierPath) { + return new Promise((resolve, reject) => { + fs.access(prettierPath, fs.constants.F_OK, (err) => { + if (err) { + reject(`Path '${prettierPath}' does not exist.Error: ${err}`); + } else { + try { + resolve(require(prettierPath)); + } catch (err) { + reject(`Error requiring prettier module from path '${prettierPath}'.Error: ${err}`); + } + } + }); + }); +} diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index ffea6646e9439a24cd7e09fd9666ee9eabfcbf49..cfa623d53444c06fb981778e075d4fe45a3462cd 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -15,6 +15,7 @@ test-support = [ "language/test-support", "settings/test-support", "text/test-support", + "prettier/test-support", ] [dependencies] @@ -31,6 +32,8 @@ git = { path = "../git" } gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } +node_runtime = { path = "../node_runtime" } +prettier = { path = "../prettier" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } @@ -73,6 +76,7 @@ gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"] } +prettier = { path = "../prettier", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } rpc = { path = "../rpc", features = ["test-support"] } git2.workspace = true diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 8beaea5031c1e663909a1fd5a627c90b1c84a367..72d79ca97992c47384f004c6d1f25bb7f59f22a1 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -10,7 +10,7 @@ use futures::future; use gpui::{AppContext, AsyncAppContext, ModelHandle}; use language::{ language_settings::{language_settings, InlayHintKind}, - point_from_lsp, point_to_lsp, + point_from_lsp, point_to_lsp, prepare_completion_documentation, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, @@ -1341,7 +1341,7 @@ impl LspCommand for GetCompletions { async fn response_from_lsp( self, completions: Option, - _: ModelHandle, + project: ModelHandle, buffer: ModelHandle, server_id: LanguageServerId, cx: AsyncAppContext, @@ -1358,10 +1358,11 @@ impl LspCommand for GetCompletions { } } } else { - Default::default() + Vec::new() }; - let completions = buffer.read_with(&cx, |buffer, _| { + let completions = buffer.read_with(&cx, |buffer, cx| { + let language_registry = project.read(cx).languages().clone(); let language = buffer.language().cloned(); let snapshot = buffer.snapshot(); let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left); @@ -1370,6 +1371,14 @@ impl LspCommand for GetCompletions { completions .into_iter() .filter_map(move |mut lsp_completion| { + if let Some(response_list) = &response_list { + if let Some(item_defaults) = &response_list.item_defaults { + if let Some(data) = &item_defaults.data { + lsp_completion.data = Some(data.clone()); + } + } + } + let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() { // If the language server provides a range to overwrite, then // check that the range is valid. @@ -1445,14 +1454,30 @@ impl LspCommand for GetCompletions { } }; - let language = language.clone(); LineEnding::normalize(&mut new_text); + let language_registry = language_registry.clone(); + let language = language.clone(); + Some(async move { let mut label = None; - if let Some(language) = language { + if let Some(language) = language.as_ref() { language.process_completion(&mut lsp_completion).await; label = language.label_for_completion(&lsp_completion).await; } + + let documentation = if let Some(lsp_docs) = &lsp_completion.documentation { + Some( + prepare_completion_documentation( + lsp_docs, + &language_registry, + language.clone(), + ) + .await, + ) + } else { + None + }; + Completion { old_range, new_text, @@ -1462,6 +1487,7 @@ impl LspCommand for GetCompletions { lsp_completion.filter_text.as_deref(), ) }), + documentation, server_id, lsp_completion, } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a50e02a631ccda81f1d40c49cc3361c1626a383d..e91c91f7c3403f01716c4ea2e855460a9e9bc470 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -20,7 +20,7 @@ use futures::{ mpsc::{self, UnboundedReceiver}, oneshot, }, - future::{try_join_all, Shared}, + future::{self, try_join_all, Shared}, stream::FuturesUnordered, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt, }; @@ -31,17 +31,19 @@ use gpui::{ }; use itertools::Itertools; use language::{ - language_settings::{language_settings, FormatOnSave, Formatter, InlayHintKind}, + language_settings::{ + language_settings, FormatOnSave, Formatter, InlayHintKind, LanguageSettings, + }, point_to_lsp, proto::{ deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version, serialize_anchor, serialize_version, split_operations, }, - range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CodeAction, - CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, Event as BufferEvent, - File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, LspAdapterDelegate, - OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, TextBufferSnapshot, - ToOffset, ToPointUtf16, Transaction, Unclipped, + range_from_lsp, range_to_lsp, Bias, Buffer, BufferSnapshot, BundledFormatter, CachedLspAdapter, + CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Diff, + Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, + LspAdapterDelegate, OffsetRangeExt, Operation, Patch, PendingLanguageServer, PointUtf16, + TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped, }; use log::error; use lsp::{ @@ -49,7 +51,9 @@ use lsp::{ DocumentHighlightKind, LanguageServer, LanguageServerBinary, LanguageServerId, OneOf, }; use lsp_command::*; +use node_runtime::NodeRuntime; use postage::watch; +use prettier::{LocateStart, Prettier, PRETTIER_SERVER_FILE, PRETTIER_SERVER_JS}; use project_settings::{LspSettings, ProjectSettings}; use rand::prelude::*; use search::SearchQuery; @@ -75,10 +79,13 @@ use std::{ time::{Duration, Instant}, }; use terminals::Terminals; -use text::Anchor; +use text::{Anchor, LineEnding, Rope}; use util::{ - debug_panic, defer, http::HttpClient, merge_json_value_into, - paths::LOCAL_SETTINGS_RELATIVE_PATH, post_inc, ResultExt, TryFutureExt as _, + debug_panic, defer, + http::HttpClient, + merge_json_value_into, + paths::{DEFAULT_PRETTIER_DIR, LOCAL_SETTINGS_RELATIVE_PATH}, + post_inc, ResultExt, TryFutureExt as _, }; pub use fs::*; @@ -152,6 +159,11 @@ pub struct Project { copilot_lsp_subscription: Option, copilot_log_subscription: Option, current_lsp_settings: HashMap, LspSettings>, + node: Option>, + prettier_instances: HashMap< + (Option, PathBuf), + Shared, Arc>>>, + >, } struct DelayedDebounced { @@ -580,6 +592,7 @@ impl Project { client.add_model_request_handler(Self::handle_apply_code_action); client.add_model_request_handler(Self::handle_on_type_formatting); client.add_model_request_handler(Self::handle_inlay_hints); + client.add_model_request_handler(Self::handle_resolve_completion_documentation); client.add_model_request_handler(Self::handle_resolve_inlay_hint); client.add_model_request_handler(Self::handle_refresh_inlay_hints); client.add_model_request_handler(Self::handle_reload_buffers); @@ -605,6 +618,7 @@ impl Project { pub fn local( client: Arc, + node: Arc, user_store: ModelHandle, languages: Arc, fs: Arc, @@ -660,6 +674,8 @@ impl Project { copilot_lsp_subscription, copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), + node: Some(node), + prettier_instances: HashMap::default(), } }) } @@ -757,6 +773,8 @@ impl Project { copilot_lsp_subscription, copilot_log_subscription: None, current_lsp_settings: settings::get::(cx).lsp.clone(), + node: None, + prettier_instances: HashMap::default(), }; for worktree in worktrees { let _ = this.add_worktree(&worktree, cx); @@ -795,8 +813,16 @@ impl Project { let http_client = util::http::FakeHttpClient::with_404_response(); let client = cx.update(|cx| client::Client::new(http_client.clone(), cx)); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx)); - let project = - cx.update(|cx| Project::local(client, user_store, Arc::new(languages), fs, cx)); + let project = cx.update(|cx| { + Project::local( + client, + node_runtime::FakeNodeRuntime::new(), + user_store, + Arc::new(languages), + fs, + cx, + ) + }); for path in root_paths { let (tree, _) = project .update(cx, |project, cx| { @@ -810,19 +836,37 @@ impl Project { project } + /// Enables a prettier mock that avoids interacting with node runtime, prettier LSP wrapper, or any real file changes. + /// Instead, if appends the suffix to every input, this suffix is returned by this method. + #[cfg(any(test, feature = "test-support"))] + pub fn enable_test_prettier(&mut self, plugins: &[&'static str]) -> &'static str { + self.node = Some(node_runtime::FakeNodeRuntime::with_prettier_support( + plugins, + )); + Prettier::FORMAT_SUFFIX + } + fn on_settings_changed(&mut self, cx: &mut ModelContext) { let mut language_servers_to_start = Vec::new(); + let mut language_formatters_to_check = Vec::new(); for buffer in self.opened_buffers.values() { if let Some(buffer) = buffer.upgrade(cx) { let buffer = buffer.read(cx); - if let Some((file, language)) = buffer.file().zip(buffer.language()) { - let settings = language_settings(Some(language), Some(file), cx); + let buffer_file = File::from_dyn(buffer.file()); + let buffer_language = buffer.language(); + let settings = language_settings(buffer_language, buffer.file(), cx); + if let Some(language) = buffer_language { if settings.enable_language_server { - if let Some(file) = File::from_dyn(Some(file)) { + if let Some(file) = buffer_file { language_servers_to_start - .push((file.worktree.clone(), language.clone())); + .push((file.worktree.clone(), Arc::clone(language))); } } + language_formatters_to_check.push(( + buffer_file.map(|f| f.worktree_id(cx)), + Arc::clone(language), + settings.clone(), + )); } } } @@ -875,6 +919,11 @@ impl Project { .detach(); } + for (worktree, language, settings) in language_formatters_to_check { + self.install_default_formatters(worktree, &language, &settings, cx) + .detach_and_log_err(cx); + } + // Start all the newly-enabled language servers. for (worktree, language) in language_servers_to_start { let worktree_path = worktree.read(cx).abs_path(); @@ -2623,7 +2672,26 @@ impl Project { } }); - if let Some(file) = File::from_dyn(buffer.read(cx).file()) { + let buffer_file = buffer.read(cx).file().cloned(); + let settings = language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone(); + let buffer_file = File::from_dyn(buffer_file.as_ref()); + let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx)); + + let task_buffer = buffer.clone(); + let prettier_installation_task = + self.install_default_formatters(worktree, &new_language, &settings, cx); + cx.spawn(|project, mut cx| async move { + prettier_installation_task.await?; + let _ = project + .update(&mut cx, |project, cx| { + project.prettier_instance_for_buffer(&task_buffer, cx) + }) + .await; + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + + if let Some(file) = buffer_file { let worktree = file.worktree.clone(); if let Some(tree) = worktree.read(cx).as_local() { self.start_language_servers(&worktree, tree.abs_path().clone(), new_language, cx); @@ -2684,15 +2752,6 @@ impl Project { let lsp = project_settings.lsp.get(&adapter.name.0); let override_options = lsp.map(|s| s.initialization_options.clone()).flatten(); - let mut initialization_options = adapter.initialization_options.clone(); - match (&mut initialization_options, override_options) { - (Some(initialization_options), Some(override_options)) => { - merge_json_value_into(override_options, initialization_options); - } - (None, override_options) => initialization_options = override_options, - _ => {} - } - let server_id = pending_server.server_id; let container_dir = pending_server.container_dir.clone(); let state = LanguageServerState::Starting({ @@ -2704,7 +2763,7 @@ impl Project { cx.spawn_weak(|this, mut cx| async move { let result = Self::setup_and_insert_language_server( this, - initialization_options, + override_options, pending_server, adapter.clone(), language.clone(), @@ -2807,7 +2866,7 @@ impl Project { async fn setup_and_insert_language_server( this: WeakModelHandle, - initialization_options: Option, + override_initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, language: Arc, @@ -2817,7 +2876,7 @@ impl Project { ) -> Result>> { let setup = Self::setup_pending_language_server( this, - initialization_options, + override_initialization_options, pending_server, adapter.clone(), server_id, @@ -2849,7 +2908,7 @@ impl Project { async fn setup_pending_language_server( this: WeakModelHandle, - initialization_options: Option, + override_options: Option, pending_server: PendingLanguageServer, adapter: Arc, server_id: LanguageServerId, @@ -2867,8 +2926,8 @@ impl Project { move |mut params, mut cx| { let this = this; let adapter = adapter.clone(); - adapter.process_diagnostics(&mut params); if let Some(this) = this.upgrade(&cx) { + adapter.process_diagnostics(&mut params); this.update(&mut cx, |this, cx| { this.update_diagnostics( server_id, @@ -2995,6 +3054,14 @@ impl Project { } }) .detach(); + let mut initialization_options = adapter.adapter.initialization_options().await; + match (&mut initialization_options, override_options) { + (Some(initialization_options), Some(override_options)) => { + merge_json_value_into(override_options, initialization_options); + } + (None, override_options) => initialization_options = override_options, + _ => {} + } let language_server = language_server.initialize(initialization_options).await?; @@ -3949,7 +4016,7 @@ impl Project { push_to_history: bool, trigger: FormatTrigger, cx: &mut ModelContext, - ) -> Task> { + ) -> Task> { if self.is_local() { let mut buffers_with_paths_and_servers = buffers .into_iter() @@ -4027,6 +4094,7 @@ impl Project { enum FormatOperation { Lsp(Vec<(Range, String)>), External(Diff), + Prettier(Diff), } // Apply language-specific formatting using either a language server @@ -4062,8 +4130,8 @@ impl Project { | (_, FormatOnSave::External { command, arguments }) => { if let Some(buffer_abs_path) = buffer_abs_path { format_operation = Self::format_via_external_command( - &buffer, - &buffer_abs_path, + buffer, + buffer_abs_path, &command, &arguments, &mut cx, @@ -4076,6 +4144,69 @@ impl Project { .map(FormatOperation::External); } } + (Formatter::Auto, FormatOnSave::On | FormatOnSave::Off) => { + if let Some(prettier_task) = this + .update(&mut cx, |project, cx| { + project.prettier_instance_for_buffer(buffer, cx) + }).await { + match prettier_task.await + { + Ok(prettier) => { + let buffer_path = buffer.read_with(&cx, |buffer, cx| { + File::from_dyn(buffer.file()).map(|file| file.abs_path(cx)) + }); + format_operation = Some(FormatOperation::Prettier( + prettier + .format(buffer, buffer_path, &cx) + .await + .context("formatting via prettier")?, + )); + } + Err(e) => anyhow::bail!( + "Failed to create prettier instance for buffer during autoformatting: {e:#}" + ), + } + } else if let Some((language_server, buffer_abs_path)) = + language_server.as_ref().zip(buffer_abs_path.as_ref()) + { + format_operation = Some(FormatOperation::Lsp( + Self::format_via_lsp( + &this, + &buffer, + buffer_abs_path, + &language_server, + tab_size, + &mut cx, + ) + .await + .context("failed to format via language server")?, + )); + } + } + (Formatter::Prettier { .. }, FormatOnSave::On | FormatOnSave::Off) => { + if let Some(prettier_task) = this + .update(&mut cx, |project, cx| { + project.prettier_instance_for_buffer(buffer, cx) + }).await { + match prettier_task.await + { + Ok(prettier) => { + let buffer_path = buffer.read_with(&cx, |buffer, cx| { + File::from_dyn(buffer.file()).map(|file| file.abs_path(cx)) + }); + format_operation = Some(FormatOperation::Prettier( + prettier + .format(buffer, buffer_path, &cx) + .await + .context("formatting via prettier")?, + )); + } + Err(e) => anyhow::bail!( + "Failed to create prettier instance for buffer during formatting: {e:#}" + ), + } + } + } }; buffer.update(&mut cx, |b, cx| { @@ -4100,6 +4231,9 @@ impl Project { FormatOperation::External(diff) => { b.apply_diff(diff, cx); } + FormatOperation::Prettier(diff) => { + b.apply_diff(diff, cx); + } } if let Some(transaction_id) = whitespace_transaction_id { @@ -5873,6 +6007,7 @@ impl Project { this.update_local_worktree_buffers(&worktree, changes, cx); this.update_local_worktree_language_servers(&worktree, changes, cx); this.update_local_worktree_settings(&worktree, changes, cx); + this.update_prettier_settings(&worktree, changes, cx); cx.emit(Event::WorktreeUpdatedEntries( worktree.read(cx).id(), changes.clone(), @@ -6252,6 +6387,69 @@ impl Project { .detach(); } + fn update_prettier_settings( + &self, + worktree: &ModelHandle, + changes: &[(Arc, ProjectEntryId, PathChange)], + cx: &mut ModelContext<'_, Project>, + ) { + let prettier_config_files = Prettier::CONFIG_FILE_NAMES + .iter() + .map(Path::new) + .collect::>(); + + let prettier_config_file_changed = changes + .iter() + .filter(|(_, _, change)| !matches!(change, PathChange::Loaded)) + .filter(|(path, _, _)| { + !path + .components() + .any(|component| component.as_os_str().to_string_lossy() == "node_modules") + }) + .find(|(path, _, _)| prettier_config_files.contains(path.as_ref())); + let current_worktree_id = worktree.read(cx).id(); + if let Some((config_path, _, _)) = prettier_config_file_changed { + log::info!( + "Prettier config file {config_path:?} changed, reloading prettier instances for worktree {current_worktree_id}" + ); + let prettiers_to_reload = self + .prettier_instances + .iter() + .filter_map(|((worktree_id, prettier_path), prettier_task)| { + if worktree_id.is_none() || worktree_id == &Some(current_worktree_id) { + Some((*worktree_id, prettier_path.clone(), prettier_task.clone())) + } else { + None + } + }) + .collect::>(); + + cx.background() + .spawn(async move { + for task_result in future::join_all(prettiers_to_reload.into_iter().map(|(worktree_id, prettier_path, prettier_task)| { + async move { + prettier_task.await? + .clear_cache() + .await + .with_context(|| { + format!( + "clearing prettier {prettier_path:?} cache for worktree {worktree_id:?} on prettier settings update" + ) + }) + .map_err(Arc::new) + } + })) + .await + { + if let Err(e) = task_result { + log::error!("Failed to clear cache for prettier: {e:#}"); + } + } + }) + .detach(); + } + } + pub fn set_active_path(&mut self, entry: Option, cx: &mut ModelContext) { let new_active_entry = entry.and_then(|project_path| { let worktree = self.worktree_for_id(project_path.worktree_id, cx)?; @@ -7155,6 +7353,40 @@ impl Project { }) } + async fn handle_resolve_completion_documentation( + this: ModelHandle, + envelope: TypedEnvelope, + _: Arc, + mut cx: AsyncAppContext, + ) -> Result { + let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?; + + let completion = this + .read_with(&mut cx, |this, _| { + let id = LanguageServerId(envelope.payload.language_server_id as usize); + let Some(server) = this.language_server_for_id(id) else { + return Err(anyhow!("No language server {id}")); + }; + + Ok(server.request::(lsp_completion)) + })? + .await?; + + let mut is_markdown = false; + let text = match completion.documentation { + Some(lsp::Documentation::String(text)) => text, + + Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => { + is_markdown = kind == lsp::MarkupKind::Markdown; + value + } + + _ => String::new(), + }; + + Ok(proto::ResolveCompletionDocumentationResponse { text, is_markdown }) + } + async fn handle_apply_code_action( this: ModelHandle, envelope: TypedEnvelope, @@ -8109,6 +8341,236 @@ impl Project { Vec::new() } } + + fn prettier_instance_for_buffer( + &mut self, + buffer: &ModelHandle, + cx: &mut ModelContext, + ) -> Task, Arc>>>>> { + let buffer = buffer.read(cx); + let buffer_file = buffer.file(); + let Some(buffer_language) = buffer.language() else { + return Task::ready(None); + }; + if !buffer_language + .lsp_adapters() + .iter() + .flat_map(|adapter| adapter.enabled_formatters()) + .any(|formatter| matches!(formatter, BundledFormatter::Prettier { .. })) + { + return Task::ready(None); + } + + let buffer_file = File::from_dyn(buffer_file); + let buffer_path = buffer_file.map(|file| Arc::clone(file.path())); + let worktree_path = buffer_file + .as_ref() + .and_then(|file| Some(file.worktree.read(cx).abs_path())); + let worktree_id = buffer_file.map(|file| file.worktree_id(cx)); + if self.is_local() || worktree_id.is_none() || worktree_path.is_none() { + let Some(node) = self.node.as_ref().map(Arc::clone) else { + return Task::ready(None); + }; + cx.spawn(|this, mut cx| async move { + let fs = this.update(&mut cx, |project, _| Arc::clone(&project.fs)); + let prettier_dir = match cx + .background() + .spawn(Prettier::locate( + worktree_path.zip(buffer_path).map( + |(worktree_root_path, starting_path)| LocateStart { + worktree_root_path, + starting_path, + }, + ), + fs, + )) + .await + { + Ok(path) => path, + Err(e) => { + return Some( + Task::ready(Err(Arc::new(e.context( + "determining prettier path for worktree {worktree_path:?}", + )))) + .shared(), + ); + } + }; + + if let Some(existing_prettier) = this.update(&mut cx, |project, _| { + project + .prettier_instances + .get(&(worktree_id, prettier_dir.clone())) + .cloned() + }) { + return Some(existing_prettier); + } + + log::info!("Found prettier in {prettier_dir:?}, starting."); + let task_prettier_dir = prettier_dir.clone(); + let weak_project = this.downgrade(); + let new_server_id = + this.update(&mut cx, |this, _| this.languages.next_language_server_id()); + let new_prettier_task = cx + .spawn(|mut cx| async move { + let prettier = Prettier::start( + worktree_id.map(|id| id.to_usize()), + new_server_id, + task_prettier_dir, + node, + cx.clone(), + ) + .await + .context("prettier start") + .map_err(Arc::new)?; + log::info!("Started prettier in {:?}", prettier.prettier_dir()); + + if let Some((project, prettier_server)) = + weak_project.upgrade(&mut cx).zip(prettier.server()) + { + project.update(&mut cx, |project, cx| { + let name = if prettier.is_default() { + LanguageServerName(Arc::from("prettier (default)")) + } else { + let prettier_dir = prettier.prettier_dir(); + let worktree_path = prettier + .worktree_id() + .map(WorktreeId::from_usize) + .and_then(|id| project.worktree_for_id(id, cx)) + .map(|worktree| worktree.read(cx).abs_path()); + match worktree_path { + Some(worktree_path) => { + if worktree_path.as_ref() == prettier_dir { + LanguageServerName(Arc::from(format!( + "prettier ({})", + prettier_dir + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or_default() + ))) + } else { + let dir_to_display = match prettier_dir + .strip_prefix(&worktree_path) + .ok() + { + Some(relative_path) => relative_path, + None => prettier_dir, + }; + LanguageServerName(Arc::from(format!( + "prettier ({})", + dir_to_display.display(), + ))) + } + } + None => LanguageServerName(Arc::from(format!( + "prettier ({})", + prettier_dir.display(), + ))), + } + }; + + project + .supplementary_language_servers + .insert(new_server_id, (name, Arc::clone(prettier_server))); + cx.emit(Event::LanguageServerAdded(new_server_id)); + }); + } + Ok(Arc::new(prettier)).map_err(Arc::new) + }) + .shared(); + this.update(&mut cx, |project, _| { + project + .prettier_instances + .insert((worktree_id, prettier_dir), new_prettier_task.clone()); + }); + Some(new_prettier_task) + }) + } else if self.remote_id().is_some() { + return Task::ready(None); + } else { + Task::ready(Some( + Task::ready(Err(Arc::new(anyhow!("project does not have a remote id")))).shared(), + )) + } + } + + fn install_default_formatters( + &self, + worktree: Option, + new_language: &Language, + language_settings: &LanguageSettings, + cx: &mut ModelContext, + ) -> Task> { + match &language_settings.formatter { + Formatter::Prettier { .. } | Formatter::Auto => {} + Formatter::LanguageServer | Formatter::External { .. } => return Task::ready(Ok(())), + }; + let Some(node) = self.node.as_ref().cloned() else { + return Task::ready(Ok(())); + }; + + let mut prettier_plugins = None; + for formatter in new_language + .lsp_adapters() + .into_iter() + .flat_map(|adapter| adapter.enabled_formatters()) + { + match formatter { + BundledFormatter::Prettier { plugin_names, .. } => prettier_plugins + .get_or_insert_with(|| HashSet::default()) + .extend(plugin_names), + } + } + let Some(prettier_plugins) = prettier_plugins else { + return Task::ready(Ok(())); + }; + + let default_prettier_dir = DEFAULT_PRETTIER_DIR.as_path(); + let already_running_prettier = self + .prettier_instances + .get(&(worktree, default_prettier_dir.to_path_buf())) + .cloned(); + + let fs = Arc::clone(&self.fs); + cx.background() + .spawn(async move { + let prettier_wrapper_path = default_prettier_dir.join(PRETTIER_SERVER_FILE); + // method creates parent directory if it doesn't exist + fs.save(&prettier_wrapper_path, &Rope::from(PRETTIER_SERVER_JS), LineEnding::Unix).await + .with_context(|| format!("writing {PRETTIER_SERVER_FILE} file at {prettier_wrapper_path:?}"))?; + + let packages_to_versions = future::try_join_all( + prettier_plugins + .iter() + .chain(Some(&"prettier")) + .map(|package_name| async { + let returned_package_name = package_name.to_string(); + let latest_version = node.npm_package_latest_version(package_name) + .await + .with_context(|| { + format!("fetching latest npm version for package {returned_package_name}") + })?; + anyhow::Ok((returned_package_name, latest_version)) + }), + ) + .await + .context("fetching latest npm versions")?; + + log::info!("Fetching default prettier and plugins: {packages_to_versions:?}"); + let borrowed_packages = packages_to_versions.iter().map(|(package, version)| { + (package.as_str(), version.as_str()) + }).collect::>(); + node.npm_install_packages(default_prettier_dir, &borrowed_packages).await.context("fetching formatter packages")?; + + if !prettier_plugins.is_empty() { + if let Some(prettier) = already_running_prettier { + prettier.await.map_err(|e| anyhow::anyhow!("Default prettier startup await failure: {e:#}"))?.clear_cache().await.context("clearing default prettier cache after plugins install")?; + } + } + + anyhow::Ok(()) + }) + } } fn subscribe_for_copilot_events( diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index f6d0dfa5d9ff2b28e6c9d6fa8fd8b31645275b7d..0ca10b381ae7cde3c76689f3946abc2a408911ef 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -89,89 +89,91 @@ message Envelope { FormatBuffersResponse format_buffers_response = 70; GetCompletions get_completions = 71; GetCompletionsResponse get_completions_response = 72; - ApplyCompletionAdditionalEdits apply_completion_additional_edits = 73; - ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 74; - GetCodeActions get_code_actions = 75; - GetCodeActionsResponse get_code_actions_response = 76; - GetHover get_hover = 77; - GetHoverResponse get_hover_response = 78; - ApplyCodeAction apply_code_action = 79; - ApplyCodeActionResponse apply_code_action_response = 80; - PrepareRename prepare_rename = 81; - PrepareRenameResponse prepare_rename_response = 82; - PerformRename perform_rename = 83; - PerformRenameResponse perform_rename_response = 84; - SearchProject search_project = 85; - SearchProjectResponse search_project_response = 86; - - UpdateContacts update_contacts = 87; - UpdateInviteInfo update_invite_info = 88; - ShowContacts show_contacts = 89; - - GetUsers get_users = 90; - FuzzySearchUsers fuzzy_search_users = 91; - UsersResponse users_response = 92; - RequestContact request_contact = 93; - RespondToContactRequest respond_to_contact_request = 94; - RemoveContact remove_contact = 95; - - Follow follow = 96; - FollowResponse follow_response = 97; - UpdateFollowers update_followers = 98; - Unfollow unfollow = 99; - GetPrivateUserInfo get_private_user_info = 100; - GetPrivateUserInfoResponse get_private_user_info_response = 101; - UpdateDiffBase update_diff_base = 102; - - OnTypeFormatting on_type_formatting = 103; - OnTypeFormattingResponse on_type_formatting_response = 104; - - UpdateWorktreeSettings update_worktree_settings = 105; - - InlayHints inlay_hints = 106; - InlayHintsResponse inlay_hints_response = 107; - ResolveInlayHint resolve_inlay_hint = 108; - ResolveInlayHintResponse resolve_inlay_hint_response = 109; - RefreshInlayHints refresh_inlay_hints = 110; - - CreateChannel create_channel = 111; - CreateChannelResponse create_channel_response = 112; - InviteChannelMember invite_channel_member = 113; - RemoveChannelMember remove_channel_member = 114; - RespondToChannelInvite respond_to_channel_invite = 115; - UpdateChannels update_channels = 116; - JoinChannel join_channel = 117; - DeleteChannel delete_channel = 118; - GetChannelMembers get_channel_members = 119; - GetChannelMembersResponse get_channel_members_response = 120; - SetChannelMemberRole set_channel_member_role = 145; - RenameChannel rename_channel = 122; - RenameChannelResponse rename_channel_response = 123; - - JoinChannelBuffer join_channel_buffer = 124; - JoinChannelBufferResponse join_channel_buffer_response = 125; - UpdateChannelBuffer update_channel_buffer = 126; - LeaveChannelBuffer leave_channel_buffer = 127; - UpdateChannelBufferCollaborators update_channel_buffer_collaborators = 128; - RejoinChannelBuffers rejoin_channel_buffers = 129; - RejoinChannelBuffersResponse rejoin_channel_buffers_response = 130; - AckBufferOperation ack_buffer_operation = 143; - - JoinChannelChat join_channel_chat = 131; - JoinChannelChatResponse join_channel_chat_response = 132; - LeaveChannelChat leave_channel_chat = 133; - SendChannelMessage send_channel_message = 134; - SendChannelMessageResponse send_channel_message_response = 135; - ChannelMessageSent channel_message_sent = 136; - GetChannelMessages get_channel_messages = 137; - GetChannelMessagesResponse get_channel_messages_response = 138; - RemoveChannelMessage remove_channel_message = 139; - AckChannelMessage ack_channel_message = 144; - - LinkChannel link_channel = 140; - UnlinkChannel unlink_channel = 141; - MoveChannel move_channel = 142; - SetChannelVisibility set_channel_visibility = 146; // current max: 146 + ResolveCompletionDocumentation resolve_completion_documentation = 73; + ResolveCompletionDocumentationResponse resolve_completion_documentation_response = 74; + ApplyCompletionAdditionalEdits apply_completion_additional_edits = 75; + ApplyCompletionAdditionalEditsResponse apply_completion_additional_edits_response = 76; + GetCodeActions get_code_actions = 77; + GetCodeActionsResponse get_code_actions_response = 78; + GetHover get_hover = 79; + GetHoverResponse get_hover_response = 80; + ApplyCodeAction apply_code_action = 81; + ApplyCodeActionResponse apply_code_action_response = 82; + PrepareRename prepare_rename = 83; + PrepareRenameResponse prepare_rename_response = 84; + PerformRename perform_rename = 85; + PerformRenameResponse perform_rename_response = 86; + SearchProject search_project = 87; + SearchProjectResponse search_project_response = 88; + + UpdateContacts update_contacts = 89; + UpdateInviteInfo update_invite_info = 90; + ShowContacts show_contacts = 91; + + GetUsers get_users = 92; + FuzzySearchUsers fuzzy_search_users = 93; + UsersResponse users_response = 94; + RequestContact request_contact = 95; + RespondToContactRequest respond_to_contact_request = 96; + RemoveContact remove_contact = 97; + + Follow follow = 98; + FollowResponse follow_response = 99; + UpdateFollowers update_followers = 100; + Unfollow unfollow = 101; + GetPrivateUserInfo get_private_user_info = 102; + GetPrivateUserInfoResponse get_private_user_info_response = 103; + UpdateDiffBase update_diff_base = 104; + + OnTypeFormatting on_type_formatting = 105; + OnTypeFormattingResponse on_type_formatting_response = 106; + + UpdateWorktreeSettings update_worktree_settings = 107; + + InlayHints inlay_hints = 108; + InlayHintsResponse inlay_hints_response = 109; + ResolveInlayHint resolve_inlay_hint = 110; + ResolveInlayHintResponse resolve_inlay_hint_response = 111; + RefreshInlayHints refresh_inlay_hints = 112; + + CreateChannel create_channel = 113; + CreateChannelResponse create_channel_response = 114; + InviteChannelMember invite_channel_member = 115; + RemoveChannelMember remove_channel_member = 116; + RespondToChannelInvite respond_to_channel_invite = 117; + UpdateChannels update_channels = 118; + JoinChannel join_channel = 119; + DeleteChannel delete_channel = 120; + GetChannelMembers get_channel_members = 121; + GetChannelMembersResponse get_channel_members_response = 122; + SetChannelMemberRole set_channel_member_role = 123; + RenameChannel rename_channel = 124; + RenameChannelResponse rename_channel_response = 125; + + JoinChannelBuffer join_channel_buffer = 126; + JoinChannelBufferResponse join_channel_buffer_response = 127; + UpdateChannelBuffer update_channel_buffer = 128; + LeaveChannelBuffer leave_channel_buffer = 129; + UpdateChannelBufferCollaborators update_channel_buffer_collaborators = 130; + RejoinChannelBuffers rejoin_channel_buffers = 131; + RejoinChannelBuffersResponse rejoin_channel_buffers_response = 132; + AckBufferOperation ack_buffer_operation = 145; + + JoinChannelChat join_channel_chat = 133; + JoinChannelChatResponse join_channel_chat_response = 134; + LeaveChannelChat leave_channel_chat = 135; + SendChannelMessage send_channel_message = 136; + SendChannelMessageResponse send_channel_message_response = 137; + ChannelMessageSent channel_message_sent = 138; + GetChannelMessages get_channel_messages = 139; + GetChannelMessagesResponse get_channel_messages_response = 140; + RemoveChannelMessage remove_channel_message = 141; + AckChannelMessage ack_channel_message = 146; + + LinkChannel link_channel = 142; + UnlinkChannel unlink_channel = 143; + MoveChannel move_channel = 144; + SetChannelVisibility set_channel_visibility = 147; // current max: 147 } } @@ -833,6 +835,17 @@ message ResolveState { } } +message ResolveCompletionDocumentation { + uint64 project_id = 1; + uint64 language_server_id = 2; + bytes lsp_completion = 3; +} + +message ResolveCompletionDocumentationResponse { + string text = 1; + bool is_markdown = 2; +} + message ResolveInlayHint { uint64 project_id = 1; uint64 buffer_id = 2; diff --git a/crates/rpc/src/proto.rs b/crates/rpc/src/proto.rs index c60e99602e9a4c42b8f6bf857c7f9a43cd069883..682b27554868d40bd3588d854422da4638128096 100644 --- a/crates/rpc/src/proto.rs +++ b/crates/rpc/src/proto.rs @@ -205,6 +205,8 @@ messages!( (OnTypeFormattingResponse, Background), (InlayHints, Background), (InlayHintsResponse, Background), + (ResolveCompletionDocumentation, Background), + (ResolveCompletionDocumentationResponse, Background), (ResolveInlayHint, Background), (ResolveInlayHintResponse, Background), (RefreshInlayHints, Foreground), @@ -319,6 +321,10 @@ request_messages!( (PrepareRename, PrepareRenameResponse), (OnTypeFormatting, OnTypeFormattingResponse), (InlayHints, InlayHintsResponse), + ( + ResolveCompletionDocumentation, + ResolveCompletionDocumentationResponse + ), (ResolveInlayHint, ResolveInlayHintResponse), (RefreshInlayHints, Ack), (ReloadBuffers, ReloadBuffersResponse), @@ -383,6 +389,7 @@ entity_messages!( PerformRename, OnTypeFormatting, InlayHints, + ResolveCompletionDocumentation, ResolveInlayHint, RefreshInlayHints, PrepareRename, diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index 942672b94bab387a95bfe37bb6b73308e73496dc..5ba531a50ed8d62cc857932c3acae31be1c48433 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 64; +pub const PROTOCOL_VERSION: u32 = 65; diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index 34850f7035c15aba1e7a1ba1c54bc6b1431a5c9d..1febb2af786e205e19764e90ca1c78d954d8a1bb 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -51,7 +51,6 @@ workspace = { path = "../workspace", features = ["test-support"] } settings = { path = "../settings", features = ["test-support"]} rust-embed = { version = "8.0", features = ["include-exclude"] } client = { path = "../client" } -zed = { path = "../zed"} node_runtime = { path = "../node_runtime"} pretty_assertions.workspace = true @@ -70,6 +69,3 @@ tree-sitter-elixir.workspace = true tree-sitter-lua.workspace = true tree-sitter-ruby.workspace = true tree-sitter-php.workspace = true - -[[example]] -name = "eval" diff --git a/crates/storybook/Cargo.lock b/crates/storybook/Cargo.lock deleted file mode 100644 index b652dbbd132575197b77521df34d1df0785ffa09..0000000000000000000000000000000000000000 --- a/crates/storybook/Cargo.lock +++ /dev/null @@ -1,2919 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" -dependencies = [ - "async-lock", - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock", - "autocfg", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite", - "log", - "parking", - "polling", - "rustix", - "slab", - "socket2", - "waker-fn", -] - -[[package]] -name = "async-lock" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-net" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4051e67316bc7eff608fe723df5d32ed639946adcd69e07df41fd42a7b411f1f" -dependencies = [ - "async-io", - "autocfg", - "blocking", - "futures-lite", -] - -[[package]] -name = "async-process" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" -dependencies = [ - "async-io", - "async-lock", - "autocfg", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix", - "signal-hook", - "windows-sys", -] - -[[package]] -name = "async-task" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic-waker" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.7.1", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.25", - "which", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" -dependencies = [ - "async-channel", - "async-lock", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "log", -] - -[[package]] -name = "bstr" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "castaway" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading 0.7.4", -] - -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "collections" -version = "0.1.0" - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "concurrent-queue" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-cstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", - "uuid 0.5.1", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags", - "core-foundation", - "libc", -] - -[[package]] -name = "core-text" -version = "19.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" -dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "curl" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.63+curl-8.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi", -] - -[[package]] -name = "data-url" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" -dependencies = [ - "matches", -] - -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading 0.8.0", -] - -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "winapi", - "wio", -] - -[[package]] -name = "dyn-clone" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "erased-serde" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f94c0e13118e7d7533271f754a168ae8400e6a1cc043f2bfd53cc7290f1a1de3" -dependencies = [ - "serde", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "etagere" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf22f748754352918e082e0039335ee92454a5d62bcaf69b5e8daf5907d9644" -dependencies = [ - "euclid", - "svg_fmt", -] - -[[package]] -name = "euclid" -version = "0.22.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide 0.7.1", -] - -[[package]] -name = "float-cmp" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e" - -[[package]] -name = "float-ord" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bad48618fdb549078c333a7a8528acb57af271d0433bdecd523eb620628364e" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "font-kit" -version = "0.11.0" -source = "git+https://github.com/zed-industries/font-kit?rev=b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18#b2f77d56f450338aa4f7dd2f0197d8c9acb0cf18" -dependencies = [ - "bitflags", - "byteorder", - "core-foundation", - "core-graphics", - "core-text", - "dirs-next", - "dwrote", - "float-ord", - "freetype", - "lazy_static", - "libc", - "log", - "pathfinder_geometry", - "pathfinder_simd", - "walkdir", - "winapi", - "yeslogic-fontconfig-sys", -] - -[[package]] -name = "fontdb" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58903f4f8d5b58c7d300908e4ebe5289c1bfdf5587964330f12023b8ff17fd1" -dependencies = [ - "log", - "memmap2", - "ttf-parser 0.12.3", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "freetype" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" -dependencies = [ - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" -dependencies = [ - "aho-corasick", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "gpui" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-task", - "bindgen", - "block", - "cc", - "cocoa", - "collections", - "core-foundation", - "core-graphics", - "core-text", - "ctor", - "etagere", - "font-kit", - "foreign-types", - "futures", - "gpui_macros", - "image", - "itertools", - "lazy_static", - "log", - "media", - "metal", - "num_cpus", - "objc", - "ordered-float", - "parking", - "parking_lot 0.11.2", - "pathfinder_color", - "pathfinder_geometry", - "postage", - "rand", - "resvg", - "schemars", - "seahash", - "serde", - "serde_derive", - "serde_json", - "smallvec", - "smol", - "sqlez", - "sum_tree", - "time", - "tiny-skia", - "usvg", - "util", - "uuid 1.4.0", - "waker-fn", -] - -[[package]] -name = "gpui_macros" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "image" -version = "0.23.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "gif", - "jpeg-decoder", - "num-iter", - "num-rational", - "num-traits", - "png", - "scoped_threadpool", - "tiff", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "indoc" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - -[[package]] -name = "isahc" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" -dependencies = [ - "async-channel", - "castaway", - "crossbeam-utils", - "curl", - "curl-sys", - "encoding_rs", - "event-listener", - "futures-lite", - "http", - "log", - "mime", - "once_cell", - "polling", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" - -[[package]] -name = "jpeg-decoder" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" -dependencies = [ - "rayon", -] - -[[package]] -name = "kurbo" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" -dependencies = [ - "arrayvec 0.7.4", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" -dependencies = [ - "cfg-if", - "windows-sys", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" -dependencies = [ - "serde", - "value-bag", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "media" -version = "0.1.0" -dependencies = [ - "anyhow", - "bindgen", - "block", - "bytes", - "core-foundation", - "foreign-types", - "metal", - "objc", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmap2" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "metal" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c" -dependencies = [ - "bitflags", - "block", - "cocoa-foundation", - "foreign-types", - "log", - "objc", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-float" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" -dependencies = [ - "num-traits", -] - -[[package]] -name = "parking" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec", - "windows-targets", -] - -[[package]] -name = "pathfinder_color" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69bdc0d277d559e35e1b374de56df9262a6b71e091ca04a8831a239f8c7f0c62" -dependencies = [ - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_geometry" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" -dependencies = [ - "log", - "pathfinder_simd", -] - -[[package]] -name = "pathfinder_simd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pest" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pico-args" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" - -[[package]] -name = "pin-project" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "storybook" -version = "0.1.0" -dependencies = [ - "gpui", -] - -[[package]] -name = "png" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" -dependencies = [ - "bitflags", - "crc32fast", - "deflate", - "miniz_oxide 0.3.7", -] - -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys", -] - -[[package]] -name = "pollster" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" - -[[package]] -name = "postage" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" -dependencies = [ - "atomic", - "crossbeam-queue", - "futures", - "log", - "parking_lot 0.12.1", - "pin-project", - "pollster", - "static_assertions", - "thiserror", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "prettyplease" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387" -dependencies = [ - "proc-macro2", - "syn 2.0.25", -] - -[[package]] -name = "proc-macro2" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "rctree" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9e29cb19c8fe84169fcb07f8f11e66bc9e6e0280efd4715c54818296f8a4a8" - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "resvg" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09697862c5c3f940cbaffef91969c62188b5c8ed385b0aef43a5ff01ddc8000f" -dependencies = [ - "jpeg-decoder", - "log", - "pico-args", - "png", - "rgb", - "svgfilters", - "tiny-skia", - "usvg", -] - -[[package]] -name = "rgb" -version = "0.8.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "rust-embed" -version = "6.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "6.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.25", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "7.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" -dependencies = [ - "globset", - "sha2", - "walkdir", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.37.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustybuzz" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10" -dependencies = [ - "bitflags", - "bytemuck", - "smallvec", - "ttf-parser 0.9.0", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-general-category", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" - -[[package]] -name = "safe_arch" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "schemars" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 1.0.109", -] - -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.171" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "serde_derive_internals" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "serde_fmt" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d4ddca14104cd60529e8c7f7ba71a2c8acd8f7f5cfcdc2faf97eeb7c3010a4" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_json" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "simplecss" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "smol" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f2b548cd8447f8de0fdf1c592929f70f4fc7039a05e47404b0d096ec6987a1" -dependencies = [ - "async-channel", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-net", - "async-process", - "blocking", - "futures-lite", -] - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "sqlez" -version = "0.1.0" -dependencies = [ - "anyhow", - "futures", - "indoc", - "lazy_static", - "libsqlite3-sys", - "parking_lot 0.11.2", - "smol", - "thread_local", - "uuid 1.4.0", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "sum_tree" -version = "0.1.0" -dependencies = [ - "arrayvec 0.7.4", - "log", -] - -[[package]] -name = "sval" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b031320a434d3e9477ccf9b5756d57d4272937b8d22cb88af80b7633a1b78b1" - -[[package]] -name = "sval_buffer" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf7e9412af26b342f3f2cc5cc4122b0105e9d16eb76046cd14ed10106cf6028" -dependencies = [ - "sval", - "sval_ref", -] - -[[package]] -name = "sval_dynamic" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ef628e8a77a46ed3338db8d1b08af77495123cc229453084e47cd716d403cf" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_fmt" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc09e9364c2045ab5fa38f7b04d077b3359d30c4c2b3ec4bae67a358bd64326" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_json" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ada6f627e38cbb8860283649509d87bc4a5771141daa41c78fd31f2b9485888d" -dependencies = [ - "itoa", - "ryu", - "sval", -] - -[[package]] -name = "sval_ref" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703ca1942a984bd0d9b5a4c0a65ab8b4b794038d080af4eb303c71bc6bf22d7c" -dependencies = [ - "sval", -] - -[[package]] -name = "sval_serde" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830926cd0581f7c3e5d51efae4d35c6b6fc4db583842652891ba2f1bed8db046" -dependencies = [ - "serde", - "sval", - "sval_buffer", - "sval_fmt", -] - -[[package]] -name = "svg_fmt" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" - -[[package]] -name = "svgfilters" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0dce2fee79ac40c21dafba48565ff7a5fa275e23ffe9ce047a40c9574ba34e" -dependencies = [ - "float-cmp", - "rgb", -] - -[[package]] -name = "svgtypes" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff" -dependencies = [ - "float-cmp", - "siphasher", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "take-until" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb" - -[[package]] -name = "thiserror" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tiff" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" -dependencies = [ - "jpeg-decoder", - "miniz_oxide 0.4.4", - "weezl", -] - -[[package]] -name = "time" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-skia" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf81f2900d2e235220e6f31ec9f63ade6a7f59090c556d74fe949bb3b15e9fe" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "bytemuck", - "cfg-if", - "png", - "safe_arch", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.25", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "ttf-parser" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca" - -[[package]] -name = "ttf-parser" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" - -[[package]] -name = "unicode-ccc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" - -[[package]] -name = "unicode-general-category" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f" - -[[package]] -name = "unicode-ident" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-script" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "usvg" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8352f317d8f9a918ba5154797fb2a93e2730244041cf7d5be35148266adfa5" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "kurbo", - "log", - "memmap2", - "pico-args", - "rctree", - "roxmltree", - "rustybuzz", - "simplecss", - "siphasher", - "svgtypes", - "ttf-parser 0.12.3", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "util" -version = "0.1.0" -dependencies = [ - "anyhow", - "backtrace", - "dirs", - "futures", - "isahc", - "lazy_static", - "log", - "rand", - "rust-embed", - "serde", - "serde_json", - "smol", - "take-until", - "url", -] - -[[package]] -name = "uuid" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" - -[[package]] -name = "uuid" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" -dependencies = [ - "getrandom", -] - -[[package]] -name = "value-bag" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" -dependencies = [ - "value-bag-serde1", - "value-bag-sval2", -] - -[[package]] -name = "value-bag-serde1" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b9f3feef403a50d4d67e9741a6d8fc688bcbb4e4f31bd4aab72cc690284394" -dependencies = [ - "erased-serde", - "serde", - "serde_fmt", -] - -[[package]] -name = "value-bag-sval2" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b24f4146b6f3361e91cbf527d1fb35e9376c3c0cef72ca5ec5af6d640fad7d" -dependencies = [ - "sval", - "sval_buffer", - "sval_dynamic", - "sval_fmt", - "sval_json", - "sval_ref", - "sval_serde", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi", -] - -[[package]] -name = "xmlparser" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "yeslogic-fontconfig-sys" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bbd69036d397ebbff671b1b8e4d918610c181c5a16073b96f984a38d08c386" -dependencies = [ - "const-cstr", - "dlib", - "once_cell", - "pkg-config", -] diff --git a/crates/storybook/Cargo.toml b/crates/storybook/Cargo.toml deleted file mode 100644 index 43890dd01a78f1b34ab7cb5f65ae67a17c3f3d23..0000000000000000000000000000000000000000 --- a/crates/storybook/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "storybook" -version = "0.1.0" -edition = "2021" -publish = false - -[[bin]] -name = "storybook" -path = "src/storybook.rs" - -[dependencies] -anyhow.workspace = true -clap = { version = "4.4", features = ["derive", "string"] } -chrono = "0.4" -fs = { path = "../fs" } -futures.workspace = true -gpui2 = { path = "../gpui2" } -itertools = "0.11.0" -log.workspace = true -rust-embed.workspace = true -serde.workspace = true -settings = { path = "../settings" } -simplelog = "0.9" -strum = { version = "0.25.0", features = ["derive"] } -theme = { path = "../theme" } -ui = { path = "../ui" } -util = { path = "../util" } - -[dev-dependencies] -gpui2 = { path = "../gpui2", features = ["test-support"] } diff --git a/crates/storybook/docs/thoughts.md b/crates/storybook/docs/thoughts.md deleted file mode 100644 index 9416f2593313b973ada409770b41b1f1f074ba09..0000000000000000000000000000000000000000 --- a/crates/storybook/docs/thoughts.md +++ /dev/null @@ -1,72 +0,0 @@ -Much of element styling is now handled by an external engine. - - -How do I make an element hover. - -There's a hover style. - -Hoverable needs to wrap another element. That element can be styled. - -```rs -struct Hoverable { - -} - -impl Element for Hoverable { - -} - -``` - - - -```rs -#[derive(Styled, Interactive)] -pub struct Div { - declared_style: StyleRefinement, - interactions: Interactions -} - -pub trait Styled { - fn declared_style(&mut self) -> &mut StyleRefinement; - fn compute_style(&mut self) -> Style { - Style::default().refine(self.declared_style()) - } - - // All the tailwind classes, modifying self.declared_style() -} - -impl Style { - pub fn paint_background(layout: Layout, cx: &mut PaintContext); - pub fn paint_foreground(layout: Layout, cx: &mut PaintContext); -} - -pub trait Interactive { - fn interactions(&mut self) -> &mut Interactions; - - fn on_click(self, ) -} - -struct Interactions { - click: SmallVec<[; 1]>, -} - - -``` - - -```rs - - -trait Stylable { - type Style; - - fn with_style(self, style: Self::Style) -> Self; -} - - - - - - -``` diff --git a/crates/storybook/src/stories.rs b/crates/storybook/src/stories.rs deleted file mode 100644 index 95b8844157c8ab72383cb10ea7a7c6dbe38efc06..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod components; -pub mod elements; -pub mod kitchen_sink; diff --git a/crates/storybook/src/stories/components.rs b/crates/storybook/src/stories/components.rs deleted file mode 100644 index 85d5ce088f05dc7410cff289b5d8a67aedbfd4c8..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub mod assistant_panel; -pub mod breadcrumb; -pub mod buffer; -pub mod chat_panel; -pub mod collab_panel; -pub mod context_menu; -pub mod facepile; -pub mod keybinding; -pub mod language_selector; -pub mod multi_buffer; -pub mod palette; -pub mod panel; -pub mod project_panel; -pub mod recent_projects; -pub mod status_bar; -pub mod tab; -pub mod tab_bar; -pub mod terminal; -pub mod theme_selector; -pub mod title_bar; -pub mod toolbar; -pub mod traffic_lights; diff --git a/crates/storybook/src/stories/components/assistant_panel.rs b/crates/storybook/src/stories/components/assistant_panel.rs deleted file mode 100644 index 09f964756ea07e74173861a9f391587f8974ccef..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/assistant_panel.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::AssistantPanel; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct AssistantPanelStory {} - -impl AssistantPanelStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, AssistantPanel>(cx)) - .child(Story::label(cx, "Default")) - .child(AssistantPanel::new()) - } -} diff --git a/crates/storybook/src/stories/components/breadcrumb.rs b/crates/storybook/src/stories/components/breadcrumb.rs deleted file mode 100644 index 002b6140e13e98731ff40be7ddefcdc11ab4bf67..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/breadcrumb.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::path::PathBuf; -use std::str::FromStr; - -use ui::prelude::*; -use ui::{Breadcrumb, HighlightedText, Symbol}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct BreadcrumbStory {} - -impl BreadcrumbStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - Story::container(cx) - .child(Story::title_for::<_, Breadcrumb>(cx)) - .child(Story::label(cx, "Default")) - .child(Breadcrumb::new( - PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(), - vec![ - Symbol(vec![ - HighlightedText { - text: "impl ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "BreadcrumbStory".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - ]), - Symbol(vec![ - HighlightedText { - text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "render".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - ]), - ], - )) - } -} diff --git a/crates/storybook/src/stories/components/buffer.rs b/crates/storybook/src/stories/components/buffer.rs deleted file mode 100644 index 0b3268421bf3ace9a429c68fc23ff9f9baa16b47..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/buffer.rs +++ /dev/null @@ -1,36 +0,0 @@ -use gpui2::geometry::rems; -use ui::prelude::*; -use ui::{ - empty_buffer_example, hello_world_rust_buffer_example, - hello_world_rust_buffer_with_status_example, Buffer, -}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct BufferStory {} - -impl BufferStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - Story::container(cx) - .child(Story::title_for::<_, Buffer>(cx)) - .child(Story::label(cx, "Default")) - .child(div().w(rems(64.)).h_96().child(empty_buffer_example())) - .child(Story::label(cx, "Hello World (Rust)")) - .child( - div() - .w(rems(64.)) - .h_96() - .child(hello_world_rust_buffer_example(&theme)), - ) - .child(Story::label(cx, "Hello World (Rust) with Status")) - .child( - div() - .w(rems(64.)) - .h_96() - .child(hello_world_rust_buffer_with_status_example(&theme)), - ) - } -} diff --git a/crates/storybook/src/stories/components/chat_panel.rs b/crates/storybook/src/stories/components/chat_panel.rs deleted file mode 100644 index e87ac0afa29cc60fbb61a674517d8e4bb1381b29..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/chat_panel.rs +++ /dev/null @@ -1,46 +0,0 @@ -use chrono::DateTime; -use ui::prelude::*; -use ui::{ChatMessage, ChatPanel, Panel}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct ChatPanelStory {} - -impl ChatPanelStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, ChatPanel>(cx)) - .child(Story::label(cx, "Default")) - .child(Panel::new( - ScrollState::default(), - |_, _| vec![ChatPanel::new(ScrollState::default()).into_any()], - Box::new(()), - )) - .child(Story::label(cx, "With Mesages")) - .child(Panel::new( - ScrollState::default(), - |_, _| { - vec![ChatPanel::new(ScrollState::default()) - .with_messages(vec![ - ChatMessage::new( - "osiewicz".to_string(), - "is this thing on?".to_string(), - DateTime::parse_from_rfc3339("2023-09-27T15:40:52.707Z") - .unwrap() - .naive_local(), - ), - ChatMessage::new( - "maxdeviant".to_string(), - "Reading you loud and clear!".to_string(), - DateTime::parse_from_rfc3339("2023-09-28T15:40:52.707Z") - .unwrap() - .naive_local(), - ), - ]) - .into_any()] - }, - Box::new(()), - )) - } -} diff --git a/crates/storybook/src/stories/components/collab_panel.rs b/crates/storybook/src/stories/components/collab_panel.rs deleted file mode 100644 index 6a66f0d47f6671ff0edcaf492bfd92506fdf2097..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/collab_panel.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::CollabPanel; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct CollabPanelStory {} - -impl CollabPanelStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, CollabPanel>(cx)) - .child(Story::label(cx, "Default")) - .child(CollabPanel::new(ScrollState::default())) - } -} diff --git a/crates/storybook/src/stories/components/context_menu.rs b/crates/storybook/src/stories/components/context_menu.rs deleted file mode 100644 index 71776ca66a20eafcc551d44e458d0ea17cdbac38..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/context_menu.rs +++ /dev/null @@ -1,21 +0,0 @@ -use ui::prelude::*; -use ui::{ContextMenu, ContextMenuItem, Label}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct ContextMenuStory {} - -impl ContextMenuStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - //.fill(theme.middle.base.default.background) - .child(Story::title_for::<_, ContextMenu>(cx)) - .child(Story::label(cx, "Default")) - .child(ContextMenu::new([ - ContextMenuItem::header("Section header"), - ContextMenuItem::Separator, - ContextMenuItem::entry(Label::new("Some entry")), - ])) - } -} diff --git a/crates/storybook/src/stories/components/facepile.rs b/crates/storybook/src/stories/components/facepile.rs deleted file mode 100644 index bbd08ae984eb972fdb16134795fb458f5348a92f..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/facepile.rs +++ /dev/null @@ -1,25 +0,0 @@ -use ui::prelude::*; -use ui::{static_players, Facepile}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct FacepileStory {} - -impl FacepileStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let players = static_players(); - - Story::container(cx) - .child(Story::title_for::<_, Facepile>(cx)) - .child(Story::label(cx, "Default")) - .child( - div() - .flex() - .gap_3() - .child(Facepile::new(players.clone().into_iter().take(1))) - .child(Facepile::new(players.clone().into_iter().take(2))) - .child(Facepile::new(players.clone().into_iter().take(3))), - ) - } -} diff --git a/crates/storybook/src/stories/components/keybinding.rs b/crates/storybook/src/stories/components/keybinding.rs deleted file mode 100644 index 1acf59fe3bc17ef53d7fef586f3e40e4edf20ef0..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/keybinding.rs +++ /dev/null @@ -1,64 +0,0 @@ -use itertools::Itertools; -use strum::IntoEnumIterator; -use ui::prelude::*; -use ui::{Keybinding, ModifierKey, ModifierKeys}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct KeybindingStory {} - -impl KeybindingStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let all_modifier_permutations = ModifierKey::iter().permutations(2); - - Story::container(cx) - .child(Story::title_for::<_, Keybinding>(cx)) - .child(Story::label(cx, "Single Key")) - .child(Keybinding::new("Z".to_string(), ModifierKeys::new())) - .child(Story::label(cx, "Single Key with Modifier")) - .child( - div() - .flex() - .gap_3() - .children(ModifierKey::iter().map(|modifier| { - Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier)) - })), - ) - .child(Story::label(cx, "Single Key with Modifier (Permuted)")) - .child( - div().flex().flex_col().children( - all_modifier_permutations - .chunks(4) - .into_iter() - .map(|chunk| { - div() - .flex() - .gap_4() - .py_3() - .children(chunk.map(|permutation| { - let mut modifiers = ModifierKeys::new(); - - for modifier in permutation { - modifiers = modifiers.add(modifier); - } - - Keybinding::new("X".to_string(), modifiers) - })) - }), - ), - ) - .child(Story::label(cx, "Single Key with All Modifiers")) - .child(Keybinding::new("Z".to_string(), ModifierKeys::all())) - .child(Story::label(cx, "Chord")) - .child(Keybinding::new_chord( - ("A".to_string(), ModifierKeys::new()), - ("Z".to_string(), ModifierKeys::new()), - )) - .child(Story::label(cx, "Chord with Modifier")) - .child(Keybinding::new_chord( - ("A".to_string(), ModifierKeys::new().control(true)), - ("Z".to_string(), ModifierKeys::new().shift(true)), - )) - } -} diff --git a/crates/storybook/src/stories/components/language_selector.rs b/crates/storybook/src/stories/components/language_selector.rs deleted file mode 100644 index c6dbd13d3fec2434e83483b71b512c8f270d5395..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/language_selector.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::LanguageSelector; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct LanguageSelectorStory {} - -impl LanguageSelectorStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, LanguageSelector>(cx)) - .child(Story::label(cx, "Default")) - .child(LanguageSelector::new()) - } -} diff --git a/crates/storybook/src/stories/components/multi_buffer.rs b/crates/storybook/src/stories/components/multi_buffer.rs deleted file mode 100644 index cd760c54dcaeb04e5d917947d00e9cfb91768c76..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/multi_buffer.rs +++ /dev/null @@ -1,24 +0,0 @@ -use ui::prelude::*; -use ui::{hello_world_rust_buffer_example, MultiBuffer}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct MultiBufferStory {} - -impl MultiBufferStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - Story::container(cx) - .child(Story::title_for::<_, MultiBuffer>(cx)) - .child(Story::label(cx, "Default")) - .child(MultiBuffer::new(vec![ - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - hello_world_rust_buffer_example(&theme), - ])) - } -} diff --git a/crates/storybook/src/stories/components/palette.rs b/crates/storybook/src/stories/components/palette.rs deleted file mode 100644 index d14fac66975f87757e741d87eb92096802f5eeeb..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/palette.rs +++ /dev/null @@ -1,53 +0,0 @@ -use ui::prelude::*; -use ui::{Keybinding, ModifierKeys, Palette, PaletteItem}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct PaletteStory {} - -impl PaletteStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, Palette>(cx)) - .child(Story::label(cx, "Default")) - .child(Palette::new(ScrollState::default())) - .child(Story::label(cx, "With Items")) - .child( - Palette::new(ScrollState::default()) - .placeholder("Execute a command...") - .items(vec![ - PaletteItem::new("theme selector: toggle").keybinding( - Keybinding::new_chord( - ("k".to_string(), ModifierKeys::new().command(true)), - ("t".to_string(), ModifierKeys::new().command(true)), - ), - ), - PaletteItem::new("assistant: inline assist").keybinding(Keybinding::new( - "enter".to_string(), - ModifierKeys::new().command(true), - )), - PaletteItem::new("assistant: quote selection").keybinding(Keybinding::new( - ">".to_string(), - ModifierKeys::new().command(true), - )), - PaletteItem::new("assistant: toggle focus").keybinding(Keybinding::new( - "?".to_string(), - ModifierKeys::new().command(true), - )), - PaletteItem::new("auto update: check"), - PaletteItem::new("auto update: view release notes"), - PaletteItem::new("branches: open recent").keybinding(Keybinding::new( - "b".to_string(), - ModifierKeys::new().command(true).alt(true), - )), - PaletteItem::new("chat panel: toggle focus"), - PaletteItem::new("cli: install"), - PaletteItem::new("client: sign in"), - PaletteItem::new("client: sign out"), - PaletteItem::new("editor: cancel") - .keybinding(Keybinding::new("escape".to_string(), ModifierKeys::new())), - ]), - ) - } -} diff --git a/crates/storybook/src/stories/components/panel.rs b/crates/storybook/src/stories/components/panel.rs deleted file mode 100644 index 39a5ceafa26107822b4d36005818bf620b39efbb..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/panel.rs +++ /dev/null @@ -1,25 +0,0 @@ -use ui::prelude::*; -use ui::{Label, Panel}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct PanelStory {} - -impl PanelStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, Panel>(cx)) - .child(Story::label(cx, "Default")) - .child(Panel::new( - ScrollState::default(), - |_, _| { - vec![div() - .overflow_y_scroll(ScrollState::default()) - .children((0..100).map(|ix| Label::new(format!("Item {}", ix + 1)))) - .into_any()] - }, - Box::new(()), - )) - } -} diff --git a/crates/storybook/src/stories/components/project_panel.rs b/crates/storybook/src/stories/components/project_panel.rs deleted file mode 100644 index cba71cd21a58695075738679a5626cff5db2f59e..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/project_panel.rs +++ /dev/null @@ -1,20 +0,0 @@ -use ui::prelude::*; -use ui::{Panel, ProjectPanel}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct ProjectPanelStory {} - -impl ProjectPanelStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, ProjectPanel>(cx)) - .child(Story::label(cx, "Default")) - .child(Panel::new( - ScrollState::default(), - |_, _| vec![ProjectPanel::new(ScrollState::default()).into_any()], - Box::new(()), - )) - } -} diff --git a/crates/storybook/src/stories/components/recent_projects.rs b/crates/storybook/src/stories/components/recent_projects.rs deleted file mode 100644 index f9246546955a12d85ec693a98d2a52741ea4f279..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/recent_projects.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::RecentProjects; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct RecentProjectsStory {} - -impl RecentProjectsStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, RecentProjects>(cx)) - .child(Story::label(cx, "Default")) - .child(RecentProjects::new()) - } -} diff --git a/crates/storybook/src/stories/components/status_bar.rs b/crates/storybook/src/stories/components/status_bar.rs deleted file mode 100644 index ed3d047c6d8f0c8adb5546f097b31bb2f57786fd..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/status_bar.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::StatusBar; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct StatusBarStory {} - -impl StatusBarStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, StatusBar>(cx)) - .child(Story::label(cx, "Default")) - .child(StatusBar::new()) - } -} diff --git a/crates/storybook/src/stories/components/tab.rs b/crates/storybook/src/stories/components/tab.rs deleted file mode 100644 index 6a154ce644e554eef226fb1aaea21b990f2e6a9e..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/tab.rs +++ /dev/null @@ -1,91 +0,0 @@ -use strum::IntoEnumIterator; -use ui::prelude::*; -use ui::{h_stack, v_stack, Tab}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct TabStory {} - -impl TabStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let git_statuses = GitStatus::iter(); - let fs_statuses = FileSystemStatus::iter(); - - Story::container(cx) - .child(Story::title_for::<_, Tab>(cx)) - .child( - h_stack().child( - v_stack() - .gap_2() - .child(Story::label(cx, "Default")) - .child(Tab::new()), - ), - ) - .child( - h_stack().child( - v_stack().gap_2().child(Story::label(cx, "Current")).child( - h_stack() - .gap_4() - .child(Tab::new().title("Current".to_string()).current(true)) - .child(Tab::new().title("Not Current".to_string()).current(false)), - ), - ), - ) - .child( - h_stack().child( - v_stack() - .gap_2() - .child(Story::label(cx, "Titled")) - .child(Tab::new().title("label".to_string())), - ), - ) - .child( - h_stack().child( - v_stack() - .gap_2() - .child(Story::label(cx, "With Icon")) - .child( - Tab::new() - .title("label".to_string()) - .icon(Some(ui::Icon::Envelope)), - ), - ), - ) - .child( - h_stack().child( - v_stack() - .gap_2() - .child(Story::label(cx, "Close Side")) - .child( - h_stack() - .gap_4() - .child( - Tab::new() - .title("Left".to_string()) - .close_side(IconSide::Left), - ) - .child(Tab::new().title("Right".to_string())), - ), - ), - ) - .child( - v_stack() - .gap_2() - .child(Story::label(cx, "Git Status")) - .child(h_stack().gap_4().children(git_statuses.map(|git_status| { - Tab::new() - .title(git_status.to_string()) - .git_status(git_status) - }))), - ) - .child( - v_stack() - .gap_2() - .child(Story::label(cx, "File System Status")) - .child(h_stack().gap_4().children(fs_statuses.map(|fs_status| { - Tab::new().title(fs_status.to_string()).fs_status(fs_status) - }))), - ) - } -} diff --git a/crates/storybook/src/stories/components/tab_bar.rs b/crates/storybook/src/stories/components/tab_bar.rs deleted file mode 100644 index b5fa45dfd6009b941cddba9a2cb139cb55cd810b..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/tab_bar.rs +++ /dev/null @@ -1,46 +0,0 @@ -use ui::prelude::*; -use ui::{Tab, TabBar}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct TabBarStory {} - -impl TabBarStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, TabBar>(cx)) - .child(Story::label(cx, "Default")) - .child(TabBar::new(vec![ - Tab::new() - .title("Cargo.toml".to_string()) - .current(false) - .git_status(GitStatus::Modified), - Tab::new() - .title("Channels Panel".to_string()) - .current(false), - Tab::new() - .title("channels_panel.rs".to_string()) - .current(true) - .git_status(GitStatus::Modified), - Tab::new() - .title("workspace.rs".to_string()) - .current(false) - .git_status(GitStatus::Modified), - Tab::new() - .title("icon_button.rs".to_string()) - .current(false), - Tab::new() - .title("storybook.rs".to_string()) - .current(false) - .git_status(GitStatus::Created), - Tab::new().title("theme.rs".to_string()).current(false), - Tab::new() - .title("theme_registry.rs".to_string()) - .current(false), - Tab::new() - .title("styleable_helpers.rs".to_string()) - .current(false), - ])) - } -} diff --git a/crates/storybook/src/stories/components/terminal.rs b/crates/storybook/src/stories/components/terminal.rs deleted file mode 100644 index 2bce2e27e53c1919aa510f1b9cc0def4578ca3f5..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/terminal.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::Terminal; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct TerminalStory {} - -impl TerminalStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, Terminal>(cx)) - .child(Story::label(cx, "Default")) - .child(Terminal::new()) - } -} diff --git a/crates/storybook/src/stories/components/theme_selector.rs b/crates/storybook/src/stories/components/theme_selector.rs deleted file mode 100644 index 43e2a704e701e240a02b1bc18cb4bc8dbc575d10..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/theme_selector.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::ThemeSelector; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct ThemeSelectorStory {} - -impl ThemeSelectorStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, ThemeSelector>(cx)) - .child(Story::label(cx, "Default")) - .child(ThemeSelector::new()) - } -} diff --git a/crates/storybook/src/stories/components/title_bar.rs b/crates/storybook/src/stories/components/title_bar.rs deleted file mode 100644 index 3c4682b3ba0745ed1d8476d306878a683f22cb40..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/title_bar.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::TitleBar; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct TitleBarStory {} - -impl TitleBarStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, TitleBar>(cx)) - .child(Story::label(cx, "Default")) - .child(TitleBar::new(cx)) - } -} diff --git a/crates/storybook/src/stories/components/toolbar.rs b/crates/storybook/src/stories/components/toolbar.rs deleted file mode 100644 index 1413c463b4366a2326b195fd9e3472b7acc29d37..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/toolbar.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::Arc; - -use ui::prelude::*; -use ui::{theme, Breadcrumb, HighlightColor, HighlightedText, Icon, IconButton, Symbol, Toolbar}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct ToolbarStory {} - -impl ToolbarStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - struct LeftItemsPayload { - pub theme: Arc, - } - - Story::container(cx) - .child(Story::title_for::<_, Toolbar>(cx)) - .child(Story::label(cx, "Default")) - .child(Toolbar::new( - |_, payload| { - let payload = payload.downcast_ref::().unwrap(); - - let theme = payload.theme.clone(); - - vec![Breadcrumb::new( - PathBuf::from_str("crates/ui/src/components/toolbar.rs").unwrap(), - vec![ - Symbol(vec![ - HighlightedText { - text: "impl ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "ToolbarStory".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - ]), - Symbol(vec![ - HighlightedText { - text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "render".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - ]), - ], - ) - .into_any()] - }, - Box::new(LeftItemsPayload { - theme: theme.clone(), - }), - |_, _| { - vec![ - IconButton::new(Icon::InlayHint).into_any(), - IconButton::new(Icon::MagnifyingGlass).into_any(), - IconButton::new(Icon::MagicWand).into_any(), - ] - }, - Box::new(()), - )) - } -} diff --git a/crates/storybook/src/stories/components/traffic_lights.rs b/crates/storybook/src/stories/components/traffic_lights.rs deleted file mode 100644 index 3b759a43a37d3219e15f7a77ad5aa4c93d238b3a..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/components/traffic_lights.rs +++ /dev/null @@ -1,18 +0,0 @@ -use ui::prelude::*; -use ui::TrafficLights; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct TrafficLightsStory {} - -impl TrafficLightsStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, TrafficLights>(cx)) - .child(Story::label(cx, "Default")) - .child(TrafficLights::new()) - .child(Story::label(cx, "Unfocused")) - .child(TrafficLights::new().window_has_focus(false)) - } -} diff --git a/crates/storybook/src/stories/elements.rs b/crates/storybook/src/stories/elements.rs deleted file mode 100644 index f7afec4d883ce12d62df968862f9de3265cbad54..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/elements.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod avatar; -pub mod button; -pub mod icon; -pub mod input; -pub mod label; diff --git a/crates/storybook/src/stories/elements/avatar.rs b/crates/storybook/src/stories/elements/avatar.rs deleted file mode 100644 index a277fa6a1e8d5d43f1ee904afa0fe7585a276139..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/elements/avatar.rs +++ /dev/null @@ -1,23 +0,0 @@ -use ui::prelude::*; -use ui::Avatar; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct AvatarStory {} - -impl AvatarStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, Avatar>(cx)) - .child(Story::label(cx, "Default")) - .child(Avatar::new( - "https://avatars.githubusercontent.com/u/1714999?v=4", - )) - .child(Story::label(cx, "Rounded rectangle")) - .child( - Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4") - .shape(Shape::RoundedRectangle), - ) - } -} diff --git a/crates/storybook/src/stories/elements/button.rs b/crates/storybook/src/stories/elements/button.rs deleted file mode 100644 index 7ff3fdd30c55bf2b9ac7c1713f814c31b659efd5..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/elements/button.rs +++ /dev/null @@ -1,192 +0,0 @@ -use gpui2::elements::div; -use gpui2::geometry::rems; -use gpui2::{Element, IntoElement, ViewContext}; -use strum::IntoEnumIterator; -use ui::prelude::*; -use ui::{h_stack, v_stack, Button, Icon, IconPosition, Label}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct ButtonStory {} - -impl ButtonStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let states = InteractionState::iter(); - - Story::container(cx) - .child(Story::title_for::<_, Button>(cx)) - .child( - div() - .flex() - .gap_8() - .child( - div() - .child(Story::label(cx, "Ghost (Default)")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Ghost) - .state(state), - ) - }))) - .child(Story::label(cx, "Ghost – Left Icon")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Ghost) - .icon(Icon::Plus) - .icon_position(IconPosition::Left) - .state(state), - ) - }))) - .child(Story::label(cx, "Ghost – Right Icon")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Ghost) - .icon(Icon::Plus) - .icon_position(IconPosition::Right) - .state(state), - ) - }))), - ) - .child( - div() - .child(Story::label(cx, "Filled")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Filled) - .state(state), - ) - }))) - .child(Story::label(cx, "Filled – Left Button")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Filled) - .icon(Icon::Plus) - .icon_position(IconPosition::Left) - .state(state), - ) - }))) - .child(Story::label(cx, "Filled – Right Button")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Filled) - .icon(Icon::Plus) - .icon_position(IconPosition::Right) - .state(state), - ) - }))), - ) - .child( - div() - .child(Story::label(cx, "Fixed With")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Filled) - .state(state) - .width(Some(rems(6.).into())), - ) - }))) - .child(Story::label(cx, "Fixed With – Left Icon")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Filled) - .state(state) - .icon(Icon::Plus) - .icon_position(IconPosition::Left) - .width(Some(rems(6.).into())), - ) - }))) - .child(Story::label(cx, "Fixed With – Right Icon")) - .child(h_stack().gap_2().children(states.clone().map(|state| { - v_stack() - .gap_1() - .child( - Label::new(state.to_string()) - .color(ui::LabelColor::Muted) - .size(ui::LabelSize::Small), - ) - .child( - Button::new("Label") - .variant(ButtonVariant::Filled) - .state(state) - .icon(Icon::Plus) - .icon_position(IconPosition::Right) - .width(Some(rems(6.).into())), - ) - }))), - ), - ) - .child(Story::label(cx, "Button with `on_click`")) - .child( - Button::new("Label") - .variant(ButtonVariant::Ghost) - // NOTE: There currently appears to be a bug in GPUI2 where only the last event handler will fire. - // So adding additional buttons with `on_click`s after this one will cause this `on_click` to not fire. - .on_click(|_view, _cx| println!("Button clicked.")), - ) - } -} diff --git a/crates/storybook/src/stories/elements/icon.rs b/crates/storybook/src/stories/elements/icon.rs deleted file mode 100644 index 66d3abc0b34bf53d77959ad1371f2ffbc4b3e5b0..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/elements/icon.rs +++ /dev/null @@ -1,19 +0,0 @@ -use strum::IntoEnumIterator; -use ui::prelude::*; -use ui::{Icon, IconElement}; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct IconStory {} - -impl IconStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let icons = Icon::iter(); - - Story::container(cx) - .child(Story::title_for::<_, IconElement>(cx)) - .child(Story::label(cx, "All Icons")) - .child(div().flex().gap_3().children(icons.map(IconElement::new))) - } -} diff --git a/crates/storybook/src/stories/elements/input.rs b/crates/storybook/src/stories/elements/input.rs deleted file mode 100644 index 8617b7daaf54f76292619752ce123ea990416731..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/elements/input.rs +++ /dev/null @@ -1,16 +0,0 @@ -use ui::prelude::*; -use ui::Input; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct InputStory {} - -impl InputStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, Input>(cx)) - .child(Story::label(cx, "Default")) - .child(div().flex().child(Input::new("Search"))) - } -} diff --git a/crates/storybook/src/stories/elements/label.rs b/crates/storybook/src/stories/elements/label.rs deleted file mode 100644 index 1b63cb3a3a41103449555132f2e6bfe8c4a98a34..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/elements/label.rs +++ /dev/null @@ -1,18 +0,0 @@ -use ui::prelude::*; -use ui::Label; - -use crate::story::Story; - -#[derive(Element, Default)] -pub struct LabelStory {} - -impl LabelStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - Story::container(cx) - .child(Story::title_for::<_, Label>(cx)) - .child(Story::label(cx, "Default")) - .child(Label::new("Hello, world!")) - .child(Story::label(cx, "Highlighted")) - .child(Label::new("Hello, world!").with_highlights(vec![0, 1, 2, 7, 8, 12])) - } -} diff --git a/crates/storybook/src/stories/kitchen_sink.rs b/crates/storybook/src/stories/kitchen_sink.rs deleted file mode 100644 index ae826f934e8fac4b211086c848a94acff0f35128..0000000000000000000000000000000000000000 --- a/crates/storybook/src/stories/kitchen_sink.rs +++ /dev/null @@ -1,26 +0,0 @@ -use strum::IntoEnumIterator; -use ui::prelude::*; - -use crate::story::Story; -use crate::story_selector::{ComponentStory, ElementStory}; - -#[derive(Element, Default)] -pub struct KitchenSinkStory {} - -impl KitchenSinkStory { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let element_stories = ElementStory::iter().map(|selector| selector.story()); - let component_stories = ComponentStory::iter().map(|selector| selector.story()); - - Story::container(cx) - .overflow_y_scroll(ScrollState::default()) - .child(Story::title(cx, "Kitchen Sink")) - .child(Story::label(cx, "Elements")) - .child(div().flex().flex_col().children_any(element_stories)) - .child(Story::label(cx, "Components")) - .child(div().flex().flex_col().children_any(component_stories)) - // Add a bit of space at the bottom of the kitchen sink so elements - // don't end up squished right up against the bottom of the screen. - .child(div().p_4()) - } -} diff --git a/crates/storybook/src/story.rs b/crates/storybook/src/story.rs deleted file mode 100644 index 16eae50f889277f5a84e7602c588f33963e73271..0000000000000000000000000000000000000000 --- a/crates/storybook/src/story.rs +++ /dev/null @@ -1,44 +0,0 @@ -use gpui2::elements::div::Div; -use ui::prelude::*; -use ui::theme; - -pub struct Story {} - -impl Story { - pub fn container(cx: &mut ViewContext) -> Div { - let theme = theme(cx); - - div() - .size_full() - .flex() - .flex_col() - .pt_2() - .px_4() - .font("Zed Mono Extended") - .fill(theme.lowest.base.default.background) - } - - pub fn title(cx: &mut ViewContext, title: &str) -> impl Element { - let theme = theme(cx); - - div() - .text_xl() - .text_color(theme.lowest.base.default.foreground) - .child(title.to_owned()) - } - - pub fn title_for(cx: &mut ViewContext) -> impl Element { - Self::title(cx, std::any::type_name::()) - } - - pub fn label(cx: &mut ViewContext, label: &str) -> impl Element { - let theme = theme(cx); - - div() - .mt_4() - .mb_2() - .text_xs() - .text_color(theme.lowest.base.default.foreground) - .child(label.to_owned()) - } -} diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs deleted file mode 100644 index 6b0a9ac78dfeee992ee2ac7592f9102d340a28f1..0000000000000000000000000000000000000000 --- a/crates/storybook/src/story_selector.rs +++ /dev/null @@ -1,178 +0,0 @@ -use std::str::FromStr; -use std::sync::OnceLock; - -use anyhow::{anyhow, Context}; -use clap::builder::PossibleValue; -use clap::ValueEnum; -use gpui2::{AnyElement, Element}; -use strum::{EnumIter, EnumString, IntoEnumIterator}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)] -#[strum(serialize_all = "snake_case")] -pub enum ElementStory { - Avatar, - Button, - Icon, - Input, - Label, -} - -impl ElementStory { - pub fn story(&self) -> AnyElement { - use crate::stories::elements; - - match self { - Self::Avatar => elements::avatar::AvatarStory::default().into_any(), - Self::Button => elements::button::ButtonStory::default().into_any(), - Self::Icon => elements::icon::IconStory::default().into_any(), - Self::Input => elements::input::InputStory::default().into_any(), - Self::Label => elements::label::LabelStory::default().into_any(), - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)] -#[strum(serialize_all = "snake_case")] -pub enum ComponentStory { - AssistantPanel, - Breadcrumb, - Buffer, - ContextMenu, - ChatPanel, - CollabPanel, - Facepile, - Keybinding, - LanguageSelector, - MultiBuffer, - Palette, - Panel, - ProjectPanel, - RecentProjects, - StatusBar, - Tab, - TabBar, - Terminal, - ThemeSelector, - TitleBar, - Toolbar, - TrafficLights, -} - -impl ComponentStory { - pub fn story(&self) -> AnyElement { - use crate::stories::components; - - match self { - Self::AssistantPanel => { - components::assistant_panel::AssistantPanelStory::default().into_any() - } - Self::Breadcrumb => components::breadcrumb::BreadcrumbStory::default().into_any(), - Self::Buffer => components::buffer::BufferStory::default().into_any(), - Self::ContextMenu => components::context_menu::ContextMenuStory::default().into_any(), - Self::ChatPanel => components::chat_panel::ChatPanelStory::default().into_any(), - Self::CollabPanel => components::collab_panel::CollabPanelStory::default().into_any(), - Self::Facepile => components::facepile::FacepileStory::default().into_any(), - Self::Keybinding => components::keybinding::KeybindingStory::default().into_any(), - Self::LanguageSelector => { - components::language_selector::LanguageSelectorStory::default().into_any() - } - Self::MultiBuffer => components::multi_buffer::MultiBufferStory::default().into_any(), - Self::Palette => components::palette::PaletteStory::default().into_any(), - Self::Panel => components::panel::PanelStory::default().into_any(), - Self::ProjectPanel => { - components::project_panel::ProjectPanelStory::default().into_any() - } - Self::RecentProjects => { - components::recent_projects::RecentProjectsStory::default().into_any() - } - Self::StatusBar => components::status_bar::StatusBarStory::default().into_any(), - Self::Tab => components::tab::TabStory::default().into_any(), - Self::TabBar => components::tab_bar::TabBarStory::default().into_any(), - Self::Terminal => components::terminal::TerminalStory::default().into_any(), - Self::ThemeSelector => { - components::theme_selector::ThemeSelectorStory::default().into_any() - } - Self::TitleBar => components::title_bar::TitleBarStory::default().into_any(), - Self::Toolbar => components::toolbar::ToolbarStory::default().into_any(), - Self::TrafficLights => { - components::traffic_lights::TrafficLightsStory::default().into_any() - } - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum StorySelector { - Element(ElementStory), - Component(ComponentStory), - KitchenSink, -} - -impl FromStr for StorySelector { - type Err = anyhow::Error; - - fn from_str(raw_story_name: &str) -> std::result::Result { - let story = raw_story_name.to_ascii_lowercase(); - - if story == "kitchen_sink" { - return Ok(Self::KitchenSink); - } - - if let Some((_, story)) = story.split_once("elements/") { - let element_story = ElementStory::from_str(story) - .with_context(|| format!("story not found for element '{story}'"))?; - - return Ok(Self::Element(element_story)); - } - - if let Some((_, story)) = story.split_once("components/") { - let component_story = ComponentStory::from_str(story) - .with_context(|| format!("story not found for component '{story}'"))?; - - return Ok(Self::Component(component_story)); - } - - Err(anyhow!("story not found for '{raw_story_name}'")) - } -} - -impl StorySelector { - pub fn story(&self) -> AnyElement { - match self { - Self::Element(element_story) => element_story.story(), - Self::Component(component_story) => component_story.story(), - Self::KitchenSink => { - crate::stories::kitchen_sink::KitchenSinkStory::default().into_any() - } - } - } -} - -/// The list of all stories available in the storybook. -static ALL_STORY_SELECTORS: OnceLock> = OnceLock::new(); - -impl ValueEnum for StorySelector { - fn value_variants<'a>() -> &'a [Self] { - let stories = ALL_STORY_SELECTORS.get_or_init(|| { - let element_stories = ElementStory::iter().map(StorySelector::Element); - let component_stories = ComponentStory::iter().map(StorySelector::Component); - - element_stories - .chain(component_stories) - .chain(std::iter::once(StorySelector::KitchenSink)) - .collect::>() - }); - - stories - } - - fn to_possible_value(&self) -> Option { - let value = match self { - Self::Element(story) => format!("elements/{story}"), - Self::Component(story) => format!("components/{story}"), - Self::KitchenSink => "kitchen_sink".to_string(), - }; - - Some(PossibleValue::new(value)) - } -} diff --git a/crates/storybook/src/storybook.rs b/crates/storybook/src/storybook.rs deleted file mode 100644 index afae0d5ebe0a82f6f8b2dd8f8934c032ad61ddd6..0000000000000000000000000000000000000000 --- a/crates/storybook/src/storybook.rs +++ /dev/null @@ -1,198 +0,0 @@ -#![allow(dead_code, unused_variables)] - -mod stories; -mod story; -mod story_selector; - -use std::{process::Command, sync::Arc}; - -use ::theme as legacy_theme; -use clap::Parser; -use gpui2::{ - serde_json, vec2f, view, Element, IntoElement, ParentElement, RectF, ViewContext, WindowBounds, -}; -use legacy_theme::{ThemeRegistry, ThemeSettings}; -use log::LevelFilter; -use settings::{default_settings, SettingsStore}; -use simplelog::SimpleLogger; -use ui::prelude::*; -use ui::{ElementExt, Theme, WorkspaceElement}; - -use crate::story_selector::StorySelector; - -gpui2::actions! { - storybook, - [ToggleInspector] -} - -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -struct Args { - #[arg(value_enum)] - story: Option, - - /// The name of the theme to use in the storybook. - /// - /// If not provided, the default theme will be used. - #[arg(long)] - theme: Option, -} - -async fn watch_zed_changes(fs: Arc) -> Option<()> { - if std::env::var("ZED_HOT_RELOAD").is_err() { - return None; - } - use futures::StreamExt; - let mut events = fs - .watch(".".as_ref(), std::time::Duration::from_millis(100)) - .await; - let mut current_child: Option = None; - while let Some(events) = events.next().await { - if !events.iter().any(|event| { - event - .path - .to_str() - .map(|path| path.contains("/crates/")) - .unwrap_or_default() - }) { - continue; - } - let child = current_child.take().map(|mut child| child.kill()); - log::info!("Storybook changed, rebuilding..."); - current_child = Some( - Command::new("cargo") - .args(["run", "-p", "storybook"]) - .spawn() - .ok()?, - ); - } - Some(()) -} - -fn main() { - SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - - let args = Args::parse(); - - let fs = Arc::new(fs::RealFs); - - gpui2::App::new(Assets).unwrap().run(move |cx| { - let mut store = SettingsStore::default(); - store - .set_default_settings(default_settings().as_ref(), cx) - .unwrap(); - cx.set_global(store); - legacy_theme::init(Assets, cx); - // load_embedded_fonts(cx.platform().as_ref()); - - let theme_registry = cx.global::>(); - - let theme_override = args - .theme - .and_then(|theme| { - theme_registry - .list_names(true) - .find(|known_theme| theme == *known_theme) - }) - .and_then(|theme_name| theme_registry.get(&theme_name).ok()); - - cx.spawn(|_| async move { - watch_zed_changes(fs).await; - }) - .detach(); - cx.add_window( - gpui2::WindowOptions { - bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), vec2f(1700., 980.))), - center: true, - ..Default::default() - }, - |cx| match args.story { - Some(selector) => view(move |cx| { - render_story( - &mut ViewContext::new(cx), - theme_override.clone(), - div().flex().flex_col().h_full().child_any(selector.story()), - ) - }), - None => view(move |cx| { - render_story( - &mut ViewContext::new(cx), - theme_override.clone(), - WorkspaceElement::default(), - ) - }), - }, - ); - cx.platform().activate(true); - }); -} - -fn render_story>( - cx: &mut ViewContext, - theme_override: Option>, - story: S, -) -> impl Element { - let theme = current_theme(cx, theme_override); - - story.into_element().themed(theme) -} - -fn current_theme( - cx: &mut ViewContext, - theme_override: Option>, -) -> Theme { - let legacy_theme = - theme_override.unwrap_or_else(|| settings::get::(cx).theme.clone()); - - let new_theme: Theme = serde_json::from_value(legacy_theme.base_theme.clone()).unwrap(); - - add_base_theme_to_legacy_theme(&legacy_theme, new_theme) -} - -// Nathan: During the transition to gpui2, we will include the base theme on the legacy Theme struct. -fn add_base_theme_to_legacy_theme(legacy_theme: &legacy_theme::Theme, new_theme: Theme) -> Theme { - legacy_theme - .deserialized_base_theme - .lock() - .get_or_insert_with(|| Box::new(new_theme)) - .downcast_ref::() - .unwrap() - .clone() -} - -use anyhow::{anyhow, Result}; -use gpui2::AssetSource; -use rust_embed::RustEmbed; - -#[derive(RustEmbed)] -#[folder = "../../assets"] -#[include = "themes/**/*"] -#[include = "fonts/**/*"] -#[include = "icons/**/*"] -#[exclude = "*.DS_Store"] -pub struct Assets; - -impl AssetSource for Assets { - fn load(&self, path: &str) -> Result> { - Self::get(path) - .map(|f| f.data) - .ok_or_else(|| anyhow!("could not find asset at path \"{}\"", path)) - } - - fn list(&self, path: &str) -> Vec> { - Self::iter().filter(|p| p.starts_with(path)).collect() - } -} - -// fn load_embedded_fonts(platform: &dyn gpui2::Platform) { -// let font_paths = Assets.list("fonts"); -// let mut embedded_fonts = Vec::new(); -// for font_path in &font_paths { -// if font_path.ends_with(".ttf") { -// let font_path = &*font_path; -// let font_bytes = Assets.load(font_path).unwrap().to_vec(); -// embedded_fonts.push(Arc::from(font_bytes)); -// } -// } -// platform.fonts().add_fonts(&embedded_fonts).unwrap(); -// } diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index cd939b5604716a1b6c0f523db53acce735cc9ac1..5a13efd07a0da4b8490444b3322bfc4600b70e17 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -150,11 +150,14 @@ impl TerminalView { cx.notify(); cx.emit(Event::Wakeup); } + Event::Bell => { this.has_bell = true; cx.emit(Event::Wakeup); } + Event::BlinkChanged => this.blinking_on = !this.blinking_on, + Event::TitleChanged => { if let Some(foreground_info) = &this.terminal().read(cx).foreground_process_info { let cwd = foreground_info.cwd.clone(); @@ -171,6 +174,7 @@ impl TerminalView { .detach(); } } + Event::NewNavigationTarget(maybe_navigation_target) => { this.can_navigate_to_selected_word = match maybe_navigation_target { Some(MaybeNavigationTarget::Url(_)) => true, @@ -180,8 +184,10 @@ impl TerminalView { None => false, } } + Event::Open(maybe_navigation_target) => match maybe_navigation_target { MaybeNavigationTarget::Url(url) => cx.platform().open_url(url), + MaybeNavigationTarget::PathLike(maybe_path) => { if !this.can_navigate_to_selected_word { return; @@ -246,6 +252,7 @@ impl TerminalView { } } }, + _ => cx.emit(event.clone()), }) .detach(); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index fa3db613288f162372ba200a073a2007bb01a6ce..6b73e5d3645d835f91e2f94ba84329dd35655b42 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -869,9 +869,13 @@ pub struct AutocompleteStyle { pub selected_item: ContainerStyle, pub hovered_item: ContainerStyle, pub match_highlight: HighlightStyle, - pub server_name_container: ContainerStyle, - pub server_name_color: Color, - pub server_name_size_percent: f32, + pub completion_min_width: f32, + pub completion_max_width: f32, + pub inline_docs_container: ContainerStyle, + pub inline_docs_color: Color, + pub inline_docs_size_percent: f32, + pub alongside_docs_max_width: f32, + pub alongside_docs_container: ContainerStyle, } #[derive(Clone, Copy, Default, Deserialize, JsonSchema)] diff --git a/crates/ui/Cargo.toml b/crates/ui/Cargo.toml deleted file mode 100644 index 7bd9d912a0fb05ae0b921e3e5c7b7f74debb984e..0000000000000000000000000000000000000000 --- a/crates/ui/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "ui" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -anyhow.workspace = true -chrono = "0.4" -gpui2 = { path = "../gpui2" } -serde.workspace = true -settings = { path = "../settings" } -smallvec.workspace = true -strum = { version = "0.25.0", features = ["derive"] } -theme = { path = "../theme" } -rand = "0.8" diff --git a/crates/ui/docs/_project.md b/crates/ui/docs/_project.md deleted file mode 100644 index a3b72a9d615892d1e185a883d39482d9a84672ce..0000000000000000000000000000000000000000 --- a/crates/ui/docs/_project.md +++ /dev/null @@ -1,13 +0,0 @@ -## Project Plan - -- Port existing UI to GPUI2 -- Update UI in places that GPUI1 was limiting us* -- Understand the needs &/|| struggles the engineers have been having with building UI in the past and address as many of those as possible as we go -- Ship a simple, straightforward system with documentation that is easy to use to build UI - -## Component Classification - -To simplify the understanding of components and minimize unnecessary cognitive load, let's categorize components into two types: - -- An element refers to a standalone component that doesn't import any other 'ui' components. -- A component indicates a component that utilizes or imports other 'ui' components. diff --git a/crates/ui/docs/elevation.md b/crates/ui/docs/elevation.md deleted file mode 100644 index bd34de3396dea1f1042bb267f8a23abe6a45441f..0000000000000000000000000000000000000000 --- a/crates/ui/docs/elevation.md +++ /dev/null @@ -1,57 +0,0 @@ -# Elevation - -Elevation in Zed applies to all surfaces and components. Elevation is categorized into levels. - -Elevation accomplishes the following: -- Allows surfaces to move in front of or behind others, such as content scrolling beneath app top bars. -- Reflects spatial relationships, for instance, how a floating action button’s shadow intimates its disconnection from a collection of cards. -- Directs attention to structures at the highest elevation, like a temporary dialog arising in front of other surfaces. - -Elevations are the initial elevation values assigned to components by default. - -Components may transition to a higher elevation in some cases, like user interations. - -On such occasions, components transition to predetermined dynamic elevation offsets. These are the typical elevations to which components move when they are not at rest. - -## Understanding Elevation - -Elevation can be thought of as the physical closeness of an element to the user. Elements with lower elevations are physically further away from the user on the z-axis and appear to be underneath elements with higher elevations. - -Material Design 3 has a some great visualizations of elevation that may be helpful to understanding the mental modal of elevation. [Material Design – Elevation](https://m3.material.io/styles/elevation/overview) - -## Elevation Levels - -Zed integrates six unique elevation levels in its design system. The elevation of a surface is expressed as a whole number ranging from 0 to 5, both numbers inclusive. A component’s elevation is ascertained by combining the component’s resting elevation with any dynamic elevation offsets. - -The levels are detailed as follows: - -0. App Background -1. UI Surface -2. Elevated Elements -3. Wash -4. Focused Element -5. Dragged Element - -### 0. App Background - -The app background constitutes the lowest elevation layer, appearing behind all other surfaces and components. It is predominantly used for the background color of the app. - -### 1. UI Surface - -The UI Surface is the standard elevation for components and is placed above the app background. It is generally used for the background color of the app bar, card, and sheet. - -### 2. Elevated Elements - -Elevated elements appear above the UI surface layer surfaces and components. Elevated elements are predominantly used for creating popovers, context menus, and tooltips. - -### 3. Wash - -Wash denotes a distinct elevation reserved to isolate app UI layers from high elevation components such as modals, notifications, and overlaid panels. The wash may not consistently be visible when these components are active. This layer is often referred to as a scrim or overlay and the background color of the wash is typically deployed in its design. - -### 4. Focused Element - -Focused elements obtain a higher elevation above surfaces and components at wash elevation. They are often used for modals, notifications, and overlaid panels and indicate that they are the sole element the user is interacting with at the moment. - -### 5. Dragged Element - -Dragged elements gain the highest elevation, thus appearing above surfaces and components at the elevation of focused elements. These are typically used for elements that are being dragged, following the cursor diff --git a/crates/ui/src/children.rs b/crates/ui/src/children.rs deleted file mode 100644 index 947f8d98cfc07421a38ac6927ffe756a691b8fed..0000000000000000000000000000000000000000 --- a/crates/ui/src/children.rs +++ /dev/null @@ -1,7 +0,0 @@ -use std::any::Any; - -use gpui2::{AnyElement, ViewContext}; - -pub type HackyChildren = fn(&mut ViewContext, &dyn Any) -> Vec>; - -pub type HackyChildrenPayload = Box; diff --git a/crates/ui/src/components.rs b/crates/ui/src/components.rs deleted file mode 100644 index 65b021856588c0bd881a561eb9e56683e8dd4c4c..0000000000000000000000000000000000000000 --- a/crates/ui/src/components.rs +++ /dev/null @@ -1,163 +0,0 @@ -mod assistant_panel; -mod breadcrumb; -mod buffer; -mod chat_panel; -mod collab_panel; -mod command_palette; -mod context_menu; -mod editor_pane; -mod facepile; -mod icon_button; -mod keybinding; -mod language_selector; -mod list; -mod multi_buffer; -mod palette; -mod panel; -mod panes; -mod player_stack; -mod project_panel; -mod recent_projects; -mod status_bar; -mod tab; -mod tab_bar; -mod terminal; -mod theme_selector; -mod title_bar; -mod toast; -mod toolbar; -mod traffic_lights; -mod workspace; - -pub use assistant_panel::*; -pub use breadcrumb::*; -pub use buffer::*; -pub use chat_panel::*; -pub use collab_panel::*; -pub use command_palette::*; -pub use context_menu::*; -pub use editor_pane::*; -pub use facepile::*; -pub use icon_button::*; -pub use keybinding::*; -pub use language_selector::*; -pub use list::*; -pub use multi_buffer::*; -pub use palette::*; -pub use panel::*; -pub use panes::*; -pub use player_stack::*; -pub use project_panel::*; -pub use recent_projects::*; -pub use status_bar::*; -pub use tab::*; -pub use tab_bar::*; -pub use terminal::*; -pub use theme_selector::*; -pub use title_bar::*; -pub use toast::*; -pub use toolbar::*; -pub use traffic_lights::*; -pub use workspace::*; - -// Nate: Commenting this out for now, unsure if we need it. - -// use std::marker::PhantomData; -// use std::rc::Rc; - -// use gpui2::elements::div; -// use gpui2::interactive::Interactive; -// use gpui2::platform::MouseButton; -// use gpui2::{ArcCow, Element, EventContext, IntoElement, ParentElement, ViewContext}; - -// struct ButtonHandlers { -// click: Option)>>, -// } - -// impl Default for ButtonHandlers { -// fn default() -> Self { -// Self { click: None } -// } -// } - -// #[derive(Element)] -// pub struct Button { -// handlers: ButtonHandlers, -// label: Option>, -// icon: Option>, -// data: Rc, -// view_type: PhantomData, -// } - -// // Impl block for buttons without data. -// // See below for an impl block for any button. -// impl Button { -// fn new() -> Self { -// Self { -// handlers: ButtonHandlers::default(), -// label: None, -// icon: None, -// data: Rc::new(()), -// view_type: PhantomData, -// } -// } - -// pub fn data(self, data: D) -> Button { -// Button { -// handlers: ButtonHandlers::default(), -// label: self.label, -// icon: self.icon, -// data: Rc::new(data), -// view_type: PhantomData, -// } -// } -// } - -// // Impl block for button regardless of its data type. -// impl Button { -// pub fn label(mut self, label: impl Into>) -> Self { -// self.label = Some(label.into()); -// self -// } - -// pub fn icon(mut self, icon: impl Into>) -> Self { -// self.icon = Some(icon.into()); -// self -// } - -// pub fn on_click( -// mut self, -// handler: impl Fn(&mut V, &D, &mut EventContext) + 'static, -// ) -> Self { -// self.handlers.click = Some(Rc::new(handler)); -// self -// } -// } - -// pub fn button() -> Button { -// Button::new() -// } - -// impl Button { -// fn render( -// &mut self, -// view: &mut V, -// cx: &mut ViewContext, -// ) -> impl IntoElement + Interactive { -// // let colors = &cx.theme::().colors; - -// let button = div() -// // .fill(colors.error(0.5)) -// .h_4() -// .children(self.label.clone()); - -// if let Some(handler) = self.handlers.click.clone() { -// let data = self.data.clone(); -// button.on_mouse_down(MouseButton::Left, move |view, event, cx| { -// handler(view, data.as_ref(), cx) -// }) -// } else { -// button -// } -// } -// } diff --git a/crates/ui/src/components/assistant_panel.rs b/crates/ui/src/components/assistant_panel.rs deleted file mode 100644 index a0a0c5288219b30bdb9353fed38a9a11b389a44b..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/assistant_panel.rs +++ /dev/null @@ -1,91 +0,0 @@ -use std::marker::PhantomData; - -use gpui2::geometry::rems; - -use crate::prelude::*; -use crate::theme::theme; -use crate::{Icon, IconButton, Label, Panel, PanelSide}; - -#[derive(Element)] -pub struct AssistantPanel { - view_type: PhantomData, - scroll_state: ScrollState, - current_side: PanelSide, -} - -impl AssistantPanel { - pub fn new() -> Self { - Self { - view_type: PhantomData, - scroll_state: ScrollState::default(), - current_side: PanelSide::default(), - } - } - - pub fn side(mut self, side: PanelSide) -> Self { - self.current_side = side; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - struct PanelPayload { - pub scroll_state: ScrollState, - } - - Panel::new( - self.scroll_state.clone(), - |_, payload| { - let payload = payload.downcast_ref::().unwrap(); - - vec![div() - .flex() - .flex_col() - .h_full() - .px_2() - .gap_2() - // Header - .child( - div() - .flex() - .justify_between() - .gap_2() - .child( - div() - .flex() - .child(IconButton::new(Icon::Menu)) - .child(Label::new("New Conversation")), - ) - .child( - div() - .flex() - .items_center() - .gap_px() - .child(IconButton::new(Icon::SplitMessage)) - .child(IconButton::new(Icon::Quote)) - .child(IconButton::new(Icon::MagicWand)) - .child(IconButton::new(Icon::Plus)) - .child(IconButton::new(Icon::Maximize)), - ), - ) - // Chat Body - .child( - div() - .w_full() - .flex() - .flex_col() - .gap_3() - .overflow_y_scroll(payload.scroll_state.clone()) - .child(Label::new("Is this thing on?")), - ) - .into_any()] - }, - Box::new(PanelPayload { - scroll_state: self.scroll_state.clone(), - }), - ) - .side(self.current_side) - .width(rems(32.)) - } -} diff --git a/crates/ui/src/components/breadcrumb.rs b/crates/ui/src/components/breadcrumb.rs deleted file mode 100644 index c14e89ee7b6793196d55d15b391a43d51e307873..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/breadcrumb.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::path::PathBuf; - -use gpui2::elements::div::Div; - -use crate::{h_stack, theme}; -use crate::{prelude::*, HighlightedText}; - -#[derive(Clone)] -pub struct Symbol(pub Vec); - -#[derive(Element)] -pub struct Breadcrumb { - path: PathBuf, - symbols: Vec, -} - -impl Breadcrumb { - pub fn new(path: PathBuf, symbols: Vec) -> Self { - Self { path, symbols } - } - - fn render_separator(&self, theme: &Theme) -> Div { - div() - .child(" › ") - .text_color(HighlightColor::Default.hsla(theme)) - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - let symbols_len = self.symbols.len(); - - h_stack() - .px_1() - // TODO: Read font from theme (or settings?). - .font("Zed Mono Extended") - .text_sm() - .text_color(theme.middle.base.default.foreground) - .rounded_md() - .hover() - .fill(theme.highest.base.hovered.background) - .child(self.path.clone().to_str().unwrap().to_string()) - .child(if !self.symbols.is_empty() { - self.render_separator(&theme) - } else { - div() - }) - .child( - div().flex().children( - self.symbols - .iter() - .enumerate() - // TODO: Could use something like `intersperse` here instead. - .flat_map(|(ix, symbol)| { - let mut items = - vec![div().flex().children(symbol.0.iter().map(|segment| { - div().child(segment.text.clone()).text_color(segment.color) - }))]; - - let is_last_segment = ix == symbols_len - 1; - if !is_last_segment { - items.push(self.render_separator(&theme)); - } - - items - }) - .collect::>(), - ), - ) - } -} diff --git a/crates/ui/src/components/buffer.rs b/crates/ui/src/components/buffer.rs deleted file mode 100644 index 00e5daee55c5d5c542daf7f1ed3d8418b0662a13..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/buffer.rs +++ /dev/null @@ -1,233 +0,0 @@ -use gpui2::{Hsla, WindowContext}; - -use crate::prelude::*; -use crate::{h_stack, theme, v_stack, Icon, IconElement}; - -#[derive(Default, PartialEq, Copy, Clone)] -pub struct PlayerCursor { - color: Hsla, - index: usize, -} - -#[derive(Default, PartialEq, Clone)] -pub struct HighlightedText { - pub text: String, - pub color: Hsla, -} - -#[derive(Default, PartialEq, Clone)] -pub struct HighlightedLine { - pub highlighted_texts: Vec, -} - -#[derive(Default, PartialEq, Clone)] -pub struct BufferRow { - pub line_number: usize, - pub code_action: bool, - pub current: bool, - pub line: Option, - pub cursors: Option>, - pub status: GitStatus, - pub show_line_number: bool, -} - -#[derive(Clone)] -pub struct BufferRows { - pub show_line_numbers: bool, - pub rows: Vec, -} - -impl Default for BufferRows { - fn default() -> Self { - Self { - show_line_numbers: true, - rows: vec![BufferRow { - line_number: 1, - code_action: false, - current: true, - line: None, - cursors: None, - status: GitStatus::None, - show_line_number: true, - }], - } - } -} - -impl BufferRow { - pub fn new(line_number: usize) -> Self { - Self { - line_number, - code_action: false, - current: false, - line: None, - cursors: None, - status: GitStatus::None, - show_line_number: true, - } - } - - pub fn set_line(mut self, line: Option) -> Self { - self.line = line; - self - } - - pub fn set_cursors(mut self, cursors: Option>) -> Self { - self.cursors = cursors; - self - } - - pub fn add_cursor(mut self, cursor: PlayerCursor) -> Self { - if let Some(cursors) = &mut self.cursors { - cursors.push(cursor); - } else { - self.cursors = Some(vec![cursor]); - } - self - } - - pub fn set_status(mut self, status: GitStatus) -> Self { - self.status = status; - self - } - - pub fn set_show_line_number(mut self, show_line_number: bool) -> Self { - self.show_line_number = show_line_number; - self - } - - pub fn set_code_action(mut self, code_action: bool) -> Self { - self.code_action = code_action; - self - } - - pub fn set_current(mut self, current: bool) -> Self { - self.current = current; - self - } -} - -#[derive(Element, Clone)] -pub struct Buffer { - scroll_state: ScrollState, - rows: Option, - readonly: bool, - language: Option, - title: Option, - path: Option, -} - -impl Buffer { - pub fn new() -> Self { - Self { - scroll_state: ScrollState::default(), - rows: Some(BufferRows::default()), - readonly: false, - language: None, - title: Some("untitled".to_string()), - path: None, - } - } - - pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) { - self.scroll_state = scroll_state; - } - - pub fn set_title>>(mut self, title: T) -> Self { - self.title = title.into(); - self - } - - pub fn set_path>>(mut self, path: P) -> Self { - self.path = path.into(); - self - } - - pub fn set_readonly(mut self, readonly: bool) -> Self { - self.readonly = readonly; - self - } - - pub fn set_rows>>(mut self, rows: R) -> Self { - self.rows = rows.into(); - self - } - - pub fn set_language>>(mut self, language: L) -> Self { - self.language = language.into(); - self - } - - fn render_row(row: BufferRow, cx: &WindowContext) -> impl IntoElement { - let theme = theme(cx); - let system_color = SystemColor::new(); - - let line_background = if row.current { - theme.middle.base.default.background - } else { - system_color.transparent - }; - - let line_number_color = if row.current { - HighlightColor::Default.hsla(&theme) - } else { - HighlightColor::Comment.hsla(&theme) - }; - - h_stack() - .fill(line_background) - .w_full() - .gap_2() - .px_1() - .child( - h_stack() - .w_4() - .h_full() - .px_0p5() - .when(row.code_action, |c| { - div().child(IconElement::new(Icon::Bolt)) - }), - ) - .when(row.show_line_number, |this| { - this.child( - h_stack().justify_end().px_0p5().w_3().child( - div() - .text_color(line_number_color) - .child(row.line_number.to_string()), - ), - ) - }) - .child(div().mx_0p5().w_1().h_full().fill(row.status.hsla(cx))) - .children(row.line.map(|line| { - div() - .flex() - .children(line.highlighted_texts.iter().map(|highlighted_text| { - div() - .text_color(highlighted_text.color) - .child(highlighted_text.text.clone()) - })) - })) - } - - fn render_rows(&self, cx: &WindowContext) -> Vec> { - match &self.rows { - Some(rows) => rows - .rows - .iter() - .map(|row| Self::render_row(row.clone(), cx)) - .collect(), - None => vec![], - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let rows = self.render_rows(cx); - v_stack() - .flex_1() - .w_full() - .h_full() - .fill(theme.highest.base.default.background) - .children(rows) - } -} diff --git a/crates/ui/src/components/chat_panel.rs b/crates/ui/src/components/chat_panel.rs deleted file mode 100644 index 5ae66967b62518b62bd668da58308ab5f6c6d155..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/chat_panel.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::marker::PhantomData; - -use chrono::NaiveDateTime; - -use crate::prelude::*; -use crate::theme::theme; -use crate::{Icon, IconButton, Input, Label, LabelColor}; - -#[derive(Element)] -pub struct ChatPanel { - view_type: PhantomData, - scroll_state: ScrollState, - messages: Vec, -} - -impl ChatPanel { - pub fn new(scroll_state: ScrollState) -> Self { - Self { - view_type: PhantomData, - scroll_state, - messages: Vec::new(), - } - } - - pub fn with_messages(mut self, messages: Vec) -> Self { - self.messages = messages; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .flex() - .flex_col() - .justify_between() - .h_full() - .px_2() - .gap_2() - // Header - .child( - div() - .flex() - .justify_between() - .py_2() - .child(div().flex().child(Label::new("#design"))) - .child( - div() - .flex() - .items_center() - .gap_px() - .child(IconButton::new(Icon::File)) - .child(IconButton::new(Icon::AudioOn)), - ), - ) - .child( - div() - .flex() - .flex_col() - // Chat Body - .child( - div() - .w_full() - .flex() - .flex_col() - .gap_3() - .overflow_y_scroll(self.scroll_state.clone()) - .children(self.messages.clone()), - ) - // Composer - .child(div().flex().my_2().child(Input::new("Message #design"))), - ) - } -} - -#[derive(Element, Clone)] -pub struct ChatMessage { - author: String, - text: String, - sent_at: NaiveDateTime, -} - -impl ChatMessage { - pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self { - Self { - author, - text, - sent_at, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div() - .flex() - .flex_col() - .child( - div() - .flex() - .gap_2() - .child(Label::new(self.author.clone())) - .child( - Label::new(self.sent_at.format("%m/%d/%Y").to_string()) - .color(LabelColor::Muted), - ), - ) - .child(div().child(Label::new(self.text.clone()))) - } -} diff --git a/crates/ui/src/components/collab_panel.rs b/crates/ui/src/components/collab_panel.rs deleted file mode 100644 index 14dd43294f838638e0361b954b65777c117a19b0..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/collab_panel.rs +++ /dev/null @@ -1,161 +0,0 @@ -use std::marker::PhantomData; - -use gpui2::elements::{img, svg}; -use gpui2::ArcCow; - -use crate::prelude::*; -use crate::theme::{theme, Theme}; -use crate::{ - static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon, List, - ListHeader, ToggleState, -}; - -#[derive(Element)] -pub struct CollabPanel { - view_type: PhantomData, - scroll_state: ScrollState, -} - -impl CollabPanel { - pub fn new(scroll_state: ScrollState) -> Self { - Self { - view_type: PhantomData, - scroll_state, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - v_stack() - .w_64() - .h_full() - .fill(theme.middle.base.default.background) - .child( - v_stack() - .w_full() - .overflow_y_scroll(self.scroll_state.clone()) - .child( - div() - .fill(theme.lowest.base.default.background) - .pb_1() - .border_color(theme.lowest.base.default.border) - .border_b() - .child( - List::new(static_collab_panel_current_call()) - .header( - ListHeader::new("CRDB") - .left_icon(Icon::Hash.into()) - .set_toggle(ToggleState::Toggled), - ) - .set_toggle(ToggleState::Toggled), - ), - ) - .child( - v_stack().py_1().child( - List::new(static_collab_panel_channels()) - .header( - ListHeader::new("CHANNELS").set_toggle(ToggleState::Toggled), - ) - .empty_message("No channels yet. Add a channel to get started.") - .set_toggle(ToggleState::Toggled), - ), - ) - .child( - v_stack().py_1().child( - List::new(static_collab_panel_current_call()) - .header( - ListHeader::new("CONTACTS – ONLINE") - .set_toggle(ToggleState::Toggled), - ) - .set_toggle(ToggleState::Toggled), - ), - ) - .child( - v_stack().py_1().child( - List::new(static_collab_panel_current_call()) - .header( - ListHeader::new("CONTACTS – OFFLINE") - .set_toggle(ToggleState::NotToggled), - ) - .set_toggle(ToggleState::NotToggled), - ), - ), - ) - .child( - div() - .h_7() - .px_2() - .border_t() - .border_color(theme.middle.variant.default.border) - .flex() - .items_center() - .child( - div() - .text_sm() - .text_color(theme.middle.variant.default.foreground) - .child("Find..."), - ), - ) - } - - fn list_section_header( - &self, - label: impl Into>, - expanded: bool, - theme: &Theme, - ) -> impl Element { - div() - .h_7() - .px_2() - .flex() - .justify_between() - .items_center() - .child(div().flex().gap_1().text_sm().child(label)) - .child( - div().flex().h_full().gap_1().items_center().child( - svg() - .path(if expanded { - "icons/caret_down.svg" - } else { - "icons/caret_up.svg" - }) - .w_3p5() - .h_3p5() - .fill(theme.middle.variant.default.foreground), - ), - ) - } - - fn list_item( - &self, - avatar_uri: impl Into>, - label: impl Into>, - theme: &Theme, - ) -> impl Element { - div() - .h_7() - .px_2() - .flex() - .items_center() - .hover() - .fill(theme.lowest.variant.hovered.background) - .active() - .fill(theme.lowest.variant.pressed.background) - .child( - div() - .flex() - .items_center() - .gap_1() - .text_sm() - .child( - img() - .uri(avatar_uri) - .size_3p5() - .rounded_full() - .fill(theme.middle.positive.default.foreground), - ) - .child(label), - ) - } -} diff --git a/crates/ui/src/components/command_palette.rs b/crates/ui/src/components/command_palette.rs deleted file mode 100644 index 797876c05e61f8b257ceba7d47b1e59c50c5c967..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/command_palette.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::{example_editor_actions, OrderMethod, Palette}; - -#[derive(Element)] -pub struct CommandPalette { - view_type: PhantomData, - scroll_state: ScrollState, -} - -impl CommandPalette { - pub fn new(scroll_state: ScrollState) -> Self { - Self { - view_type: PhantomData, - scroll_state, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div().child( - Palette::new(self.scroll_state.clone()) - .items(example_editor_actions()) - .placeholder("Execute a command...") - .empty_string("No items found.") - .default_order(OrderMethod::Ascending), - ) - } -} diff --git a/crates/ui/src/components/context_menu.rs b/crates/ui/src/components/context_menu.rs deleted file mode 100644 index 0808bf655688bd104962ed63d5f19d1eef1f9995..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/context_menu.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::prelude::*; -use crate::theme::theme; -use crate::{ - v_stack, Label, List, ListEntry, ListItem, ListItemVariant, ListSeparator, ListSubHeader, -}; - -#[derive(Clone)] -pub enum ContextMenuItem { - Header(&'static str), - Entry(Label), - Separator, -} - -impl ContextMenuItem { - fn to_list_item(self) -> ListItem { - match self { - ContextMenuItem::Header(label) => ListSubHeader::new(label).into(), - ContextMenuItem::Entry(label) => { - ListEntry::new(label).variant(ListItemVariant::Inset).into() - } - ContextMenuItem::Separator => ListSeparator::new().into(), - } - } - pub fn header(label: &'static str) -> Self { - Self::Header(label) - } - pub fn separator() -> Self { - Self::Separator - } - pub fn entry(label: Label) -> Self { - Self::Entry(label) - } -} - -#[derive(Element)] -pub struct ContextMenu { - items: Vec, -} - -impl ContextMenu { - pub fn new(items: impl IntoIterator) -> Self { - Self { - items: items.into_iter().collect(), - } - } - fn render(&mut self, view: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - v_stack() - .flex() - .fill(theme.lowest.base.default.background) - .border() - .border_color(theme.lowest.base.default.border) - .child( - List::new( - self.items - .clone() - .into_iter() - .map(ContextMenuItem::to_list_item) - .collect(), - ) - .set_toggle(ToggleState::Toggled), - ) - //div().p_1().children(self.items.clone()) - } -} diff --git a/crates/ui/src/components/editor_pane.rs b/crates/ui/src/components/editor_pane.rs deleted file mode 100644 index 561081164c8f9c8982ef2915ba5a612428f64ed4..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/editor_pane.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::marker::PhantomData; -use std::path::PathBuf; - -use crate::prelude::*; -use crate::{v_stack, Breadcrumb, Buffer, Icon, IconButton, Symbol, Tab, TabBar, Toolbar}; - -pub struct Editor { - pub tabs: Vec, - pub path: PathBuf, - pub symbols: Vec, - pub buffer: Buffer, -} - -#[derive(Element)] -pub struct EditorPane { - view_type: PhantomData, - editor: Editor, -} - -impl EditorPane { - pub fn new(editor: Editor) -> Self { - Self { - view_type: PhantomData, - editor, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - struct LeftItemsPayload { - path: PathBuf, - symbols: Vec, - } - - v_stack() - .w_full() - .h_full() - .flex_1() - .child(TabBar::new(self.editor.tabs.clone())) - .child(Toolbar::new( - |_, payload| { - let payload = payload.downcast_ref::().unwrap(); - - vec![Breadcrumb::new(payload.path.clone(), payload.symbols.clone()).into_any()] - }, - Box::new(LeftItemsPayload { - path: self.editor.path.clone(), - symbols: self.editor.symbols.clone(), - }), - |_, _| { - vec![ - IconButton::new(Icon::InlayHint).into_any(), - IconButton::new(Icon::MagnifyingGlass).into_any(), - IconButton::new(Icon::MagicWand).into_any(), - ] - }, - Box::new(()), - )) - .child(self.editor.buffer.clone()) - } -} diff --git a/crates/ui/src/components/facepile.rs b/crates/ui/src/components/facepile.rs deleted file mode 100644 index 949e226c257d2eb504ebb00eeb9cd3611d0059ea..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/facepile.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::prelude::*; -use crate::{theme, Avatar, Player}; - -#[derive(Element)] -pub struct Facepile { - players: Vec, -} - -impl Facepile { - pub fn new>(players: P) -> Self { - Self { - players: players.collect(), - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let player_count = self.players.len(); - let player_list = self.players.iter().enumerate().map(|(ix, player)| { - let isnt_last = ix < player_count - 1; - - div() - .when(isnt_last, |div| div.neg_mr_1()) - .child(Avatar::new(player.avatar_src().to_string())) - }); - div().p_1().flex().items_center().children(player_list) - } -} diff --git a/crates/ui/src/components/icon_button.rs b/crates/ui/src/components/icon_button.rs deleted file mode 100644 index 8ac122d3eb0ecdac5f4ac02666770120ae18e836..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/icon_button.rs +++ /dev/null @@ -1,67 +0,0 @@ -use crate::prelude::*; -use crate::{theme, Icon, IconColor, IconElement}; - -#[derive(Element)] -pub struct IconButton { - icon: Icon, - color: IconColor, - variant: ButtonVariant, - state: InteractionState, -} - -impl IconButton { - pub fn new(icon: Icon) -> Self { - Self { - icon, - color: IconColor::default(), - variant: ButtonVariant::default(), - state: InteractionState::default(), - } - } - - pub fn icon(mut self, icon: Icon) -> Self { - self.icon = icon; - self - } - - pub fn color(mut self, color: IconColor) -> Self { - self.color = color; - self - } - - pub fn variant(mut self, variant: ButtonVariant) -> Self { - self.variant = variant; - self - } - - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - let icon_color = match (self.state, self.color) { - (InteractionState::Disabled, _) => IconColor::Disabled, - _ => self.color, - }; - - let mut div = div(); - if self.variant == ButtonVariant::Filled { - div = div.fill(theme.highest.on.default.background); - } - - div.w_7() - .h_6() - .flex() - .items_center() - .justify_center() - .rounded_md() - .hover() - .fill(theme.highest.base.hovered.background) - .active() - .fill(theme.highest.base.pressed.background) - .child(IconElement::new(self.icon).color(icon_color)) - } -} diff --git a/crates/ui/src/components/keybinding.rs b/crates/ui/src/components/keybinding.rs deleted file mode 100644 index 63c5f11039b752b5d53bb2c12878e33ac017fccd..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/keybinding.rs +++ /dev/null @@ -1,158 +0,0 @@ -use std::collections::HashSet; - -use strum::{EnumIter, IntoEnumIterator}; - -use crate::prelude::*; -use crate::theme; - -#[derive(Element, Clone)] -pub struct Keybinding { - /// A keybinding consists of a key and a set of modifier keys. - /// More then one keybinding produces a chord. - /// - /// This should always contain at least one element. - keybinding: Vec<(String, ModifierKeys)>, -} - -impl Keybinding { - pub fn new(key: String, modifiers: ModifierKeys) -> Self { - Self { - keybinding: vec![(key, modifiers)], - } - } - - pub fn new_chord( - first_note: (String, ModifierKeys), - second_note: (String, ModifierKeys), - ) -> Self { - Self { - keybinding: vec![first_note, second_note], - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div() - .flex() - .gap_2() - .children(self.keybinding.iter().map(|(key, modifiers)| { - div() - .flex() - .gap_1() - .children(ModifierKey::iter().filter_map(|modifier| { - if modifiers.0.contains(&modifier) { - Some(Key::new(modifier.glyph())) - } else { - None - } - })) - .child(Key::new(key.clone())) - })) - } -} - -#[derive(Element)] -pub struct Key { - key: String, -} - -impl Key { - pub fn new(key: K) -> Self - where - K: Into, - { - Self { key: key.into() } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .px_2() - .py_0() - .rounded_md() - .text_sm() - .text_color(theme.lowest.on.default.foreground) - .fill(theme.lowest.on.default.background) - .child(self.key.clone()) - } -} - -// NOTE: The order the modifier keys appear in this enum impacts the order in -// which they are rendered in the UI. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum ModifierKey { - Control, - Alt, - Command, - Shift, -} - -impl ModifierKey { - /// Returns the glyph for the [`ModifierKey`]. - pub fn glyph(&self) -> char { - match self { - Self::Control => '^', - Self::Alt => '⌥', - Self::Command => '⌘', - Self::Shift => '⇧', - } - } -} - -#[derive(Clone)] -pub struct ModifierKeys(HashSet); - -impl ModifierKeys { - pub fn new() -> Self { - Self(HashSet::new()) - } - - pub fn all() -> Self { - Self(HashSet::from_iter(ModifierKey::iter())) - } - - pub fn add(mut self, modifier: ModifierKey) -> Self { - self.0.insert(modifier); - self - } - - pub fn control(mut self, control: bool) -> Self { - if control { - self.0.insert(ModifierKey::Control); - } else { - self.0.remove(&ModifierKey::Control); - } - - self - } - - pub fn alt(mut self, alt: bool) -> Self { - if alt { - self.0.insert(ModifierKey::Alt); - } else { - self.0.remove(&ModifierKey::Alt); - } - - self - } - - pub fn command(mut self, command: bool) -> Self { - if command { - self.0.insert(ModifierKey::Command); - } else { - self.0.remove(&ModifierKey::Command); - } - - self - } - - pub fn shift(mut self, shift: bool) -> Self { - if shift { - self.0.insert(ModifierKey::Shift); - } else { - self.0.remove(&ModifierKey::Shift); - } - - self - } -} diff --git a/crates/ui/src/components/language_selector.rs b/crates/ui/src/components/language_selector.rs deleted file mode 100644 index 124d7f13ee0fbefc94c7629ab12105b8d92ae356..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/language_selector.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::prelude::*; -use crate::{OrderMethod, Palette, PaletteItem}; - -#[derive(Element)] -pub struct LanguageSelector { - scroll_state: ScrollState, -} - -impl LanguageSelector { - pub fn new() -> Self { - Self { - scroll_state: ScrollState::default(), - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div().child( - Palette::new(self.scroll_state.clone()) - .items(vec![ - PaletteItem::new("C"), - PaletteItem::new("C++"), - PaletteItem::new("CSS"), - PaletteItem::new("Elixir"), - PaletteItem::new("Elm"), - PaletteItem::new("ERB"), - PaletteItem::new("Rust (current)"), - PaletteItem::new("Scheme"), - PaletteItem::new("TOML"), - PaletteItem::new("TypeScript"), - ]) - .placeholder("Select a language...") - .empty_string("No matches") - .default_order(OrderMethod::Ascending), - ) - } -} diff --git a/crates/ui/src/components/list.rs b/crates/ui/src/components/list.rs deleted file mode 100644 index b7dff6b2c59dec0f7f772a1b1f40df8948567850..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/list.rs +++ /dev/null @@ -1,512 +0,0 @@ -use gpui2::elements::div::Div; -use gpui2::{Hsla, WindowContext}; - -use crate::prelude::*; -use crate::{ - h_stack, theme, token, v_stack, Avatar, DisclosureControlVisibility, Icon, IconColor, - IconElement, IconSize, InteractionState, Label, LabelColor, LabelSize, SystemColor, - ToggleState, -}; - -#[derive(Clone, Copy, Default, Debug, PartialEq)] -pub enum ListItemVariant { - /// The list item extends to the far left and right of the list. - #[default] - FullWidth, - Inset, -} - -#[derive(Element, Clone, Copy)] -pub struct ListHeader { - label: &'static str, - left_icon: Option, - variant: ListItemVariant, - state: InteractionState, - toggleable: Toggleable, -} - -impl ListHeader { - pub fn new(label: &'static str) -> Self { - Self { - label, - left_icon: None, - variant: ListItemVariant::default(), - state: InteractionState::default(), - toggleable: Toggleable::default(), - } - } - - pub fn set_toggle(mut self, toggle: ToggleState) -> Self { - self.toggleable = toggle.into(); - self - } - - pub fn set_toggleable(mut self, toggleable: Toggleable) -> Self { - self.toggleable = toggleable; - self - } - - pub fn left_icon(mut self, left_icon: Option) -> Self { - self.left_icon = left_icon; - self - } - - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; - self - } - - fn disclosure_control(&self) -> Div { - let is_toggleable = self.toggleable != Toggleable::NotToggleable; - let is_toggled = Toggleable::is_toggled(&self.toggleable); - - match (is_toggleable, is_toggled) { - (false, _) => div(), - (_, true) => div().child(IconElement::new(Icon::ChevronRight).color(IconColor::Muted)), - (_, false) => div().child(IconElement::new(Icon::ChevronDown).size(IconSize::Small)), - } - } - - fn background_color(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - let system_color = SystemColor::new(); - - match self.state { - InteractionState::Hovered => theme.lowest.base.hovered.background, - InteractionState::Active => theme.lowest.base.pressed.background, - InteractionState::Enabled => theme.lowest.on.default.background, - _ => system_color.transparent, - } - } - - fn label_color(&self) -> LabelColor { - match self.state { - InteractionState::Disabled => LabelColor::Disabled, - _ => Default::default(), - } - } - - fn icon_color(&self) -> IconColor { - match self.state { - InteractionState::Disabled => IconColor::Disabled, - _ => Default::default(), - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let token = token(); - let system_color = SystemColor::new(); - let background_color = self.background_color(cx); - - let is_toggleable = self.toggleable != Toggleable::NotToggleable; - let is_toggled = Toggleable::is_toggled(&self.toggleable); - - let disclosure_control = self.disclosure_control(); - - h_stack() - .flex_1() - .w_full() - .fill(background_color) - .when(self.state == InteractionState::Focused, |this| { - this.border() - .border_color(theme.lowest.accent.default.border) - }) - .relative() - .py_1() - .child( - div() - .h_6() - .when(self.variant == ListItemVariant::Inset, |this| this.px_2()) - .flex() - .flex_1() - .w_full() - .gap_1() - .items_center() - .justify_between() - .child( - div() - .flex() - .gap_1() - .items_center() - .children(self.left_icon.map(|i| { - IconElement::new(i) - .color(IconColor::Muted) - .size(IconSize::Small) - })) - .child( - Label::new(self.label) - .color(LabelColor::Muted) - .size(LabelSize::Small), - ), - ) - .child(disclosure_control), - ) - } -} - -#[derive(Element, Clone, Copy)] -pub struct ListSubHeader { - label: &'static str, - left_icon: Option, - variant: ListItemVariant, -} - -impl ListSubHeader { - pub fn new(label: &'static str) -> Self { - Self { - label, - left_icon: None, - variant: ListItemVariant::default(), - } - } - - pub fn left_icon(mut self, left_icon: Option) -> Self { - self.left_icon = left_icon; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let token = token(); - - h_stack().flex_1().w_full().relative().py_1().child( - div() - .h_6() - .when(self.variant == ListItemVariant::Inset, |this| this.px_2()) - .flex() - .flex_1() - .w_full() - .gap_1() - .items_center() - .justify_between() - .child( - div() - .flex() - .gap_1() - .items_center() - .children(self.left_icon.map(|i| { - IconElement::new(i) - .color(IconColor::Muted) - .size(IconSize::Small) - })) - .child( - Label::new(self.label) - .color(LabelColor::Muted) - .size(LabelSize::Small), - ), - ), - ) - } -} - -#[derive(Clone)] -pub enum LeftContent { - Icon(Icon), - Avatar(&'static str), -} - -#[derive(Default, PartialEq, Copy, Clone)] -pub enum ListEntrySize { - #[default] - Small, - Medium, -} - -#[derive(Clone, Element)] -pub enum ListItem { - Entry(ListEntry), - Separator(ListSeparator), - Header(ListSubHeader), -} - -impl From for ListItem { - fn from(entry: ListEntry) -> Self { - Self::Entry(entry) - } -} - -impl From for ListItem { - fn from(entry: ListSeparator) -> Self { - Self::Separator(entry) - } -} - -impl From for ListItem { - fn from(entry: ListSubHeader) -> Self { - Self::Header(entry) - } -} - -impl ListItem { - fn render(&mut self, v: &mut V, cx: &mut ViewContext) -> impl IntoElement { - match self { - ListItem::Entry(entry) => div().child(entry.render(v, cx)), - ListItem::Separator(separator) => div().child(separator.render(v, cx)), - ListItem::Header(header) => div().child(header.render(v, cx)), - } - } - pub fn new(label: Label) -> Self { - Self::Entry(ListEntry::new(label)) - } - pub fn as_entry(&mut self) -> Option<&mut ListEntry> { - if let Self::Entry(entry) = self { - Some(entry) - } else { - None - } - } -} - -#[derive(Element, Clone)] -pub struct ListEntry { - disclosure_control_style: DisclosureControlVisibility, - indent_level: u32, - label: Label, - left_content: Option, - variant: ListItemVariant, - size: ListEntrySize, - state: InteractionState, - toggle: Option, -} - -impl ListEntry { - pub fn new(label: Label) -> Self { - Self { - disclosure_control_style: DisclosureControlVisibility::default(), - indent_level: 0, - label, - variant: ListItemVariant::default(), - left_content: None, - size: ListEntrySize::default(), - state: InteractionState::default(), - toggle: None, - } - } - pub fn variant(mut self, variant: ListItemVariant) -> Self { - self.variant = variant; - self - } - pub fn indent_level(mut self, indent_level: u32) -> Self { - self.indent_level = indent_level; - self - } - - pub fn set_toggle(mut self, toggle: ToggleState) -> Self { - self.toggle = Some(toggle); - self - } - - pub fn left_content(mut self, left_content: LeftContent) -> Self { - self.left_content = Some(left_content); - self - } - - pub fn left_icon(mut self, left_icon: Icon) -> Self { - self.left_content = Some(LeftContent::Icon(left_icon)); - self - } - - pub fn left_avatar(mut self, left_avatar: &'static str) -> Self { - self.left_content = Some(LeftContent::Avatar(left_avatar)); - self - } - - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; - self - } - - pub fn size(mut self, size: ListEntrySize) -> Self { - self.size = size; - self - } - - pub fn disclosure_control_style( - mut self, - disclosure_control_style: DisclosureControlVisibility, - ) -> Self { - self.disclosure_control_style = disclosure_control_style; - self - } - - fn background_color(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - let system_color = SystemColor::new(); - - match self.state { - InteractionState::Hovered => theme.lowest.base.hovered.background, - InteractionState::Active => theme.lowest.base.pressed.background, - InteractionState::Enabled => theme.lowest.on.default.background, - _ => system_color.transparent, - } - } - - fn label_color(&self) -> LabelColor { - match self.state { - InteractionState::Disabled => LabelColor::Disabled, - _ => Default::default(), - } - } - - fn icon_color(&self) -> IconColor { - match self.state { - InteractionState::Disabled => IconColor::Disabled, - _ => Default::default(), - } - } - - fn disclosure_control( - &mut self, - cx: &mut ViewContext, - ) -> Option> { - let theme = theme(cx); - let token = token(); - - let disclosure_control_icon = if let Some(ToggleState::Toggled) = self.toggle { - IconElement::new(Icon::ChevronDown) - } else { - IconElement::new(Icon::ChevronRight) - } - .color(IconColor::Muted) - .size(IconSize::Small); - - match (self.toggle, self.disclosure_control_style) { - (Some(_), DisclosureControlVisibility::OnHover) => { - Some(div().absolute().neg_left_5().child(disclosure_control_icon)) - } - (Some(_), DisclosureControlVisibility::Always) => { - Some(div().child(disclosure_control_icon)) - } - (None, _) => None, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let token = token(); - let system_color = SystemColor::new(); - let background_color = self.background_color(cx); - - let left_content = match self.left_content { - Some(LeftContent::Icon(i)) => { - Some(h_stack().child(IconElement::new(i).size(IconSize::Small))) - } - Some(LeftContent::Avatar(src)) => Some(h_stack().child(Avatar::new(src))), - None => None, - }; - - let sized_item = match self.size { - ListEntrySize::Small => div().h_6(), - ListEntrySize::Medium => div().h_7(), - }; - - div() - .fill(background_color) - .when(self.state == InteractionState::Focused, |this| { - this.border() - .border_color(theme.lowest.accent.default.border) - }) - .relative() - .py_1() - .child( - sized_item - .when(self.variant == ListItemVariant::Inset, |this| this.px_2()) - // .ml(rems(0.75 * self.indent_level as f32)) - .children((0..self.indent_level).map(|_| { - div() - .w(token.list_indent_depth) - .h_full() - .flex() - .justify_center() - .child(h_stack().child(div().w_px().h_full()).child( - div().w_px().h_full().fill(theme.middle.base.default.border), - )) - })) - .flex() - .gap_1() - .items_center() - .relative() - .children(self.disclosure_control(cx)) - .children(left_content) - .child(self.label.clone()), - ) - } -} - -#[derive(Clone, Default, Element)] -pub struct ListSeparator; - -impl ListSeparator { - pub fn new() -> Self { - Self::default() - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div().h_px().w_full().fill(theme.lowest.base.default.border) - } -} - -#[derive(Element)] -pub struct List { - items: Vec, - empty_message: &'static str, - header: Option, - toggleable: Toggleable, -} - -impl List { - pub fn new(items: Vec) -> Self { - Self { - items, - empty_message: "No items", - header: None, - toggleable: Toggleable::default(), - } - } - - pub fn empty_message(mut self, empty_message: &'static str) -> Self { - self.empty_message = empty_message; - self - } - - pub fn header(mut self, header: ListHeader) -> Self { - self.header = Some(header); - self - } - - pub fn set_toggle(mut self, toggle: ToggleState) -> Self { - self.toggleable = toggle.into(); - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let token = token(); - let is_toggleable = self.toggleable != Toggleable::NotToggleable; - let is_toggled = Toggleable::is_toggled(&self.toggleable); - - let disclosure_control = if is_toggleable { - IconElement::new(Icon::ChevronRight) - } else { - IconElement::new(Icon::ChevronDown) - }; - - let list_content = match (self.items.is_empty(), is_toggled) { - (_, false) => div(), - (false, _) => div().children(self.items.iter().cloned()), - (true, _) => div().child(Label::new(self.empty_message).color(LabelColor::Muted)), - }; - - v_stack() - .py_1() - .children( - self.header - .clone() - .map(|header| header.set_toggleable(self.toggleable)), - ) - .child(list_content) - } -} diff --git a/crates/ui/src/components/multi_buffer.rs b/crates/ui/src/components/multi_buffer.rs deleted file mode 100644 index d38603457a7d5778c3918d84ab5487830a97e606..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/multi_buffer.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::{v_stack, Buffer, Icon, IconButton, Label, LabelSize}; - -#[derive(Element)] -pub struct MultiBuffer { - view_type: PhantomData, - buffers: Vec, -} - -impl MultiBuffer { - pub fn new(buffers: Vec) -> Self { - Self { - view_type: PhantomData, - buffers, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - v_stack() - .w_full() - .h_full() - .flex_1() - .children(self.buffers.clone().into_iter().map(|buffer| { - v_stack() - .child( - div() - .flex() - .items_center() - .justify_between() - .p_4() - .fill(theme.lowest.base.default.background) - .child(Label::new("main.rs").size(LabelSize::Small)) - .child(IconButton::new(Icon::ArrowUpRight)), - ) - .child(buffer) - })) - } -} diff --git a/crates/ui/src/components/palette.rs b/crates/ui/src/components/palette.rs deleted file mode 100644 index 16001e50c1c7031483611dd86ab7e3d8d64efc9e..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/palette.rs +++ /dev/null @@ -1,152 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::theme::theme; -use crate::{h_stack, v_stack, Keybinding, Label, LabelColor}; - -#[derive(Element)] -pub struct Palette { - view_type: PhantomData, - scroll_state: ScrollState, - input_placeholder: &'static str, - empty_string: &'static str, - items: Vec, - default_order: OrderMethod, -} - -impl Palette { - pub fn new(scroll_state: ScrollState) -> Self { - Self { - view_type: PhantomData, - scroll_state, - input_placeholder: "Find something...", - empty_string: "No items found.", - items: vec![], - default_order: OrderMethod::default(), - } - } - - pub fn items(mut self, items: Vec) -> Self { - self.items = items; - self - } - - pub fn placeholder(mut self, input_placeholder: &'static str) -> Self { - self.input_placeholder = input_placeholder; - self - } - - pub fn empty_string(mut self, empty_string: &'static str) -> Self { - self.empty_string = empty_string; - self - } - - // TODO: Hook up sort order - pub fn default_order(mut self, default_order: OrderMethod) -> Self { - self.default_order = default_order; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - v_stack() - .w_96() - .rounded_lg() - .fill(theme.lowest.base.default.background) - .border() - .border_color(theme.lowest.base.default.border) - .child( - v_stack() - .gap_px() - .child(v_stack().py_0p5().px_1().child( - div().px_2().py_0p5().child( - Label::new(self.input_placeholder).color(LabelColor::Placeholder), - ), - )) - .child(div().h_px().w_full().fill(theme.lowest.base.default.border)) - .child( - v_stack() - .py_0p5() - .px_1() - .grow() - .max_h_96() - .overflow_y_scroll(self.scroll_state.clone()) - .children( - vec![if self.items.is_empty() { - Some(h_stack().justify_between().px_2().py_1().child( - Label::new(self.empty_string).color(LabelColor::Muted), - )) - } else { - None - }] - .into_iter() - .flatten(), - ) - .children(self.items.iter().map(|item| { - h_stack() - .justify_between() - .px_2() - .py_0p5() - .rounded_lg() - .hover() - .fill(theme.lowest.base.hovered.background) - .active() - .fill(theme.lowest.base.pressed.background) - .child(item.clone()) - })), - ), - ) - } -} - -#[derive(Element, Clone)] -pub struct PaletteItem { - pub label: &'static str, - pub sublabel: Option<&'static str>, - pub keybinding: Option, -} - -impl PaletteItem { - pub fn new(label: &'static str) -> Self { - Self { - label, - sublabel: None, - keybinding: None, - } - } - - pub fn label(mut self, label: &'static str) -> Self { - self.label = label; - self - } - - pub fn sublabel>>(mut self, sublabel: L) -> Self { - self.sublabel = sublabel.into(); - self - } - - pub fn keybinding(mut self, keybinding: K) -> Self - where - K: Into>, - { - self.keybinding = keybinding.into(); - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .flex() - .flex_row() - .grow() - .justify_between() - .child( - v_stack() - .child(Label::new(self.label)) - .children(self.sublabel.map(|sublabel| Label::new(sublabel))), - ) - .children(self.keybinding.clone()) - } -} diff --git a/crates/ui/src/components/panel.rs b/crates/ui/src/components/panel.rs deleted file mode 100644 index cbcf502670f4957c90558c84e97a45f44ca93b16..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/panel.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::marker::PhantomData; - -use gpui2::geometry::AbsoluteLength; - -use crate::prelude::*; -use crate::{theme, token, v_stack}; - -#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub enum PanelAllowedSides { - LeftOnly, - RightOnly, - BottomOnly, - #[default] - LeftAndRight, - All, -} - -impl PanelAllowedSides { - /// Return a `HashSet` that contains the allowable `PanelSide`s. - pub fn allowed_sides(&self) -> HashSet { - match self { - Self::LeftOnly => HashSet::from_iter([PanelSide::Left]), - Self::RightOnly => HashSet::from_iter([PanelSide::Right]), - Self::BottomOnly => HashSet::from_iter([PanelSide::Bottom]), - Self::LeftAndRight => HashSet::from_iter([PanelSide::Left, PanelSide::Right]), - Self::All => HashSet::from_iter([PanelSide::Left, PanelSide::Right, PanelSide::Bottom]), - } - } -} - -#[derive(Default, Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub enum PanelSide { - #[default] - Left, - Right, - Bottom, -} - -use std::collections::HashSet; - -#[derive(Element)] -pub struct Panel { - view_type: PhantomData, - scroll_state: ScrollState, - current_side: PanelSide, - /// Defaults to PanelAllowedSides::LeftAndRight - allowed_sides: PanelAllowedSides, - initial_width: AbsoluteLength, - width: Option, - children: HackyChildren, - payload: HackyChildrenPayload, -} - -impl Panel { - pub fn new( - scroll_state: ScrollState, - children: HackyChildren, - payload: HackyChildrenPayload, - ) -> Self { - let token = token(); - - Self { - view_type: PhantomData, - scroll_state, - current_side: PanelSide::default(), - allowed_sides: PanelAllowedSides::default(), - initial_width: token.default_panel_size, - width: None, - children, - payload, - } - } - - pub fn initial_width(mut self, initial_width: AbsoluteLength) -> Self { - self.initial_width = initial_width; - self - } - - pub fn width(mut self, width: AbsoluteLength) -> Self { - self.width = Some(width); - self - } - - pub fn allowed_sides(mut self, allowed_sides: PanelAllowedSides) -> Self { - self.allowed_sides = allowed_sides; - self - } - - pub fn side(mut self, side: PanelSide) -> Self { - let allowed_sides = self.allowed_sides.allowed_sides(); - - if allowed_sides.contains(&side) { - self.current_side = side; - } else { - panic!( - "The panel side {:?} was not added as allowed before it was set.", - side - ); - } - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let token = token(); - let theme = theme(cx); - - let panel_base; - let current_width = self.width.unwrap_or(self.initial_width); - - match self.current_side { - PanelSide::Left => { - panel_base = v_stack() - .flex_initial() - .h_full() - .w(current_width) - .fill(theme.middle.base.default.background) - .border_r() - .border_color(theme.middle.base.default.border); - } - PanelSide::Right => { - panel_base = v_stack() - .flex_initial() - .h_full() - .w(current_width) - .fill(theme.middle.base.default.background) - .border_l() - .border_color(theme.middle.base.default.border); - } - PanelSide::Bottom => { - panel_base = v_stack() - .flex_initial() - .w_full() - .h(current_width) - .fill(theme.middle.base.default.background) - .border_t() - .border_color(theme.middle.base.default.border); - } - } - - panel_base.children_any((self.children)(cx, self.payload.as_ref())) - } -} diff --git a/crates/ui/src/components/panes.rs b/crates/ui/src/components/panes.rs deleted file mode 100644 index 2518eea8ee9db3c3353ff59ccc7c241efcaffaf6..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/panes.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::marker::PhantomData; - -use gpui2::geometry::{Length, Size}; -use gpui2::{hsla, Hsla}; - -use crate::prelude::*; -use crate::theme; - -#[derive(Default, PartialEq)] -pub enum SplitDirection { - #[default] - Horizontal, - Vertical, -} - -#[derive(Element)] -pub struct Pane { - view_type: PhantomData, - scroll_state: ScrollState, - size: Size, - fill: Hsla, - children: HackyChildren, - payload: HackyChildrenPayload, -} - -impl Pane { - pub fn new( - scroll_state: ScrollState, - size: Size, - children: HackyChildren, - payload: HackyChildrenPayload, - ) -> Self { - // Fill is only here for debugging purposes, remove before release - let system_color = SystemColor::new(); - - Self { - view_type: PhantomData, - scroll_state, - size, - fill: hsla(0.3, 0.3, 0.3, 1.), - // fill: system_color.transparent, - children, - payload, - } - } - - pub fn fill(mut self, fill: Hsla) -> Self { - self.fill = fill; - self - } - - fn render(&mut self, view: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .flex() - .flex_initial() - .fill(self.fill) - .w(self.size.width) - .h(self.size.height) - .overflow_y_scroll(self.scroll_state.clone()) - .children_any((self.children)(cx, self.payload.as_ref())) - } -} - -#[derive(Element)] -pub struct PaneGroup { - view_type: PhantomData, - groups: Vec>, - panes: Vec>, - split_direction: SplitDirection, -} - -impl PaneGroup { - pub fn new_groups(groups: Vec>, split_direction: SplitDirection) -> Self { - Self { - view_type: PhantomData, - groups, - panes: Vec::new(), - split_direction, - } - } - - pub fn new_panes(panes: Vec>, split_direction: SplitDirection) -> Self { - Self { - view_type: PhantomData, - groups: Vec::new(), - panes, - split_direction, - } - } - - fn render(&mut self, view: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - if !self.panes.is_empty() { - let el = div() - .flex() - .flex_1() - .gap_px() - .w_full() - .h_full() - .fill(theme.lowest.base.default.background) - .children(self.panes.iter_mut().map(|pane| pane.render(view, cx))); - - if self.split_direction == SplitDirection::Horizontal { - return el; - } else { - return el.flex_col(); - } - } - - if !self.groups.is_empty() { - let el = div() - .flex() - .flex_1() - .gap_px() - .w_full() - .h_full() - .fill(theme.lowest.base.default.background) - .children(self.groups.iter_mut().map(|group| group.render(view, cx))); - - if self.split_direction == SplitDirection::Horizontal { - return el; - } else { - return el.flex_col(); - } - } - - unreachable!() - } -} diff --git a/crates/ui/src/components/player_stack.rs b/crates/ui/src/components/player_stack.rs deleted file mode 100644 index 7df6f065fb280f4c821f1bb5b2488691d0eef874..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/player_stack.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::prelude::*; -use crate::{Avatar, Facepile, PlayerWithCallStatus}; - -#[derive(Element)] -pub struct PlayerStack { - player_with_call_status: PlayerWithCallStatus, -} - -impl PlayerStack { - pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self { - Self { - player_with_call_status, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let system_color = SystemColor::new(); - let player = self.player_with_call_status.get_player(); - self.player_with_call_status.get_call_status(); - - let followers = self - .player_with_call_status - .get_call_status() - .followers - .as_ref() - .map(|followers| followers.clone()); - - // if we have no followers return a slightly different element - // if mic_status == muted add a red ring to avatar - - div() - .h_full() - .flex() - .flex_col() - .gap_px() - .justify_center() - .child( - div().flex().justify_center().w_full().child( - div() - .w_4() - .h_0p5() - .rounded_sm() - .fill(player.cursor_color(cx)), - ), - ) - .child( - div() - .flex() - .items_center() - .justify_center() - .h_6() - .pl_1() - .rounded_lg() - .fill(if followers.is_none() { - system_color.transparent - } else { - player.selection_color(cx) - }) - .child(Avatar::new(player.avatar_src().to_string())) - .children(followers.map(|followers| { - div().neg_ml_2().child(Facepile::new(followers.into_iter())) - })), - ) - } -} diff --git a/crates/ui/src/components/project_panel.rs b/crates/ui/src/components/project_panel.rs deleted file mode 100644 index 1f32c698e583d79b7fd327b24b9671f1ad0af0ac..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/project_panel.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::{ - static_project_panel_project_items, static_project_panel_single_items, theme, Input, List, - ListHeader, -}; - -#[derive(Element)] -pub struct ProjectPanel { - view_type: PhantomData, - scroll_state: ScrollState, -} - -impl ProjectPanel { - pub fn new(scroll_state: ScrollState) -> Self { - Self { - view_type: PhantomData, - scroll_state, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .flex() - .flex_col() - .w_full() - .h_full() - .px_2() - .fill(theme.middle.base.default.background) - .child( - div() - .w_56() - .flex() - .flex_col() - .overflow_y_scroll(ScrollState::default()) - .child( - List::new(static_project_panel_single_items()) - .header(ListHeader::new("FILES").set_toggle(ToggleState::Toggled)) - .empty_message("No files in directory") - .set_toggle(ToggleState::Toggled), - ) - .child( - List::new(static_project_panel_project_items()) - .header(ListHeader::new("PROJECT").set_toggle(ToggleState::Toggled)) - .empty_message("No folders in directory") - .set_toggle(ToggleState::Toggled), - ), - ) - .child( - Input::new("Find something...") - .value("buffe".to_string()) - .state(InteractionState::Focused), - ) - } -} diff --git a/crates/ui/src/components/recent_projects.rs b/crates/ui/src/components/recent_projects.rs deleted file mode 100644 index 6aca6631b9f523bf232d5d6b6d35ae79a11b3174..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/recent_projects.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::prelude::*; -use crate::{OrderMethod, Palette, PaletteItem}; - -#[derive(Element)] -pub struct RecentProjects { - scroll_state: ScrollState, -} - -impl RecentProjects { - pub fn new() -> Self { - Self { - scroll_state: ScrollState::default(), - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div().child( - Palette::new(self.scroll_state.clone()) - .items(vec![ - PaletteItem::new("zed").sublabel("~/projects/zed"), - PaletteItem::new("saga").sublabel("~/projects/saga"), - PaletteItem::new("journal").sublabel("~/journal"), - PaletteItem::new("dotfiles").sublabel("~/dotfiles"), - PaletteItem::new("zed.dev").sublabel("~/projects/zed.dev"), - PaletteItem::new("laminar").sublabel("~/projects/laminar"), - ]) - .placeholder("Recent Projects...") - .empty_string("No matches") - .default_order(OrderMethod::Ascending), - ) - } -} diff --git a/crates/ui/src/components/status_bar.rs b/crates/ui/src/components/status_bar.rs deleted file mode 100644 index 1970b0bff9807299961741c993491805eba93431..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/status_bar.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::theme::{theme, Theme}; -use crate::{Button, Icon, IconButton, IconColor, ToolDivider}; - -#[derive(Default, PartialEq)] -pub enum Tool { - #[default] - ProjectPanel, - CollaborationPanel, - Terminal, - Assistant, - Feedback, - Diagnostics, -} - -struct ToolGroup { - active_index: Option, - tools: Vec, -} - -impl Default for ToolGroup { - fn default() -> Self { - ToolGroup { - active_index: None, - tools: vec![], - } - } -} - -#[derive(Element)] -pub struct StatusBar { - view_type: PhantomData, - left_tools: Option, - right_tools: Option, - bottom_tools: Option, -} - -impl StatusBar { - pub fn new() -> Self { - Self { - view_type: PhantomData, - left_tools: None, - right_tools: None, - bottom_tools: None, - } - } - - pub fn left_tool(mut self, tool: Tool, active_index: Option) -> Self { - self.left_tools = { - let mut tools = vec![tool]; - tools.extend(self.left_tools.take().unwrap_or_default().tools); - Some(ToolGroup { - active_index, - tools, - }) - }; - self - } - - pub fn right_tool(mut self, tool: Tool, active_index: Option) -> Self { - self.right_tools = { - let mut tools = vec![tool]; - tools.extend(self.left_tools.take().unwrap_or_default().tools); - Some(ToolGroup { - active_index, - tools, - }) - }; - self - } - - pub fn bottom_tool(mut self, tool: Tool, active_index: Option) -> Self { - self.bottom_tools = { - let mut tools = vec![tool]; - tools.extend(self.left_tools.take().unwrap_or_default().tools); - Some(ToolGroup { - active_index, - tools, - }) - }; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .py_0p5() - .px_1() - .flex() - .items_center() - .justify_between() - .w_full() - .fill(theme.lowest.base.default.background) - .child(self.left_tools(&theme)) - .child(self.right_tools(&theme)) - } - - fn left_tools(&self, theme: &Theme) -> impl Element { - div() - .flex() - .items_center() - .gap_1() - .child(IconButton::new(Icon::FileTree).color(IconColor::Accent)) - .child(IconButton::new(Icon::Hash)) - .child(ToolDivider::new()) - .child(IconButton::new(Icon::XCircle)) - } - fn right_tools(&self, theme: &Theme) -> impl Element { - div() - .flex() - .items_center() - .gap_2() - .child( - div() - .flex() - .items_center() - .gap_1() - .child(Button::new("116:25")) - .child(Button::new("Rust")), - ) - .child(ToolDivider::new()) - .child( - div() - .flex() - .items_center() - .gap_1() - .child(IconButton::new(Icon::Copilot)) - .child(IconButton::new(Icon::Envelope)), - ) - .child(ToolDivider::new()) - .child( - div() - .flex() - .items_center() - .gap_1() - .child(IconButton::new(Icon::Terminal)) - .child(IconButton::new(Icon::MessageBubbles)) - .child(IconButton::new(Icon::Ai)), - ) - } -} diff --git a/crates/ui/src/components/tab.rs b/crates/ui/src/components/tab.rs deleted file mode 100644 index 9eb1122775474382b61151ef6c370438abe38ec6..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/tab.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::prelude::*; -use crate::{theme, Icon, IconColor, IconElement, Label, LabelColor}; - -#[derive(Element, Clone)] -pub struct Tab { - title: String, - icon: Option, - current: bool, - dirty: bool, - fs_status: FileSystemStatus, - git_status: GitStatus, - diagnostic_status: DiagnosticStatus, - close_side: IconSide, -} - -impl Tab { - pub fn new() -> Self { - Self { - title: "untitled".to_string(), - icon: None, - current: false, - dirty: false, - fs_status: FileSystemStatus::None, - git_status: GitStatus::None, - diagnostic_status: DiagnosticStatus::None, - close_side: IconSide::Right, - } - } - - pub fn current(mut self, current: bool) -> Self { - self.current = current; - self - } - - pub fn title(mut self, title: String) -> Self { - self.title = title; - self - } - - pub fn icon(mut self, icon: I) -> Self - where - I: Into>, - { - self.icon = icon.into(); - self - } - - pub fn dirty(mut self, dirty: bool) -> Self { - self.dirty = dirty; - self - } - - pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self { - self.fs_status = fs_status; - self - } - - pub fn git_status(mut self, git_status: GitStatus) -> Self { - self.git_status = git_status; - self - } - - pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self { - self.diagnostic_status = diagnostic_status; - self - } - - pub fn close_side(mut self, close_side: IconSide) -> Self { - self.close_side = close_side; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict; - let is_deleted = self.fs_status == FileSystemStatus::Deleted; - - let label = match (self.git_status, is_deleted) { - (_, true) | (GitStatus::Deleted, false) => Label::new(self.title.clone()) - .color(LabelColor::Hidden) - .set_strikethrough(true), - (GitStatus::None, false) => Label::new(self.title.clone()), - (GitStatus::Created, false) => { - Label::new(self.title.clone()).color(LabelColor::Created) - } - (GitStatus::Modified, false) => { - Label::new(self.title.clone()).color(LabelColor::Modified) - } - (GitStatus::Renamed, false) => Label::new(self.title.clone()).color(LabelColor::Accent), - (GitStatus::Conflict, false) => Label::new(self.title.clone()), - }; - - let close_icon = IconElement::new(Icon::Close).color(IconColor::Muted); - - div() - .px_2() - .py_0p5() - .flex() - .items_center() - .justify_center() - .fill(if self.current { - theme.highest.base.default.background - } else { - theme.middle.base.default.background - }) - .child( - div() - .px_1() - .flex() - .items_center() - .gap_1() - .children(has_fs_conflict.then(|| { - IconElement::new(Icon::ExclamationTriangle) - .size(crate::IconSize::Small) - .color(IconColor::Warning) - })) - .children(self.icon.map(IconElement::new)) - .children(if self.close_side == IconSide::Left { - Some(close_icon.clone()) - } else { - None - }) - .child(label) - .children(if self.close_side == IconSide::Right { - Some(close_icon) - } else { - None - }), - ) - } -} diff --git a/crates/ui/src/components/tab_bar.rs b/crates/ui/src/components/tab_bar.rs deleted file mode 100644 index 8addcb87b1599545355fc01d5b3c972515d251f0..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/tab_bar.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::marker::PhantomData; - -use crate::prelude::*; -use crate::{theme, Icon, IconButton, Tab}; - -#[derive(Element)] -pub struct TabBar { - view_type: PhantomData, - scroll_state: ScrollState, - tabs: Vec, -} - -impl TabBar { - pub fn new(tabs: Vec) -> Self { - Self { - view_type: PhantomData, - scroll_state: ScrollState::default(), - tabs, - } - } - - pub fn bind_scroll_state(&mut self, scroll_state: ScrollState) { - self.scroll_state = scroll_state; - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let can_navigate_back = true; - let can_navigate_forward = false; - - div() - .w_full() - .flex() - .fill(theme.middle.base.default.background) - // Left Side - .child( - div() - .px_1() - .flex() - .flex_none() - .gap_2() - // Nav Buttons - .child( - div() - .flex() - .items_center() - .gap_px() - .child( - IconButton::new(Icon::ArrowLeft) - .state(InteractionState::Enabled.if_enabled(can_navigate_back)), - ) - .child( - IconButton::new(Icon::ArrowRight).state( - InteractionState::Enabled.if_enabled(can_navigate_forward), - ), - ), - ), - ) - .child( - div().w_0().flex_1().h_full().child( - div() - .flex() - .overflow_x_scroll(self.scroll_state.clone()) - .children(self.tabs.clone()), - ), - ) - // Right Side - .child( - div() - .px_1() - .flex() - .flex_none() - .gap_2() - // Nav Buttons - .child( - div() - .flex() - .items_center() - .gap_px() - .child(IconButton::new(Icon::Plus)) - .child(IconButton::new(Icon::Split)), - ), - ) - } -} diff --git a/crates/ui/src/components/terminal.rs b/crates/ui/src/components/terminal.rs deleted file mode 100644 index 909cb886ce01d8a8dff3212e0d5d44d09a433b95..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/terminal.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::sync::Arc; - -use gpui2::geometry::{relative, rems, Size}; - -use crate::prelude::*; -use crate::{theme, Icon, IconButton, Pane, Tab}; - -#[derive(Element)] -pub struct Terminal {} - -impl Terminal { - pub fn new() -> Self { - Self {} - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - let can_navigate_back = true; - let can_navigate_forward = false; - - div() - .flex() - .flex_col() - .w_full() - .child( - // Terminal Tabs. - div() - .w_full() - .flex() - .fill(theme.middle.base.default.background) - .child( - div().px_1().flex().flex_none().gap_2().child( - div() - .flex() - .items_center() - .gap_px() - .child( - IconButton::new(Icon::ArrowLeft).state( - InteractionState::Enabled.if_enabled(can_navigate_back), - ), - ) - .child(IconButton::new(Icon::ArrowRight).state( - InteractionState::Enabled.if_enabled(can_navigate_forward), - )), - ), - ) - .child( - div().w_0().flex_1().h_full().child( - div() - .flex() - .child( - Tab::new() - .title("zed — fish".to_string()) - .icon(Icon::Terminal) - .close_side(IconSide::Right) - .current(true), - ) - .child( - Tab::new() - .title("zed — fish".to_string()) - .icon(Icon::Terminal) - .close_side(IconSide::Right) - .current(false), - ), - ), - ), - ) - // Terminal Pane. - .child(Pane::new( - ScrollState::default(), - Size { - width: relative(1.).into(), - height: rems(36.).into(), - }, - |_, payload| { - let theme = payload.downcast_ref::>().unwrap(); - - vec![crate::static_data::terminal_buffer(&theme).into_any()] - }, - Box::new(theme), - )) - } -} diff --git a/crates/ui/src/components/theme_selector.rs b/crates/ui/src/components/theme_selector.rs deleted file mode 100644 index e6f5237afe1a52eb89fef51955f03821e87976a9..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/theme_selector.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::prelude::*; -use crate::{OrderMethod, Palette, PaletteItem}; - -#[derive(Element)] -pub struct ThemeSelector { - scroll_state: ScrollState, -} - -impl ThemeSelector { - pub fn new() -> Self { - Self { - scroll_state: ScrollState::default(), - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - div().child( - Palette::new(self.scroll_state.clone()) - .items(vec![ - PaletteItem::new("One Dark"), - PaletteItem::new("Rosé Pine"), - PaletteItem::new("Rosé Pine Moon"), - PaletteItem::new("Sandcastle"), - PaletteItem::new("Solarized Dark"), - PaletteItem::new("Summercamp"), - PaletteItem::new("Atelier Cave Light"), - PaletteItem::new("Atelier Dune Light"), - PaletteItem::new("Atelier Estuary Light"), - PaletteItem::new("Atelier Forest Light"), - PaletteItem::new("Atelier Heath Light"), - ]) - .placeholder("Select Theme...") - .empty_string("No matches") - .default_order(OrderMethod::Ascending), - ) - } -} diff --git a/crates/ui/src/components/title_bar.rs b/crates/ui/src/components/title_bar.rs deleted file mode 100644 index dd3d6c1fba652a139235e79eb0823002861901f6..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/title_bar.rs +++ /dev/null @@ -1,117 +0,0 @@ -use std::marker::PhantomData; -use std::sync::atomic::AtomicBool; -use std::sync::Arc; - -use crate::{prelude::*, PlayerWithCallStatus}; -use crate::{ - theme, Avatar, Button, Icon, IconButton, IconColor, PlayerStack, ToolDivider, TrafficLights, -}; - -#[derive(Clone)] -pub struct Livestream { - pub players: Vec, - pub channel: Option, // projects - // windows -} - -#[derive(Element)] -pub struct TitleBar { - view_type: PhantomData, - /// If the window is active from the OS's perspective. - is_active: Arc, - livestream: Option, -} - -impl TitleBar { - pub fn new(cx: &mut ViewContext) -> Self { - let is_active = Arc::new(AtomicBool::new(true)); - let active = is_active.clone(); - - cx.observe_window_activation(move |_, is_active, cx| { - active.store(is_active, std::sync::atomic::Ordering::SeqCst); - cx.notify(); - }) - .detach(); - - Self { - view_type: PhantomData, - is_active, - livestream: None, - } - } - - pub fn set_livestream(mut self, livestream: Option) -> Self { - self.livestream = livestream; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let has_focus = cx.window_is_active(); - - let player_list = if let Some(livestream) = &self.livestream { - livestream.players.clone().into_iter() - } else { - vec![].into_iter() - }; - - div() - .flex() - .items_center() - .justify_between() - .w_full() - .h_8() - .fill(theme.lowest.base.default.background) - .child( - div() - .flex() - .items_center() - .h_full() - .gap_4() - .px_2() - .child(TrafficLights::new().window_has_focus(has_focus)) - // === Project Info === // - .child( - div() - .flex() - .items_center() - .gap_1() - .child(Button::new("zed")) - .child(Button::new("nate/gpui2-ui-components")), - ) - .children(player_list.map(|p| PlayerStack::new(p))) - .child(IconButton::new(Icon::Plus)), - ) - .child( - div() - .flex() - .items_center() - .child( - div() - .px_2() - .flex() - .items_center() - .gap_1() - .child(IconButton::new(Icon::FolderX)) - .child(IconButton::new(Icon::Close)), - ) - .child(ToolDivider::new()) - .child( - div() - .px_2() - .flex() - .items_center() - .gap_1() - .child(IconButton::new(Icon::Mic)) - .child(IconButton::new(Icon::AudioOn)) - .child(IconButton::new(Icon::Screen).color(IconColor::Accent)), - ) - .child( - div().px_2().flex().items_center().child( - Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4") - .shape(Shape::RoundedRectangle), - ), - ), - ) - } -} diff --git a/crates/ui/src/components/toast.rs b/crates/ui/src/components/toast.rs deleted file mode 100644 index c299cdd6bcd52fdded20fa73ccbfd29eb461ed34..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/toast.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::prelude::*; - -#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] -pub enum ToastOrigin { - #[default] - Bottom, - BottomRight, -} - -#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)] -pub enum ToastVariant { - #[default] - Toast, - Status, -} - -/// A toast is a small, temporary window that appears to show a message to the user -/// or indicate a required action. -/// -/// Toasts should not persist on the screen for more than a few seconds unless -/// they are actively showing the a process in progress. -/// -/// Only one toast may be visible at a time. -#[derive(Element)] -pub struct Toast { - origin: ToastOrigin, - children: HackyChildren, - payload: HackyChildrenPayload, -} - -impl Toast { - pub fn new( - origin: ToastOrigin, - children: HackyChildren, - payload: HackyChildrenPayload, - ) -> Self { - Self { - origin, - children, - payload, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let color = ThemeColor::new(cx); - - let mut div = div(); - - if self.origin == ToastOrigin::Bottom { - div = div.right_1_2(); - } else { - div = div.right_4(); - } - - div.absolute() - .bottom_4() - .flex() - .py_2() - .px_1p5() - .min_w_40() - .rounded_md() - .fill(color.elevated_surface) - .max_w_64() - .children_any((self.children)(cx, self.payload.as_ref())) - } -} diff --git a/crates/ui/src/components/toolbar.rs b/crates/ui/src/components/toolbar.rs deleted file mode 100644 index e0953bf3b2d21f7e95e0e22a027a6e8a78f0dd3f..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/toolbar.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::prelude::*; -use crate::theme; - -#[derive(Clone)] -pub struct ToolbarItem {} - -#[derive(Element)] -pub struct Toolbar { - left_items: HackyChildren, - left_items_payload: HackyChildrenPayload, - right_items: HackyChildren, - right_items_payload: HackyChildrenPayload, -} - -impl Toolbar { - pub fn new( - left_items: HackyChildren, - left_items_payload: HackyChildrenPayload, - right_items: HackyChildren, - right_items_payload: HackyChildrenPayload, - ) -> Self { - Self { - left_items, - left_items_payload, - right_items, - right_items_payload, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - .fill(theme.highest.base.default.background) - .p_2() - .flex() - .justify_between() - .child( - div() - .flex() - .children_any((self.left_items)(cx, self.left_items_payload.as_ref())), - ) - .child( - div() - .flex() - .children_any((self.right_items)(cx, self.right_items_payload.as_ref())), - ) - } -} diff --git a/crates/ui/src/components/traffic_lights.rs b/crates/ui/src/components/traffic_lights.rs deleted file mode 100644 index 0d644c49ca83c4c3ad74424721d16fc1fd407c27..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/traffic_lights.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::prelude::*; -use crate::{theme, token, SystemColor}; - -#[derive(Clone, Copy)] -enum TrafficLightColor { - Red, - Yellow, - Green, -} - -#[derive(Element)] -struct TrafficLight { - color: TrafficLightColor, - window_has_focus: bool, -} - -impl TrafficLight { - fn new(color: TrafficLightColor, window_has_focus: bool) -> Self { - Self { - color, - window_has_focus, - } - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let system_color = SystemColor::new(); - - let fill = match (self.window_has_focus, self.color) { - (true, TrafficLightColor::Red) => system_color.mac_os_traffic_light_red, - (true, TrafficLightColor::Yellow) => system_color.mac_os_traffic_light_yellow, - (true, TrafficLightColor::Green) => system_color.mac_os_traffic_light_green, - (false, _) => theme.lowest.base.active.background, - }; - - div().w_3().h_3().rounded_full().fill(fill) - } -} - -#[derive(Element)] -pub struct TrafficLights { - window_has_focus: bool, -} - -impl TrafficLights { - pub fn new() -> Self { - Self { - window_has_focus: true, - } - } - - pub fn window_has_focus(mut self, window_has_focus: bool) -> Self { - self.window_has_focus = window_has_focus; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let token = token(); - - div() - .flex() - .items_center() - .gap_2() - .child(TrafficLight::new( - TrafficLightColor::Red, - self.window_has_focus, - )) - .child(TrafficLight::new( - TrafficLightColor::Yellow, - self.window_has_focus, - )) - .child(TrafficLight::new( - TrafficLightColor::Green, - self.window_has_focus, - )) - } -} diff --git a/crates/ui/src/components/workspace.rs b/crates/ui/src/components/workspace.rs deleted file mode 100644 index b3d375bd647c2281ebe80e4e4618eeed74ac3c3a..0000000000000000000000000000000000000000 --- a/crates/ui/src/components/workspace.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::sync::Arc; - -use chrono::DateTime; -use gpui2::geometry::{relative, rems, Size}; - -use crate::{ - hello_world_rust_editor_with_status_example, prelude::*, random_players_with_call_status, - Livestream, -}; -use crate::{ - theme, v_stack, ChatMessage, ChatPanel, EditorPane, Pane, PaneGroup, Panel, PanelAllowedSides, - PanelSide, ProjectPanel, SplitDirection, StatusBar, Terminal, TitleBar, -}; - -#[derive(Element, Default)] -pub struct WorkspaceElement { - left_panel_scroll_state: ScrollState, - right_panel_scroll_state: ScrollState, - tab_bar_scroll_state: ScrollState, - bottom_panel_scroll_state: ScrollState, -} - -impl WorkspaceElement { - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx).clone(); - - let temp_size = rems(36.).into(); - - let root_group = PaneGroup::new_groups( - vec![ - PaneGroup::new_panes( - vec![ - Pane::new( - ScrollState::default(), - Size { - width: relative(1.).into(), - height: temp_size, - }, - |_, payload| { - let theme = payload.downcast_ref::>().unwrap(); - - vec![EditorPane::new(hello_world_rust_editor_with_status_example( - &theme, - )) - .into_any()] - }, - Box::new(theme.clone()), - ), - Pane::new( - ScrollState::default(), - Size { - width: relative(1.).into(), - height: temp_size, - }, - |_, _| vec![Terminal::new().into_any()], - Box::new(()), - ), - ], - SplitDirection::Vertical, - ), - PaneGroup::new_panes( - vec![Pane::new( - ScrollState::default(), - Size { - width: relative(1.).into(), - height: relative(1.).into(), - }, - |_, payload| { - let theme = payload.downcast_ref::>().unwrap(); - - vec![EditorPane::new(hello_world_rust_editor_with_status_example( - &theme, - )) - .into_any()] - }, - Box::new(theme.clone()), - )], - SplitDirection::Vertical, - ), - ], - SplitDirection::Horizontal, - ); - - div() - .relative() - .size_full() - .flex() - .flex_col() - .font("Zed Sans Extended") - .gap_0() - .justify_start() - .items_start() - .text_color(theme.lowest.base.default.foreground) - .fill(theme.lowest.base.default.background) - .child(TitleBar::new(cx).set_livestream(Some(Livestream { - players: random_players_with_call_status(7), - channel: Some("gpui2-ui".to_string()), - }))) - .child( - div() - .flex_1() - .w_full() - .flex() - .flex_row() - .overflow_hidden() - .border_t() - .border_b() - .border_color(theme.lowest.base.default.border) - .child( - Panel::new( - self.left_panel_scroll_state.clone(), - |_, payload| vec![ProjectPanel::new(ScrollState::default()).into_any()], - Box::new(()), - ) - .side(PanelSide::Left), - ) - .child( - v_stack() - .flex_1() - .h_full() - .child( - div() - .flex() - .flex_1() - // CSS Hack: Flex 1 has to have a set height to properly fill the space - // Or it will give you a height of 0 - .h_px() - .child(root_group), - ) - .child( - Panel::new( - self.bottom_panel_scroll_state.clone(), - |_, _| vec![Terminal::new().into_any()], - Box::new(()), - ) - .allowed_sides(PanelAllowedSides::BottomOnly) - .side(PanelSide::Bottom), - ), - ) - .child( - Panel::new( - self.right_panel_scroll_state.clone(), - |_, payload| { - vec![ChatPanel::new(ScrollState::default()) - .with_messages(vec![ - ChatMessage::new( - "osiewicz".to_string(), - "is this thing on?".to_string(), - DateTime::parse_from_rfc3339( - "2023-09-27T15:40:52.707Z", - ) - .unwrap() - .naive_local(), - ), - ChatMessage::new( - "maxdeviant".to_string(), - "Reading you loud and clear!".to_string(), - DateTime::parse_from_rfc3339( - "2023-09-28T15:40:52.707Z", - ) - .unwrap() - .naive_local(), - ), - ]) - .into_any()] - }, - Box::new(()), - ) - .side(PanelSide::Right), - ), - ) - .child(StatusBar::new()) - // An example of a toast is below - // Currently because of stacking order this gets obscured by other elements - - // .child(Toast::new( - // ToastOrigin::Bottom, - // |_, payload| { - // let theme = payload.downcast_ref::>().unwrap(); - - // vec![Label::new("label").into_any()] - // }, - // Box::new(theme.clone()), - // )) - } -} diff --git a/crates/ui/src/element_ext.rs b/crates/ui/src/element_ext.rs deleted file mode 100644 index 67352f0779774527af4067b37bda662b1fcfdb2c..0000000000000000000000000000000000000000 --- a/crates/ui/src/element_ext.rs +++ /dev/null @@ -1,24 +0,0 @@ -use std::marker::PhantomData; - -use gpui2::Element; - -use crate::theme::{Theme, Themed}; - -pub trait ElementExt: Element { - fn themed(self, theme: Theme) -> Themed - where - Self: Sized; -} - -impl> ElementExt for E { - fn themed(self, theme: Theme) -> Themed - where - Self: Sized, - { - Themed { - child: self, - theme, - view_type: PhantomData, - } - } -} diff --git a/crates/ui/src/elements.rs b/crates/ui/src/elements.rs deleted file mode 100644 index c60902ae9865e5a3756e893745e764c2d3c5f304..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements.rs +++ /dev/null @@ -1,19 +0,0 @@ -mod avatar; -mod button; -mod details; -mod icon; -mod input; -mod label; -mod player; -mod stack; -mod tool_divider; - -pub use avatar::*; -pub use button::*; -pub use details::*; -pub use icon::*; -pub use input::*; -pub use label::*; -pub use player::*; -pub use stack::*; -pub use tool_divider::*; diff --git a/crates/ui/src/elements/avatar.rs b/crates/ui/src/elements/avatar.rs deleted file mode 100644 index 2072b0e501f58c1178f1f29fcd716b8f4051d362..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/avatar.rs +++ /dev/null @@ -1,41 +0,0 @@ -use gpui2::elements::img; -use gpui2::ArcCow; - -use crate::prelude::*; -use crate::theme; - -#[derive(Element, Clone)] -pub struct Avatar { - src: ArcCow<'static, str>, - shape: Shape, -} - -impl Avatar { - pub fn new(src: impl Into>) -> Self { - Self { - src: src.into(), - shape: Shape::Circle, - } - } - - pub fn shape(mut self, shape: Shape) -> Self { - self.shape = shape; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - let mut img = img(); - - if self.shape == Shape::Circle { - img = img.rounded_full(); - } else { - img = img.rounded_md(); - } - - img.uri(self.src.clone()) - .size_4() - .fill(theme.middle.warning.default.foreground) - } -} diff --git a/crates/ui/src/elements/button.rs b/crates/ui/src/elements/button.rs deleted file mode 100644 index c516b27908e25aa4183588084d9fa0e8453fe57b..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/button.rs +++ /dev/null @@ -1,203 +0,0 @@ -use std::rc::Rc; - -use gpui2::geometry::DefiniteLength; -use gpui2::platform::MouseButton; -use gpui2::{EventContext, Hsla, Interactive, WindowContext}; - -use crate::prelude::*; -use crate::{h_stack, theme, Icon, IconColor, IconElement, Label, LabelColor, LabelSize}; - -#[derive(Default, PartialEq, Clone, Copy)] -pub enum IconPosition { - #[default] - Left, - Right, -} - -#[derive(Default, Copy, Clone, PartialEq)] -pub enum ButtonVariant { - #[default] - Ghost, - Filled, -} - -struct ButtonHandlers { - click: Option)>>, -} - -impl Default for ButtonHandlers { - fn default() -> Self { - Self { click: None } - } -} - -#[derive(Element)] -pub struct Button { - label: String, - variant: ButtonVariant, - state: InteractionState, - icon: Option, - icon_position: Option, - width: Option, - handlers: ButtonHandlers, -} - -impl Button { - pub fn new(label: L) -> Self - where - L: Into, - { - Self { - label: label.into(), - variant: Default::default(), - state: Default::default(), - icon: None, - icon_position: None, - width: Default::default(), - handlers: ButtonHandlers::default(), - } - } - - pub fn ghost(label: L) -> Self - where - L: Into, - { - Self::new(label).variant(ButtonVariant::Ghost) - } - - pub fn variant(mut self, variant: ButtonVariant) -> Self { - self.variant = variant; - self - } - - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; - self - } - - pub fn icon(mut self, icon: Icon) -> Self { - self.icon = Some(icon); - self - } - - pub fn icon_position(mut self, icon_position: IconPosition) -> Self { - if self.icon.is_none() { - panic!("An icon must be present if an icon_position is provided."); - } - self.icon_position = Some(icon_position); - self - } - - pub fn width(mut self, width: Option) -> Self { - self.width = width; - self - } - - pub fn on_click(mut self, handler: impl Fn(&mut V, &mut EventContext) + 'static) -> Self { - self.handlers.click = Some(Rc::new(handler)); - self - } - - fn background_color(&self, cx: &mut ViewContext) -> Hsla { - let theme = theme(cx); - let system_color = SystemColor::new(); - - match (self.variant, self.state) { - (ButtonVariant::Ghost, InteractionState::Hovered) => { - theme.lowest.base.hovered.background - } - (ButtonVariant::Ghost, InteractionState::Active) => { - theme.lowest.base.pressed.background - } - (ButtonVariant::Filled, InteractionState::Enabled) => { - theme.lowest.on.default.background - } - (ButtonVariant::Filled, InteractionState::Hovered) => { - theme.lowest.on.hovered.background - } - (ButtonVariant::Filled, InteractionState::Active) => theme.lowest.on.pressed.background, - (ButtonVariant::Filled, InteractionState::Disabled) => { - theme.lowest.on.disabled.background - } - _ => system_color.transparent, - } - } - - fn label_color(&self) -> LabelColor { - match self.state { - InteractionState::Disabled => LabelColor::Disabled, - _ => Default::default(), - } - } - - fn icon_color(&self) -> IconColor { - match self.state { - InteractionState::Disabled => IconColor::Disabled, - _ => Default::default(), - } - } - - fn border_color(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - let system_color = SystemColor::new(); - - match self.state { - InteractionState::Focused => theme.lowest.accent.default.border, - _ => system_color.transparent, - } - } - - fn render_label(&self) -> Label { - Label::new(self.label.clone()) - .size(LabelSize::Small) - .color(self.label_color()) - } - - fn render_icon(&self, icon_color: IconColor) -> Option { - self.icon.map(|i| IconElement::new(i).color(icon_color)) - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let icon_color = self.icon_color(); - let system_color = SystemColor::new(); - let border_color = self.border_color(cx); - - let mut el = h_stack() - .h_6() - .px_1() - .items_center() - .rounded_md() - .border() - .border_color(border_color) - .fill(self.background_color(cx)); - - match (self.icon, self.icon_position) { - (Some(_), Some(IconPosition::Left)) => { - el = el - .gap_1() - .child(self.render_label()) - .children(self.render_icon(icon_color)) - } - (Some(_), Some(IconPosition::Right)) => { - el = el - .gap_1() - .children(self.render_icon(icon_color)) - .child(self.render_label()) - } - (_, _) => el = el.child(self.render_label()), - } - - if let Some(width) = self.width { - el = el.w(width).justify_center(); - } - - if let Some(click_handler) = self.handlers.click.clone() { - el = el.on_mouse_down(MouseButton::Left, move |view, event, cx| { - click_handler(view, cx); - }); - } - - el - } -} diff --git a/crates/ui/src/elements/details.rs b/crates/ui/src/elements/details.rs deleted file mode 100644 index 9c829bcd4160d358c89835e8808eae75ee5de0b0..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/details.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::prelude::*; -use crate::theme; - -#[derive(Element, Clone)] -pub struct Details { - text: &'static str, - meta: Option<&'static str>, -} - -impl Details { - pub fn new(text: &'static str) -> Self { - Self { text, meta: None } - } - - pub fn meta_text(mut self, meta: &'static str) -> Self { - self.meta = Some(meta); - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div() - // .flex() - // .w_full() - .p_1() - .gap_0p5() - .text_xs() - .text_color(theme.lowest.base.default.foreground) - .child(self.text) - .children(self.meta.map(|m| m)) - } -} diff --git a/crates/ui/src/elements/icon.rs b/crates/ui/src/elements/icon.rs deleted file mode 100644 index 26bf7dab22779a6eea220c43f3436f4f0b9bce93..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/icon.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::sync::Arc; - -use gpui2::elements::svg; -use gpui2::Hsla; -use strum::EnumIter; - -use crate::prelude::*; -use crate::theme::theme; -use crate::Theme; - -#[derive(Default, PartialEq, Copy, Clone)] -pub enum IconSize { - Small, - #[default] - Large, -} - -#[derive(Default, PartialEq, Copy, Clone)] -pub enum IconColor { - #[default] - Default, - Muted, - Disabled, - Placeholder, - Accent, - Error, - Warning, - Success, - Info, -} - -impl IconColor { - pub fn color(self, theme: Arc) -> Hsla { - match self { - IconColor::Default => theme.lowest.base.default.foreground, - IconColor::Muted => theme.lowest.variant.default.foreground, - IconColor::Disabled => theme.lowest.base.disabled.foreground, - IconColor::Placeholder => theme.lowest.base.disabled.foreground, - IconColor::Accent => theme.lowest.accent.default.foreground, - IconColor::Error => theme.lowest.negative.default.foreground, - IconColor::Warning => theme.lowest.warning.default.foreground, - IconColor::Success => theme.lowest.positive.default.foreground, - IconColor::Info => theme.lowest.accent.default.foreground, - } - } -} - -#[derive(Default, PartialEq, Copy, Clone, EnumIter)] -pub enum Icon { - Ai, - ArrowLeft, - ArrowRight, - ArrowUpRight, - AudioOff, - AudioOn, - Bolt, - ChevronDown, - ChevronLeft, - ChevronRight, - ChevronUp, - Close, - ExclamationTriangle, - ExternalLink, - File, - FileGeneric, - FileDoc, - FileGit, - FileLock, - FileRust, - FileToml, - FileTree, - Folder, - FolderOpen, - FolderX, - #[default] - Hash, - InlayHint, - MagicWand, - MagnifyingGlass, - Maximize, - Menu, - MessageBubbles, - Mic, - MicMute, - Plus, - Quote, - Screen, - SelectAll, - Split, - SplitMessage, - Terminal, - XCircle, - Copilot, - Envelope, -} - -impl Icon { - pub fn path(self) -> &'static str { - match self { - Icon::Ai => "icons/ai.svg", - Icon::ArrowLeft => "icons/arrow_left.svg", - Icon::ArrowRight => "icons/arrow_right.svg", - Icon::ArrowUpRight => "icons/arrow_up_right.svg", - Icon::AudioOff => "icons/speaker-off.svg", - Icon::AudioOn => "icons/speaker-loud.svg", - Icon::Bolt => "icons/bolt.svg", - Icon::ChevronDown => "icons/chevron_down.svg", - Icon::ChevronLeft => "icons/chevron_left.svg", - Icon::ChevronRight => "icons/chevron_right.svg", - Icon::ChevronUp => "icons/chevron_up.svg", - Icon::Close => "icons/x.svg", - Icon::ExclamationTriangle => "icons/warning.svg", - Icon::ExternalLink => "icons/external_link.svg", - Icon::File => "icons/file.svg", - Icon::FileGeneric => "icons/file_icons/file.svg", - Icon::FileDoc => "icons/file_icons/book.svg", - Icon::FileGit => "icons/file_icons/git.svg", - Icon::FileLock => "icons/file_icons/lock.svg", - Icon::FileRust => "icons/file_icons/rust.svg", - Icon::FileToml => "icons/file_icons/toml.svg", - Icon::FileTree => "icons/project.svg", - Icon::Folder => "icons/file_icons/folder.svg", - Icon::FolderOpen => "icons/file_icons/folder_open.svg", - Icon::FolderX => "icons/stop_sharing.svg", - Icon::Hash => "icons/hash.svg", - Icon::InlayHint => "icons/inlay_hint.svg", - Icon::MagicWand => "icons/magic-wand.svg", - Icon::MagnifyingGlass => "icons/magnifying_glass.svg", - Icon::Maximize => "icons/maximize.svg", - Icon::Menu => "icons/menu.svg", - Icon::MessageBubbles => "icons/conversations.svg", - Icon::Mic => "icons/mic.svg", - Icon::MicMute => "icons/mic-mute.svg", - Icon::Plus => "icons/plus.svg", - Icon::Quote => "icons/quote.svg", - Icon::Screen => "icons/desktop.svg", - Icon::SelectAll => "icons/select-all.svg", - Icon::Split => "icons/split.svg", - Icon::SplitMessage => "icons/split_message.svg", - Icon::Terminal => "icons/terminal.svg", - Icon::XCircle => "icons/error.svg", - Icon::Copilot => "icons/copilot.svg", - Icon::Envelope => "icons/feedback.svg", - } - } -} - -#[derive(Element, Clone)] -pub struct IconElement { - icon: Icon, - color: IconColor, - size: IconSize, -} - -impl IconElement { - pub fn new(icon: Icon) -> Self { - Self { - icon, - color: IconColor::default(), - size: IconSize::default(), - } - } - - pub fn color(mut self, color: IconColor) -> Self { - self.color = color; - self - } - - pub fn size(mut self, size: IconSize) -> Self { - self.size = size; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - let fill = self.color.color(theme); - - let sized_svg = match self.size { - IconSize::Small => svg().size_3p5(), - IconSize::Large => svg().size_4(), - }; - - sized_svg.flex_none().path(self.icon.path()).fill(fill) - } -} diff --git a/crates/ui/src/elements/input.rs b/crates/ui/src/elements/input.rs deleted file mode 100644 index fd860f30c2cd02afce02802ef6476386ffc007ca..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/input.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::prelude::*; -use crate::theme; - -#[derive(Default, PartialEq)] -pub enum InputVariant { - #[default] - Ghost, - Filled, -} - -#[derive(Element)] -pub struct Input { - placeholder: &'static str, - value: String, - state: InteractionState, - variant: InputVariant, -} - -impl Input { - pub fn new(placeholder: &'static str) -> Self { - Self { - placeholder, - value: "".to_string(), - state: InteractionState::default(), - variant: InputVariant::default(), - } - } - - pub fn value(mut self, value: String) -> Self { - self.value = value; - self - } - - pub fn state(mut self, state: InteractionState) -> Self { - self.state = state; - self - } - - pub fn variant(mut self, variant: InputVariant) -> Self { - self.variant = variant; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - let text_el; - let text_color; - let background_color_default; - let background_color_active; - - let mut border_color_default = theme.middle.base.default.border; - let mut border_color_hover = theme.middle.base.hovered.border; - let mut border_color_active = theme.middle.base.pressed.border; - let border_color_focus = theme.middle.base.pressed.background; - - match self.variant { - InputVariant::Ghost => { - background_color_default = theme.middle.base.default.background; - background_color_active = theme.middle.base.active.background; - } - InputVariant::Filled => { - background_color_default = theme.middle.on.default.background; - background_color_active = theme.middle.on.active.background; - } - }; - - if self.state == InteractionState::Focused { - border_color_default = theme.players[0].cursor; - border_color_hover = theme.players[0].cursor; - border_color_active = theme.players[0].cursor; - } - - if self.state == InteractionState::Focused || self.state == InteractionState::Active { - text_el = self.value.clone(); - text_color = theme.lowest.base.default.foreground; - } else { - text_el = self.placeholder.to_string().clone(); - text_color = theme.lowest.base.disabled.foreground; - } - - div() - .h_7() - .w_full() - .px_2() - .border() - .border_color(border_color_default) - .fill(background_color_default) - .hover() - .border_color(border_color_hover) - .active() - .border_color(border_color_active) - .fill(background_color_active) - .flex() - .items_center() - .child( - div() - .flex() - .items_center() - .text_sm() - .text_color(text_color) - .child(text_el) - .child(div().text_color(theme.players[0].cursor).child("|")), - ) - } -} diff --git a/crates/ui/src/elements/label.rs b/crates/ui/src/elements/label.rs deleted file mode 100644 index e7b15aaf02248a24cbfd3c2c5ab874bd43fd86b1..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/label.rs +++ /dev/null @@ -1,161 +0,0 @@ -use gpui2::{Hsla, WindowContext}; -use smallvec::SmallVec; - -use crate::prelude::*; -use crate::theme::theme; - -#[derive(Default, PartialEq, Copy, Clone)] -pub enum LabelColor { - #[default] - Default, - Muted, - Created, - Modified, - Deleted, - Disabled, - Hidden, - Placeholder, - Accent, -} - -impl LabelColor { - pub fn hsla(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - - match self { - Self::Default => theme.middle.base.default.foreground, - Self::Muted => theme.middle.variant.default.foreground, - Self::Created => theme.middle.positive.default.foreground, - Self::Modified => theme.middle.warning.default.foreground, - Self::Deleted => theme.middle.negative.default.foreground, - Self::Disabled => theme.middle.base.disabled.foreground, - Self::Hidden => theme.middle.variant.default.foreground, - Self::Placeholder => theme.middle.base.disabled.foreground, - Self::Accent => theme.middle.accent.default.foreground, - } - } -} - -#[derive(Default, PartialEq, Copy, Clone)] -pub enum LabelSize { - #[default] - Default, - Small, -} - -#[derive(Element, Clone)] -pub struct Label { - label: String, - color: LabelColor, - size: LabelSize, - highlight_indices: Vec, - strikethrough: bool, -} - -impl Label { - pub fn new(label: L) -> Self - where - L: Into, - { - Self { - label: label.into(), - color: LabelColor::Default, - size: LabelSize::Default, - highlight_indices: Vec::new(), - strikethrough: false, - } - } - - pub fn color(mut self, color: LabelColor) -> Self { - self.color = color; - self - } - - pub fn size(mut self, size: LabelSize) -> Self { - self.size = size; - self - } - - pub fn with_highlights(mut self, indices: Vec) -> Self { - self.highlight_indices = indices; - self - } - - pub fn set_strikethrough(mut self, strikethrough: bool) -> Self { - self.strikethrough = strikethrough; - self - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - let highlight_color = theme.lowest.accent.default.foreground; - - let mut highlight_indices = self.highlight_indices.iter().copied().peekable(); - - let mut runs: SmallVec<[Run; 8]> = SmallVec::new(); - - for (char_ix, char) in self.label.char_indices() { - let mut color = self.color.hsla(cx); - - if let Some(highlight_ix) = highlight_indices.peek() { - if char_ix == *highlight_ix { - color = highlight_color; - - highlight_indices.next(); - } - } - - let last_run = runs.last_mut(); - - let start_new_run = if let Some(last_run) = last_run { - if color == last_run.color { - last_run.text.push(char); - false - } else { - true - } - } else { - true - }; - - if start_new_run { - runs.push(Run { - text: char.to_string(), - color, - }); - } - } - - div() - .flex() - .when(self.strikethrough, |this| { - this.relative().child( - div() - .absolute() - .top_px() - .my_auto() - .w_full() - .h_px() - .fill(LabelColor::Hidden.hsla(cx)), - ) - }) - .children(runs.into_iter().map(|run| { - let mut div = div(); - - if self.size == LabelSize::Small { - div = div.text_xs(); - } else { - div = div.text_sm(); - } - - div.text_color(run.color).child(run.text) - })) - } -} - -/// A run of text that receives the same style. -struct Run { - pub text: String, - pub color: Hsla, -} diff --git a/crates/ui/src/elements/player.rs b/crates/ui/src/elements/player.rs deleted file mode 100644 index 465542dc7f39a0ce397e978da70520c7aa137897..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/player.rs +++ /dev/null @@ -1,133 +0,0 @@ -use gpui2::{Hsla, ViewContext}; - -use crate::theme; - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum PlayerStatus { - #[default] - Offline, - Online, - InCall, - Away, - DoNotDisturb, - Invisible, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum MicStatus { - Muted, - #[default] - Unmuted, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum VideoStatus { - On, - #[default] - Off, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] -pub enum ScreenShareStatus { - Shared, - #[default] - NotShared, -} - -#[derive(Clone)] -pub struct PlayerCallStatus { - pub mic_status: MicStatus, - /// Indicates if the player is currently speaking - /// And the intensity of the volume coming through - /// - /// 0.0 - 1.0 - pub voice_activity: f32, - pub video_status: VideoStatus, - pub screen_share_status: ScreenShareStatus, - pub in_current_project: bool, - pub disconnected: bool, - pub following: Option>, - pub followers: Option>, -} - -impl PlayerCallStatus { - pub fn new() -> Self { - Self { - mic_status: MicStatus::default(), - voice_activity: 0., - video_status: VideoStatus::default(), - screen_share_status: ScreenShareStatus::default(), - in_current_project: true, - disconnected: false, - following: None, - followers: None, - } - } -} - -#[derive(PartialEq, Clone)] -pub struct Player { - index: usize, - avatar_src: String, - username: String, - status: PlayerStatus, -} - -#[derive(Clone)] -pub struct PlayerWithCallStatus { - player: Player, - call_status: PlayerCallStatus, -} - -impl PlayerWithCallStatus { - pub fn new(player: Player, call_status: PlayerCallStatus) -> Self { - Self { - player, - call_status, - } - } - - pub fn get_player(&self) -> &Player { - &self.player - } - - pub fn get_call_status(&self) -> &PlayerCallStatus { - &self.call_status - } -} - -impl Player { - pub fn new(index: usize, avatar_src: String, username: String) -> Self { - Self { - index, - avatar_src, - username, - status: Default::default(), - } - } - - pub fn set_status(mut self, status: PlayerStatus) -> Self { - self.status = status; - self - } - - pub fn cursor_color(&self, cx: &mut ViewContext) -> Hsla { - let theme = theme(cx); - let index = self.index % 8; - theme.players[self.index].cursor - } - - pub fn selection_color(&self, cx: &mut ViewContext) -> Hsla { - let theme = theme(cx); - let index = self.index % 8; - theme.players[self.index].selection - } - - pub fn avatar_src(&self) -> &str { - &self.avatar_src - } - - pub fn index(&self) -> usize { - self.index - } -} diff --git a/crates/ui/src/elements/stack.rs b/crates/ui/src/elements/stack.rs deleted file mode 100644 index ef186f5ebe7fb6f80cfbb6445a09e84e5a36e5c3..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/stack.rs +++ /dev/null @@ -1,31 +0,0 @@ -use gpui2::elements::div::Div; - -use crate::prelude::*; - -pub trait Stack: StyleHelpers { - /// Horizontally stacks elements. - fn h_stack(self) -> Self { - self.flex().flex_row().items_center() - } - - /// Vertically stacks elements. - fn v_stack(self) -> Self { - self.flex().flex_col() - } -} - -impl Stack for Div {} - -/// Horizontally stacks elements. -/// -/// Sets `flex()`, `flex_row()`, `items_center()` -pub fn h_stack() -> Div { - div().h_stack() -} - -/// Vertically stacks elements. -/// -/// Sets `flex()`, `flex_col()` -pub fn v_stack() -> Div { - div().v_stack() -} diff --git a/crates/ui/src/elements/tool_divider.rs b/crates/ui/src/elements/tool_divider.rs deleted file mode 100644 index 8b5a191445b389135ca88fee484d42e296ffa516..0000000000000000000000000000000000000000 --- a/crates/ui/src/elements/tool_divider.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::prelude::*; -use crate::theme; - -#[derive(Element)] -pub struct ToolDivider {} - -impl ToolDivider { - pub fn new() -> Self { - Self {} - } - - fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { - let theme = theme(cx); - - div().w_px().h_3().fill(theme.lowest.base.default.border) - } -} diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs deleted file mode 100644 index 39156d3ab46df504ebc3dc963c95cf2b4cd3335a..0000000000000000000000000000000000000000 --- a/crates/ui/src/lib.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![allow(dead_code, unused_variables)] - -mod children; -mod components; -mod element_ext; -mod elements; -pub mod prelude; -mod static_data; -mod theme; -mod tokens; - -pub use children::*; -pub use components::*; -pub use element_ext::*; -pub use elements::*; -pub use prelude::*; -pub use static_data::*; -pub use tokens::*; - -pub use crate::theme::*; diff --git a/crates/ui/src/prelude.rs b/crates/ui/src/prelude.rs deleted file mode 100644 index e0da3579e21df1ce5fca02451688f9ca7b5014de..0000000000000000000000000000000000000000 --- a/crates/ui/src/prelude.rs +++ /dev/null @@ -1,274 +0,0 @@ -pub use gpui2::elements::div::{div, ScrollState}; -pub use gpui2::style::{StyleHelpers, Styleable}; -pub use gpui2::{Element, IntoElement, ParentElement, ViewContext}; - -pub use crate::{theme, ButtonVariant, HackyChildren, HackyChildrenPayload, InputVariant, Theme}; - -use gpui2::{hsla, rgb, Hsla, WindowContext}; -use strum::EnumIter; - -#[derive(Default)] -pub struct SystemColor { - pub transparent: Hsla, - pub mac_os_traffic_light_red: Hsla, - pub mac_os_traffic_light_yellow: Hsla, - pub mac_os_traffic_light_green: Hsla, -} - -impl SystemColor { - pub fn new() -> SystemColor { - SystemColor { - transparent: hsla(0.0, 0.0, 0.0, 0.0), - mac_os_traffic_light_red: rgb::(0xEC695E), - mac_os_traffic_light_yellow: rgb::(0xF4BF4F), - mac_os_traffic_light_green: rgb::(0x62C554), - } - } - pub fn color(&self) -> Hsla { - self.transparent - } -} - -#[derive(Clone, Copy)] -pub struct ThemeColor { - pub border: Hsla, - pub border_variant: Hsla, - /// The background color of an elevated surface, like a modal, tooltip or toast. - pub elevated_surface: Hsla, -} - -impl ThemeColor { - pub fn new(cx: &WindowContext) -> Self { - let theme = theme(cx); - - Self { - border: theme.lowest.base.default.border, - border_variant: theme.lowest.variant.default.border, - elevated_surface: theme.middle.base.default.background, - } - } -} - -#[derive(Default, PartialEq, EnumIter, Clone, Copy)] -pub enum HighlightColor { - #[default] - Default, - Comment, - String, - Function, - Keyword, -} - -impl HighlightColor { - pub fn hsla(&self, theme: &Theme) -> Hsla { - let system_color = SystemColor::new(); - - match self { - Self::Default => theme - .syntax - .get("primary") - .expect("no theme.syntax.primary") - .clone(), - Self::Comment => theme - .syntax - .get("comment") - .expect("no theme.syntax.comment") - .clone(), - Self::String => theme - .syntax - .get("string") - .expect("no theme.syntax.string") - .clone(), - Self::Function => theme - .syntax - .get("function") - .expect("no theme.syntax.function") - .clone(), - Self::Keyword => theme - .syntax - .get("keyword") - .expect("no theme.syntax.keyword") - .clone(), - } - } -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum FileSystemStatus { - #[default] - None, - Conflict, - Deleted, -} - -impl FileSystemStatus { - pub fn to_string(&self) -> String { - match self { - Self::None => "None".to_string(), - Self::Conflict => "Conflict".to_string(), - Self::Deleted => "Deleted".to_string(), - } - } -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum GitStatus { - #[default] - None, - Created, - Modified, - Deleted, - Conflict, - Renamed, -} - -impl GitStatus { - pub fn to_string(&self) -> String { - match self { - Self::None => "None".to_string(), - Self::Created => "Created".to_string(), - Self::Modified => "Modified".to_string(), - Self::Deleted => "Deleted".to_string(), - Self::Conflict => "Conflict".to_string(), - Self::Renamed => "Renamed".to_string(), - } - } - - pub fn hsla(&self, cx: &WindowContext) -> Hsla { - let theme = theme(cx); - let system_color = SystemColor::new(); - - match self { - Self::None => system_color.transparent, - Self::Created => theme.lowest.positive.default.foreground, - Self::Modified => theme.lowest.warning.default.foreground, - Self::Deleted => theme.lowest.negative.default.foreground, - Self::Conflict => theme.lowest.warning.default.foreground, - Self::Renamed => theme.lowest.accent.default.foreground, - } - } -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum DiagnosticStatus { - #[default] - None, - Error, - Warning, - Info, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum IconSide { - #[default] - Left, - Right, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum OrderMethod { - #[default] - Ascending, - Descending, - MostRecent, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum Shape { - #[default] - Circle, - RoundedRectangle, -} - -#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)] -pub enum DisclosureControlVisibility { - #[default] - OnHover, - Always, -} - -#[derive(Default, PartialEq, Copy, Clone, EnumIter, strum::Display)] -pub enum InteractionState { - #[default] - Enabled, - Hovered, - Active, - Focused, - Disabled, -} - -impl InteractionState { - pub fn if_enabled(&self, enabled: bool) -> Self { - if enabled { - *self - } else { - InteractionState::Disabled - } - } -} - -#[derive(Default, PartialEq)] -pub enum SelectedState { - #[default] - Unselected, - PartiallySelected, - Selected, -} - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -pub enum Toggleable { - Toggleable(ToggleState), - #[default] - NotToggleable, -} - -impl Toggleable { - pub fn is_toggled(&self) -> bool { - match self { - Self::Toggleable(ToggleState::Toggled) => true, - _ => false, - } - } -} - -impl From for Toggleable { - fn from(state: ToggleState) -> Self { - Self::Toggleable(state) - } -} - -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -pub enum ToggleState { - /// The "on" state of a toggleable element. - /// - /// Example: - /// - A collasable list that is currently expanded - /// - A toggle button that is currently on. - Toggled, - /// The "off" state of a toggleable element. - /// - /// Example: - /// - A collasable list that is currently collapsed - /// - A toggle button that is currently off. - #[default] - NotToggled, -} - -impl From for ToggleState { - fn from(toggleable: Toggleable) -> Self { - match toggleable { - Toggleable::Toggleable(state) => state, - Toggleable::NotToggleable => ToggleState::NotToggled, - } - } -} - -impl From for ToggleState { - fn from(toggled: bool) -> Self { - if toggled { - ToggleState::Toggled - } else { - ToggleState::NotToggled - } - } -} diff --git a/crates/ui/src/static_data.rs b/crates/ui/src/static_data.rs deleted file mode 100644 index b8c4e18f14c31cfb018fe581353e43d2fca971f6..0000000000000000000000000000000000000000 --- a/crates/ui/src/static_data.rs +++ /dev/null @@ -1,966 +0,0 @@ -use std::path::PathBuf; -use std::str::FromStr; - -use rand::Rng; - -use crate::{ - Buffer, BufferRow, BufferRows, Editor, FileSystemStatus, GitStatus, HighlightColor, - HighlightedLine, HighlightedText, Icon, Keybinding, Label, LabelColor, ListEntry, - ListEntrySize, ListItem, Livestream, MicStatus, ModifierKeys, PaletteItem, Player, - PlayerCallStatus, PlayerWithCallStatus, ScreenShareStatus, Symbol, Tab, Theme, ToggleState, - VideoStatus, -}; - -pub fn static_tabs_example() -> Vec { - vec![ - Tab::new() - .title("wip.rs".to_string()) - .icon(Icon::FileRust) - .current(false) - .fs_status(FileSystemStatus::Deleted), - Tab::new() - .title("Cargo.toml".to_string()) - .icon(Icon::FileToml) - .current(false) - .git_status(GitStatus::Modified), - Tab::new() - .title("Channels Panel".to_string()) - .icon(Icon::Hash) - .current(false), - Tab::new() - .title("channels_panel.rs".to_string()) - .icon(Icon::FileRust) - .current(true) - .git_status(GitStatus::Modified), - Tab::new() - .title("workspace.rs".to_string()) - .current(false) - .icon(Icon::FileRust) - .git_status(GitStatus::Modified), - Tab::new() - .title("icon_button.rs".to_string()) - .icon(Icon::FileRust) - .current(false), - Tab::new() - .title("storybook.rs".to_string()) - .icon(Icon::FileRust) - .current(false) - .git_status(GitStatus::Created), - Tab::new() - .title("theme.rs".to_string()) - .icon(Icon::FileRust) - .current(false), - Tab::new() - .title("theme_registry.rs".to_string()) - .icon(Icon::FileRust) - .current(false), - Tab::new() - .title("styleable_helpers.rs".to_string()) - .icon(Icon::FileRust) - .current(false), - ] -} - -pub fn static_tabs_1() -> Vec { - vec![ - Tab::new() - .title("project_panel.rs".to_string()) - .icon(Icon::FileRust) - .current(false) - .fs_status(FileSystemStatus::Deleted), - Tab::new() - .title("tab_bar.rs".to_string()) - .icon(Icon::FileRust) - .current(false) - .git_status(GitStatus::Modified), - Tab::new() - .title("workspace.rs".to_string()) - .icon(Icon::FileRust) - .current(false), - Tab::new() - .title("tab.rs".to_string()) - .icon(Icon::FileRust) - .current(true) - .git_status(GitStatus::Modified), - ] -} - -pub fn static_tabs_2() -> Vec { - vec![ - Tab::new() - .title("tab_bar.rs".to_string()) - .icon(Icon::FileRust) - .current(false) - .fs_status(FileSystemStatus::Deleted), - Tab::new() - .title("static_data.rs".to_string()) - .icon(Icon::FileRust) - .current(true) - .git_status(GitStatus::Modified), - ] -} - -pub fn static_tabs_3() -> Vec { - vec![Tab::new().git_status(GitStatus::Created).current(true)] -} - -pub fn static_players() -> Vec { - vec![ - Player::new( - 0, - "https://avatars.githubusercontent.com/u/1714999?v=4".into(), - "nathansobo".into(), - ), - Player::new( - 1, - "https://avatars.githubusercontent.com/u/326587?v=4".into(), - "maxbrunsfeld".into(), - ), - Player::new( - 2, - "https://avatars.githubusercontent.com/u/482957?v=4".into(), - "as-cii".into(), - ), - Player::new( - 3, - "https://avatars.githubusercontent.com/u/1714999?v=4".into(), - "iamnbutler".into(), - ), - Player::new( - 4, - "https://avatars.githubusercontent.com/u/1486634?v=4".into(), - "maxdeviant".into(), - ), - ] -} - -#[derive(Debug)] -pub struct PlayerData { - pub url: String, - pub name: String, -} -pub fn static_player_data() -> Vec { - vec![ - PlayerData { - url: "https://avatars.githubusercontent.com/u/1714999?v=4".into(), - name: "iamnbutler".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/326587?v=4".into(), - name: "maxbrunsfeld".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/482957?v=4".into(), - name: "as-cii".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/1789?v=4".into(), - name: "nathansobo".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/1486634?v=4".into(), - name: "ForLoveOfCats".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/2690773?v=4".into(), - name: "SomeoneToIgnore".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/19867440?v=4".into(), - name: "JosephTLyons".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/24362066?v=4".into(), - name: "osiewicz".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/22121886?v=4".into(), - name: "KCaverly".into(), - }, - PlayerData { - url: "https://avatars.githubusercontent.com/u/1486634?v=4".into(), - name: "maxdeviant".into(), - }, - ] -} -pub fn create_static_players(player_data: Vec) -> Vec { - let mut players = Vec::new(); - for data in player_data { - players.push(Player::new(players.len(), data.url, data.name)); - } - players -} -pub fn static_player_1(data: &Vec) -> Player { - Player::new(1, data[0].url.clone(), data[0].name.clone()) -} -pub fn static_player_2(data: &Vec) -> Player { - Player::new(2, data[1].url.clone(), data[1].name.clone()) -} -pub fn static_player_3(data: &Vec) -> Player { - Player::new(3, data[2].url.clone(), data[2].name.clone()) -} -pub fn static_player_4(data: &Vec) -> Player { - Player::new(4, data[3].url.clone(), data[3].name.clone()) -} -pub fn static_player_5(data: &Vec) -> Player { - Player::new(5, data[4].url.clone(), data[4].name.clone()) -} -pub fn static_player_6(data: &Vec) -> Player { - Player::new(6, data[5].url.clone(), data[5].name.clone()) -} -pub fn static_player_7(data: &Vec) -> Player { - Player::new(7, data[6].url.clone(), data[6].name.clone()) -} -pub fn static_player_8(data: &Vec) -> Player { - Player::new(8, data[7].url.clone(), data[7].name.clone()) -} -pub fn static_player_9(data: &Vec) -> Player { - Player::new(9, data[8].url.clone(), data[8].name.clone()) -} -pub fn static_player_10(data: &Vec) -> Player { - Player::new(10, data[9].url.clone(), data[9].name.clone()) -} -pub fn static_livestream() -> Livestream { - Livestream { - players: random_players_with_call_status(7), - channel: Some("gpui2-ui".to_string()), - } -} -pub fn populate_player_call_status( - player: Player, - followers: Option>, -) -> PlayerCallStatus { - let mut rng = rand::thread_rng(); - let in_current_project: bool = rng.gen(); - let disconnected: bool = rng.gen(); - let voice_activity: f32 = rng.gen(); - let mic_status = if rng.gen_bool(0.5) { - MicStatus::Muted - } else { - MicStatus::Unmuted - }; - let video_status = if rng.gen_bool(0.5) { - VideoStatus::On - } else { - VideoStatus::Off - }; - let screen_share_status = if rng.gen_bool(0.5) { - ScreenShareStatus::Shared - } else { - ScreenShareStatus::NotShared - }; - PlayerCallStatus { - mic_status, - voice_activity, - video_status, - screen_share_status, - in_current_project, - disconnected, - following: None, - followers, - } -} -pub fn random_players_with_call_status(number_of_players: usize) -> Vec { - let players = create_static_players(static_player_data()); - let mut player_status = vec![]; - for i in 0..number_of_players { - let followers = if i == 0 { - Some(vec![ - players[1].clone(), - players[3].clone(), - players[5].clone(), - players[6].clone(), - ]) - } else if i == 1 { - Some(vec![players[2].clone(), players[6].clone()]) - } else { - None - }; - let call_status = populate_player_call_status(players[i].clone(), followers); - player_status.push(PlayerWithCallStatus::new(players[i].clone(), call_status)); - } - player_status -} - -pub fn static_players_with_call_status() -> Vec { - let players = static_players(); - let mut player_0_status = PlayerCallStatus::new(); - let player_1_status = PlayerCallStatus::new(); - let player_2_status = PlayerCallStatus::new(); - let mut player_3_status = PlayerCallStatus::new(); - let mut player_4_status = PlayerCallStatus::new(); - - player_0_status.screen_share_status = ScreenShareStatus::Shared; - player_0_status.followers = Some(vec![players[1].clone(), players[3].clone()]); - - player_3_status.voice_activity = 0.5; - player_4_status.mic_status = MicStatus::Muted; - player_4_status.in_current_project = false; - - vec![ - PlayerWithCallStatus::new(players[0].clone(), player_0_status), - PlayerWithCallStatus::new(players[1].clone(), player_1_status), - PlayerWithCallStatus::new(players[2].clone(), player_2_status), - PlayerWithCallStatus::new(players[3].clone(), player_3_status), - PlayerWithCallStatus::new(players[4].clone(), player_4_status), - ] -} - -pub fn static_project_panel_project_items() -> Vec { - vec![ - ListEntry::new(Label::new("zed")) - .left_icon(Icon::FolderOpen.into()) - .indent_level(0) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new(".cargo")) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new(".config")) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new(".git").color(LabelColor::Hidden)) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new(".cargo")) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new(".idea").color(LabelColor::Hidden)) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new("assets")) - .left_icon(Icon::Folder.into()) - .indent_level(1) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("cargo-target").color(LabelColor::Hidden)) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new("crates")) - .left_icon(Icon::FolderOpen.into()) - .indent_level(1) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("activity_indicator")) - .left_icon(Icon::Folder.into()) - .indent_level(2), - ListEntry::new(Label::new("ai")) - .left_icon(Icon::Folder.into()) - .indent_level(2), - ListEntry::new(Label::new("audio")) - .left_icon(Icon::Folder.into()) - .indent_level(2), - ListEntry::new(Label::new("auto_update")) - .left_icon(Icon::Folder.into()) - .indent_level(2), - ListEntry::new(Label::new("breadcrumbs")) - .left_icon(Icon::Folder.into()) - .indent_level(2), - ListEntry::new(Label::new("call")) - .left_icon(Icon::Folder.into()) - .indent_level(2), - ListEntry::new(Label::new("sqlez").color(LabelColor::Modified)) - .left_icon(Icon::Folder.into()) - .indent_level(2) - .set_toggle(ToggleState::NotToggled), - ListEntry::new(Label::new("gpui2")) - .left_icon(Icon::FolderOpen.into()) - .indent_level(2) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("src")) - .left_icon(Icon::FolderOpen.into()) - .indent_level(3) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("derive_element.rs")) - .left_icon(Icon::FileRust.into()) - .indent_level(4), - ListEntry::new(Label::new("storybook").color(LabelColor::Modified)) - .left_icon(Icon::FolderOpen.into()) - .indent_level(1) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("docs").color(LabelColor::Default)) - .left_icon(Icon::Folder.into()) - .indent_level(2) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("src").color(LabelColor::Modified)) - .left_icon(Icon::FolderOpen.into()) - .indent_level(3) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("ui").color(LabelColor::Modified)) - .left_icon(Icon::FolderOpen.into()) - .indent_level(4) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("component").color(LabelColor::Created)) - .left_icon(Icon::FolderOpen.into()) - .indent_level(5) - .set_toggle(ToggleState::Toggled), - ListEntry::new(Label::new("facepile.rs").color(LabelColor::Default)) - .left_icon(Icon::FileRust.into()) - .indent_level(6), - ListEntry::new(Label::new("follow_group.rs").color(LabelColor::Default)) - .left_icon(Icon::FileRust.into()) - .indent_level(6), - ListEntry::new(Label::new("list_item.rs").color(LabelColor::Created)) - .left_icon(Icon::FileRust.into()) - .indent_level(6), - ListEntry::new(Label::new("tab.rs").color(LabelColor::Default)) - .left_icon(Icon::FileRust.into()) - .indent_level(6), - ListEntry::new(Label::new("target").color(LabelColor::Hidden)) - .left_icon(Icon::Folder.into()) - .indent_level(1), - ListEntry::new(Label::new(".dockerignore")) - .left_icon(Icon::FileGeneric.into()) - .indent_level(1), - ListEntry::new(Label::new(".DS_Store").color(LabelColor::Hidden)) - .left_icon(Icon::FileGeneric.into()) - .indent_level(1), - ListEntry::new(Label::new("Cargo.lock")) - .left_icon(Icon::FileLock.into()) - .indent_level(1), - ListEntry::new(Label::new("Cargo.toml")) - .left_icon(Icon::FileToml.into()) - .indent_level(1), - ListEntry::new(Label::new("Dockerfile")) - .left_icon(Icon::FileGeneric.into()) - .indent_level(1), - ListEntry::new(Label::new("Procfile")) - .left_icon(Icon::FileGeneric.into()) - .indent_level(1), - ListEntry::new(Label::new("README.md")) - .left_icon(Icon::FileDoc.into()) - .indent_level(1), - ] - .into_iter() - .map(From::from) - .collect() -} - -pub fn static_project_panel_single_items() -> Vec { - vec![ - ListEntry::new(Label::new("todo.md")) - .left_icon(Icon::FileDoc.into()) - .indent_level(0), - ListEntry::new(Label::new("README.md")) - .left_icon(Icon::FileDoc.into()) - .indent_level(0), - ListEntry::new(Label::new("config.json")) - .left_icon(Icon::FileGeneric.into()) - .indent_level(0), - ] - .into_iter() - .map(From::from) - .collect() -} - -pub fn static_collab_panel_current_call() -> Vec { - vec![ - ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"), - ListEntry::new(Label::new("nathansobo")) - .left_avatar("http://github.com/nathansobo.png?s=50"), - ListEntry::new(Label::new("maxbrunsfeld")) - .left_avatar("http://github.com/maxbrunsfeld.png?s=50"), - ] - .into_iter() - .map(From::from) - .collect() -} - -pub fn static_collab_panel_channels() -> Vec { - vec![ - ListEntry::new(Label::new("zed")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(0), - ListEntry::new(Label::new("community")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(1), - ListEntry::new(Label::new("dashboards")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("feedback")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("teams-in-channels-alpha")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("current-projects")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(1), - ListEntry::new(Label::new("codegen")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("gpui2")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("livestreaming")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("open-source")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("replace")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("semantic-index")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("vim")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ListEntry::new(Label::new("web-tech")) - .left_icon(Icon::Hash.into()) - .size(ListEntrySize::Medium) - .indent_level(2), - ] - .into_iter() - .map(From::from) - .collect() -} - -pub fn example_editor_actions() -> Vec { - vec![ - PaletteItem::new("New File").keybinding(Keybinding::new( - "N".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Open File").keybinding(Keybinding::new( - "O".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Save File").keybinding(Keybinding::new( - "S".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Cut").keybinding(Keybinding::new( - "X".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Copy").keybinding(Keybinding::new( - "C".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Paste").keybinding(Keybinding::new( - "V".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Undo").keybinding(Keybinding::new( - "Z".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Redo").keybinding(Keybinding::new( - "Z".to_string(), - ModifierKeys::new().control(true).shift(true), - )), - PaletteItem::new("Find").keybinding(Keybinding::new( - "F".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Replace").keybinding(Keybinding::new( - "R".to_string(), - ModifierKeys::new().control(true), - )), - PaletteItem::new("Jump to Line"), - PaletteItem::new("Select All"), - PaletteItem::new("Deselect All"), - PaletteItem::new("Switch Document"), - PaletteItem::new("Insert Line Below"), - PaletteItem::new("Insert Line Above"), - PaletteItem::new("Move Line Up"), - PaletteItem::new("Move Line Down"), - PaletteItem::new("Toggle Comment"), - PaletteItem::new("Delete Line"), - ] -} - -pub fn empty_editor_example() -> Editor { - Editor { - tabs: static_tabs_example(), - path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(), - symbols: vec![], - buffer: empty_buffer_example(), - } -} - -pub fn empty_buffer_example() -> Buffer { - Buffer::new().set_rows(Some(BufferRows::default())) -} - -pub fn hello_world_rust_editor_example(theme: &Theme) -> Editor { - Editor { - tabs: static_tabs_example(), - path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(), - symbols: vec![Symbol(vec![ - HighlightedText { - text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "main".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - ])], - buffer: hello_world_rust_buffer_example(theme), - } -} - -pub fn hello_world_rust_buffer_example(theme: &Theme) -> Buffer { - Buffer::new() - .set_title("hello_world.rs".to_string()) - .set_path("src/hello_world.rs".to_string()) - .set_language("rust".to_string()) - .set_rows(Some(BufferRows { - show_line_numbers: true, - rows: hello_world_rust_buffer_rows(theme), - })) -} - -pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec { - let show_line_number = true; - - vec![ - BufferRow { - line_number: 1, - code_action: false, - current: true, - line: Some(HighlightedLine { - highlighted_texts: vec![ - HighlightedText { - text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "main".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - HighlightedText { - text: "() {".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - ], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 2, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: " // Statements here are executed when the compiled binary is called." - .to_string(), - color: HighlightColor::Comment.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 3, - code_action: false, - current: false, - line: None, - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 4, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: " // Print text to the console.".to_string(), - color: HighlightColor::Comment.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 5, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![ - HighlightedText { - text: " println!(".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - HighlightedText { - text: "\"Hello, world!\"".to_string(), - color: HighlightColor::String.hsla(&theme), - }, - HighlightedText { - text: ");".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - ], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 6, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: "}".to_string(), - color: HighlightColor::Default.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - ] -} - -pub fn hello_world_rust_editor_with_status_example(theme: &Theme) -> Editor { - Editor { - tabs: static_tabs_example(), - path: PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(), - symbols: vec![Symbol(vec![ - HighlightedText { - text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "main".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - ])], - buffer: hello_world_rust_buffer_with_status_example(theme), - } -} - -pub fn hello_world_rust_buffer_with_status_example(theme: &Theme) -> Buffer { - Buffer::new() - .set_title("hello_world.rs".to_string()) - .set_path("src/hello_world.rs".to_string()) - .set_language("rust".to_string()) - .set_rows(Some(BufferRows { - show_line_numbers: true, - rows: hello_world_rust_with_status_buffer_rows(theme), - })) -} - -pub fn hello_world_rust_with_status_buffer_rows(theme: &Theme) -> Vec { - let show_line_number = true; - - vec![ - BufferRow { - line_number: 1, - code_action: false, - current: true, - line: Some(HighlightedLine { - highlighted_texts: vec![ - HighlightedText { - text: "fn ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "main".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - HighlightedText { - text: "() {".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - ], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 2, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: "// Statements here are executed when the compiled binary is called." - .to_string(), - color: HighlightColor::Comment.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::Modified, - show_line_number, - }, - BufferRow { - line_number: 3, - code_action: false, - current: false, - line: None, - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 4, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: " // Print text to the console.".to_string(), - color: HighlightColor::Comment.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 5, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![ - HighlightedText { - text: " println!(".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - HighlightedText { - text: "\"Hello, world!\"".to_string(), - color: HighlightColor::String.hsla(&theme), - }, - HighlightedText { - text: ");".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - ], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 6, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: "}".to_string(), - color: HighlightColor::Default.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 7, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: "".to_string(), - color: HighlightColor::Default.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::Created, - show_line_number, - }, - BufferRow { - line_number: 8, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: "// Marshall and Nate were here".to_string(), - color: HighlightColor::Comment.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::Created, - show_line_number, - }, - ] -} - -pub fn terminal_buffer(theme: &Theme) -> Buffer { - Buffer::new() - .set_title("zed — fish".to_string()) - .set_rows(Some(BufferRows { - show_line_numbers: false, - rows: terminal_buffer_rows(theme), - })) -} - -pub fn terminal_buffer_rows(theme: &Theme) -> Vec { - let show_line_number = false; - - vec![ - BufferRow { - line_number: 1, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![ - HighlightedText { - text: "maxdeviant ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - HighlightedText { - text: "in ".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - HighlightedText { - text: "profaned-capital ".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - HighlightedText { - text: "in ".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - HighlightedText { - text: "~/p/zed ".to_string(), - color: HighlightColor::Function.hsla(&theme), - }, - HighlightedText { - text: "on ".to_string(), - color: HighlightColor::Default.hsla(&theme), - }, - HighlightedText { - text: " gpui2-ui ".to_string(), - color: HighlightColor::Keyword.hsla(&theme), - }, - ], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - BufferRow { - line_number: 2, - code_action: false, - current: false, - line: Some(HighlightedLine { - highlighted_texts: vec![HighlightedText { - text: "λ ".to_string(), - color: HighlightColor::String.hsla(&theme), - }], - }), - cursors: None, - status: GitStatus::None, - show_line_number, - }, - ] -} diff --git a/crates/ui/src/theme.rs b/crates/ui/src/theme.rs deleted file mode 100644 index d4c8c262739d102c2515c097d88f8da22e70a515..0000000000000000000000000000000000000000 --- a/crates/ui/src/theme.rs +++ /dev/null @@ -1,196 +0,0 @@ -use std::collections::HashMap; -use std::fmt; -use std::marker::PhantomData; -use std::sync::Arc; - -use gpui2::color::Hsla; -use gpui2::element::Element; -use gpui2::{serde_json, AppContext, IntoElement, Vector2F, ViewContext, WindowContext}; -use serde::de::Visitor; -use serde::{Deserialize, Deserializer}; -use theme::ThemeSettings; - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct Theme { - pub name: String, - pub is_light: bool, - pub lowest: Layer, - pub middle: Layer, - pub highest: Layer, - pub popover_shadow: Shadow, - pub modal_shadow: Shadow, - #[serde(deserialize_with = "deserialize_player_colors")] - pub players: Vec, - #[serde(deserialize_with = "deserialize_syntax_colors")] - pub syntax: HashMap, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct Layer { - pub base: StyleSet, - pub variant: StyleSet, - pub on: StyleSet, - pub accent: StyleSet, - pub positive: StyleSet, - pub warning: StyleSet, - pub negative: StyleSet, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct StyleSet { - #[serde(rename = "default")] - pub default: ContainerColors, - pub hovered: ContainerColors, - pub pressed: ContainerColors, - pub active: ContainerColors, - pub disabled: ContainerColors, - pub inverted: ContainerColors, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct ContainerColors { - pub background: Hsla, - pub foreground: Hsla, - pub border: Hsla, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct PlayerColors { - pub selection: Hsla, - pub cursor: Hsla, -} - -#[derive(Deserialize, Clone, Default, Debug)] -pub struct Shadow { - pub blur: u8, - pub color: Hsla, - pub offset: Vec, -} - -fn deserialize_player_colors<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - struct PlayerArrayVisitor; - - impl<'de> Visitor<'de> for PlayerArrayVisitor { - type Value = Vec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an object with integer keys") - } - - fn visit_map>( - self, - mut map: A, - ) -> Result { - let mut players = Vec::with_capacity(8); - while let Some((key, value)) = map.next_entry::()? { - if key < 8 { - players.push(value); - } else { - return Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Unsigned(key as u64), - &"a key in range 0..7", - )); - } - } - Ok(players) - } - } - - deserializer.deserialize_map(PlayerArrayVisitor) -} - -fn deserialize_syntax_colors<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - #[derive(Deserialize)] - struct ColorWrapper { - color: Hsla, - } - - struct SyntaxVisitor; - - impl<'de> Visitor<'de> for SyntaxVisitor { - type Value = HashMap; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a map with keys and objects with a single color field as values") - } - - fn visit_map(self, mut map: M) -> Result, M::Error> - where - M: serde::de::MapAccess<'de>, - { - let mut result = HashMap::new(); - while let Some(key) = map.next_key()? { - let wrapper: ColorWrapper = map.next_value()?; // Deserialize values as Hsla - result.insert(key, wrapper.color); - } - Ok(result) - } - } - deserializer.deserialize_map(SyntaxVisitor) -} - -#[derive(IntoElement)] -pub struct Themed> { - pub(crate) theme: Theme, - pub(crate) child: E, - pub(crate) view_type: PhantomData, -} - -impl> Element for Themed { - type PaintState = E::PaintState; - - fn layout( - &mut self, - view: &mut V, - cx: &mut ViewContext, - ) -> anyhow::Result<(gpui2::LayoutId, Self::PaintState)> - where - Self: Sized, - { - cx.push_theme(self.theme.clone()); - let result = self.child.layout(view, cx); - cx.pop_theme(); - result - } - - fn paint( - &mut self, - view: &mut V, - parent_origin: Vector2F, - layout: &gpui2::Layout, - state: &mut Self::PaintState, - cx: &mut ViewContext, - ) where - Self: Sized, - { - cx.push_theme(self.theme.clone()); - self.child.paint(view, parent_origin, layout, state, cx); - cx.pop_theme(); - } -} - -fn preferred_theme(cx: &AppContext) -> Theme { - settings::get::(cx) - .theme - .deserialized_base_theme - .lock() - .get_or_insert_with(|| { - let theme: Theme = - serde_json::from_value(settings::get::(cx).theme.base_theme.clone()) - .unwrap(); - Box::new(theme) - }) - .downcast_ref::() - .unwrap() - .clone() -} - -pub fn theme(cx: &WindowContext) -> Arc { - cx.theme::() -} diff --git a/crates/ui/src/tokens.rs b/crates/ui/src/tokens.rs deleted file mode 100644 index 5fd5b69a2a23ae62be52841984e5d5a20f38fe83..0000000000000000000000000000000000000000 --- a/crates/ui/src/tokens.rs +++ /dev/null @@ -1,25 +0,0 @@ -use gpui2::geometry::AbsoluteLength; -use gpui2::{hsla, Hsla}; - -#[derive(Clone, Copy)] -pub struct Token { - pub list_indent_depth: AbsoluteLength, - pub default_panel_size: AbsoluteLength, - pub state_hover_background: Hsla, - pub state_active_background: Hsla, -} - -impl Default for Token { - fn default() -> Self { - Self { - list_indent_depth: AbsoluteLength::Rems(0.5), - default_panel_size: AbsoluteLength::Rems(16.), - state_hover_background: hsla(0.0, 0.0, 0.0, 0.08), - state_active_background: hsla(0.0, 0.0, 0.0, 0.16), - } - } -} - -pub fn token() -> Token { - Token::default() -} diff --git a/crates/ui/tracker.md b/crates/ui/tracker.md deleted file mode 100644 index 11b5804db201ee7c2c814e00c6cd12b7cedb85c2..0000000000000000000000000000000000000000 --- a/crates/ui/tracker.md +++ /dev/null @@ -1,133 +0,0 @@ -* = Not in the app today - -## Template -- [ ] Workspace -- [ ] Title Bar -- [ ] Project Panel -- [ ] Collab Panel -- [ ] Project Diagnosics -- [ ] Project Search -- [ ] Feedback Editor -- [ ] Terminal -- [ ] Assistant -- [ ] Chat* -- [ ] Notifications* -- [ ] Status Bar -- [ ] Panes -- [ ] Pane -- [ ] Editor -- [ ] Tab Bar -- [ ] Tool Bar -- [ ] Buffer -- [ ] Zoomed Editor (Modal) - -### Palettes -- [ ] Project Files Palette (⌘-P) -- [ ] Command Palette (⌘-SHIFT-P) -- [ ] Recent Projects Palette (⌘-OPT-O) -- [ ] Recent Branches Palette (⌘-OPT-B) -- [ ] Project Symbols (⌘-T) -- [ ] Theme Palette (⌘-K, ⌘-T) -- [ ] Outline View (⌘-SHIFT-O) - -### Debug Views -- [ ] LSP Tool -- [ ] Syntax Tree - -## Modules - -### Title Bar -- [ ] Traffic Lights -- [ ] Host Menu -- [ ] Project Menu -- [ ] Branch Menu -- [ ] Collaborators -- [ ] Add Collaborator* -- [ ] Project Controls -- [ ] Call Controls -- [ ] User Menu - -### Project Panel -- [ ] Open Editors* -- [ ] Open Files (Non-project files) -- [ ] Project Files -- [ ] Root Folder - Context Menu -- [ ] Folder - Context Menu -- [ ] File - Context Menu -- [ ] Project Filter* - -### Collab Panel -- [ ] Current Call -- [ ] Channels -- [ ] Channel - Context Menu -- [ ] Contacts -- [ ] Collab Filter - -### Project Diagnosics -WIP - -### Feedback Editor -- [ ] Feedback Header -- [ ] Editor -- [ ] Feedback Actions - -### Terminal -- [ ] Terminal Toolbar* -- [ ] Terminal Line -- [ ] Terminal Input - -### Assistant -- [ ] Toolbar -- [ ] History / Past Conversations -- [ ] Model Controls / Token Counter -- [ ] Chat Editor - -### Chat -WIP - -### Notifications -WIP - -### Status Bar -- [ ] Status Bar Tool (Icon) -- [ ] Status Bar Tool (Text) -- [ ] Status Bar Tool - Context Menu -- [ ] Status Bar Tool - Popover Palette -- [ ] Status Bar Tool - Popover Menu -- [ ] Diagnostic Message -- [ ] LSP Message -- [ ] Update message (New version available, downloading, etc) - -### Panes/Pane - -- [ ] Editor -- [ ] Split Divider/Control - -### Editor -- [ ] Editor -- [ ] Read-only Editor -- [ ] Rendered Markdown View* - -### Tab Bar -- [ ] Navigation History / Control -- [ ] Tabs -- [ ] Editor Controls (New, Split, Zoom) - -### Tool Bar -- [ ] Breadcrumb -- [ ] Editor Tool (Togglable) -- [ ] Buffer Search - -### Buffer - -### Zoomed Editor (Modal) -- [ ] Modal View - -### Palette -- [ ] Input -- [ ] Section Title -- [ ] List - -## Components - -- [ ] Context Menu diff --git a/crates/util/src/github.rs b/crates/util/src/github.rs index b1e981ae4963e19829a91cc3cd961d604c9d1643..a3df4c996bea900f44b2f485d344809e1cf762b6 100644 --- a/crates/util/src/github.rs +++ b/crates/util/src/github.rs @@ -16,6 +16,7 @@ pub struct GithubRelease { pub pre_release: bool, pub assets: Vec, pub tarball_url: String, + pub zipball_url: String, } #[derive(Deserialize, Debug)] diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 4578ce0bc9105583db07030aaadeb90ede7079b0..96d77236a904b38847674b01fdccbe0dc4c9c502 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -11,6 +11,7 @@ lazy_static::lazy_static! { pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed"); pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages"); pub static ref COPILOT_DIR: PathBuf = HOME.join("Library/Application Support/Zed/copilot"); + pub static ref DEFAULT_PRETTIER_DIR: PathBuf = HOME.join("Library/Application Support/Zed/prettier"); pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db"); pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json"); pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json"); diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index d1240a45cea5ced287514da3569ae4a782f36883..99f19ed9d06b21ad6f9ba66464c016beb0b0bba0 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -30,6 +30,7 @@ gpui = { path = "../gpui" } install_cli = { path = "../install_cli" } language = { path = "../language" } menu = { path = "../menu" } +node_runtime = { path = "../node_runtime" } project = { path = "../project" } settings = { path = "../settings" } terminal = { path = "../terminal" } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 710883d7cc428eaceca51835ac54c24aee4c5b9f..e879b981ef65f55ab221ddd888b84d025dbd1ef7 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -42,6 +42,7 @@ use gpui::{ use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use itertools::Itertools; use language::{LanguageRegistry, Rope}; +use node_runtime::NodeRuntime; use std::{ any::TypeId, borrow::Cow, @@ -457,6 +458,7 @@ pub struct AppState { pub initialize_workspace: fn(WeakViewHandle, bool, Arc, AsyncAppContext) -> Task>, pub background_actions: BackgroundActions, + pub node_runtime: Arc, } pub struct WorkspaceStore { @@ -475,6 +477,7 @@ struct Follower { impl AppState { #[cfg(any(test, feature = "test-support"))] pub fn test(cx: &mut AppContext) -> Arc { + use node_runtime::FakeNodeRuntime; use settings::SettingsStore; if !cx.has_global::() { @@ -499,6 +502,7 @@ impl AppState { user_store, // channel_store, workspace_store, + node_runtime: FakeNodeRuntime::new(), initialize_workspace: |_, _, _, _| Task::ready(Ok(())), build_window_options: |_, _, _| Default::default(), background_actions: || &[], @@ -817,6 +821,7 @@ impl Workspace { )> { let project_handle = Project::local( app_state.client.clone(), + app_state.node_runtime.clone(), app_state.user_store.clone(), app_state.languages.clone(), app_state.fs.clone(), @@ -3518,6 +3523,8 @@ impl Workspace { #[cfg(any(test, feature = "test-support"))] pub fn test_new(project: ModelHandle, cx: &mut ViewContext) -> Self { + use node_runtime::FakeNodeRuntime; + let client = project.read(cx).client(); let user_store = project.read(cx).user_store(); @@ -3531,6 +3538,7 @@ impl Workspace { build_window_options: |_, _, _| Default::default(), initialize_workspace: |_, _, _, _| Task::ready(Ok(())), background_actions: || &[], + node_runtime: FakeNodeRuntime::new(), }); Self::new(0, project, app_state, cx) } diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 4174f7d6d54089ffe4250d3a6f80ab0060b1306d..aeabd4b45315b238be8e2fade5d0f13396324f1b 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -15,6 +15,9 @@ doctest = false name = "Zed" path = "src/main.rs" +[[example]] +name = "semantic_index_eval" + [dependencies] audio = { path = "../audio" } activity_indicator = { path = "../activity_indicator" } @@ -135,12 +138,14 @@ tree-sitter-yaml.workspace = true tree-sitter-lua.workspace = true tree-sitter-nix.workspace = true tree-sitter-nu.workspace = true +tree-sitter-vue.workspace = true url = "2.2" urlencoding = "2.1.2" uuid.workspace = true [dev-dependencies] +ai = { path = "../ai" } call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } diff --git a/crates/zed/contents/dev/embedded.provisionprofile b/crates/zed/contents/dev/embedded.provisionprofile new file mode 100644 index 0000000000000000000000000000000000000000..8979e1fb9fb72e9f5adbbc05d3498e6218016581 Binary files /dev/null and b/crates/zed/contents/dev/embedded.provisionprofile differ diff --git a/crates/zed/contents/preview/embedded.provisionprofile b/crates/zed/contents/preview/embedded.provisionprofile new file mode 100644 index 0000000000000000000000000000000000000000..6eea317c373c93336526bb5403001254622237bd Binary files /dev/null and b/crates/zed/contents/preview/embedded.provisionprofile differ diff --git a/crates/zed/contents/stable/Zed_Stable_Provisioning_Profile.provisionprofile b/crates/zed/contents/stable/Zed_Stable_Provisioning_Profile.provisionprofile new file mode 100644 index 0000000000000000000000000000000000000000..0b2abe1838b3ec9238f8995fb7a28788690f099f Binary files /dev/null and b/crates/zed/contents/stable/Zed_Stable_Provisioning_Profile.provisionprofile differ diff --git a/crates/semantic_index/examples/eval.rs b/crates/zed/examples/semantic_index_eval.rs similarity index 99% rename from crates/semantic_index/examples/eval.rs rename to crates/zed/examples/semantic_index_eval.rs index 573cf73d783fde74aba7669b1d09fc088bed67ff..33d6b3689c1f617a96d5fab00d41e51fa28d63f4 100644 --- a/crates/semantic_index/examples/eval.rs +++ b/crates/zed/examples/semantic_index_eval.rs @@ -494,6 +494,7 @@ fn main() { let project = cx.update(|cx| { Project::local( client.clone(), + node_runtime::FakeNodeRuntime::new(), user_store.clone(), languages.clone(), fs.clone(), diff --git a/crates/zed/resources/zed.entitlements b/crates/zed/resources/zed.entitlements index f40a8a253ad082bc08e48ed8684b2d553d5065e8..27499b62ca078c9213082e4bc9c63bfddeb2c34e 100644 --- a/crates/zed/resources/zed.entitlements +++ b/crates/zed/resources/zed.entitlements @@ -2,6 +2,8 @@ + com.apple.developer.associated-domains + applinks:zed.dev com.apple.security.automation.apple-events com.apple.security.cs.allow-jit @@ -10,14 +12,8 @@ com.apple.security.device.camera - com.apple.security.personal-information.addressbook - - com.apple.security.personal-information.calendars - - com.apple.security.personal-information.location - - com.apple.security.personal-information.photos-library - + com.apple.security.keychain-access-groups + MQ55VZLNZQ.dev.zed.Shared diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 04e5292a7df6cf4fa9c58d1999aed246f963c352..caf3cbf7c948ec239b8e620d30587f79c5e9b291 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -24,6 +24,7 @@ mod rust; mod svelte; mod tailwind; mod typescript; +mod vue; mod yaml; // 1. Add tree-sitter-{language} parser to zed crate @@ -190,13 +191,20 @@ pub fn init( language( "php", tree_sitter_php::language(), - vec![Arc::new(php::IntelephenseLspAdapter::new(node_runtime))], + vec![Arc::new(php::IntelephenseLspAdapter::new( + node_runtime.clone(), + ))], ); language("elm", tree_sitter_elm::language(), vec![]); language("glsl", tree_sitter_glsl::language(), vec![]); language("nix", tree_sitter_nix::language(), vec![]); language("nu", tree_sitter_nu::language(), vec![]); + language( + "vue", + tree_sitter_vue::language(), + vec![Arc::new(vue::VueLspAdapter::new(node_runtime))], + ); } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/zed/src/languages/css.rs b/crates/zed/src/languages/css.rs index fdbc179209603ea41c16e3a5aa6aac0d6a7a7f8e..f046437d75ec2ac824c81b33498522581fb89a9a 100644 --- a/crates/zed/src/languages/css.rs +++ b/crates/zed/src/languages/css.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::StreamExt; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::json; @@ -96,6 +96,10 @@ impl LspAdapter for CssLspAdapter { "provideFormatter": true })) } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::prettier("css")] + } } async fn get_cached_server_binary( diff --git a/crates/zed/src/languages/html.rs b/crates/zed/src/languages/html.rs index b8f1c70cce2ae00ca2a1647840e483844ea2a2e9..6f27b7ca8faa8ca5474a9dbcf7b6af219610ab2b 100644 --- a/crates/zed/src/languages/html.rs +++ b/crates/zed/src/languages/html.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::StreamExt; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::json; @@ -96,6 +96,10 @@ impl LspAdapter for HtmlLspAdapter { "provideFormatter": true })) } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::prettier("html")] + } } async fn get_cached_server_binary( diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 63f909ae2a2e264ea672dee48e305ba1be82e066..f017af0a22a278dc10d8a36c6941e0fc3adaa1a1 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -4,7 +4,9 @@ use collections::HashMap; use feature_flags::FeatureFlagAppExt; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; -use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate}; +use language::{ + BundledFormatter, LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate, +}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::json; @@ -144,6 +146,10 @@ impl LspAdapter for JsonLspAdapter { async fn language_ids(&self) -> HashMap { [("JSON".into(), "jsonc".into())].into_iter().collect() } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::prettier("json")] + } } async fn get_cached_server_binary( diff --git a/crates/zed/src/languages/svelte.rs b/crates/zed/src/languages/svelte.rs index 5e42d80e77fe04763643cbb6cd6ba298ba9e9e92..2089fe88b1ce03702a62e61033ec954fa9bdb789 100644 --- a/crates/zed/src/languages/svelte.rs +++ b/crates/zed/src/languages/svelte.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use futures::StreamExt; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::json; @@ -95,6 +95,13 @@ impl LspAdapter for SvelteLspAdapter { "provideFormatter": true })) } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::Prettier { + parser_name: Some("svelte"), + plugin_names: vec!["prettier-plugin-svelte"], + }] + } } async fn get_cached_server_binary( diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index cf07fa71c9c82f7866c0dcabbf8d8fbd50acb3cf..8e81f728dc1f39de023a8292018518ad772d3b3a 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -6,7 +6,7 @@ use futures::{ FutureExt, StreamExt, }; use gpui::AppContext; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; use serde_json::{json, Value}; @@ -127,6 +127,13 @@ impl LspAdapter for TailwindLspAdapter { .into_iter(), ) } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::Prettier { + parser_name: None, + plugin_names: vec!["prettier-plugin-tailwindcss"], + }] + } } async fn get_cached_server_binary( diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 676d0fd4c0d7afeaf1d3d39bd0c91f17c1a862cf..f09c9645881401aaa43ba00c9f13757ca0b53224 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -4,7 +4,7 @@ use async_tar::Archive; use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt}; use gpui::AppContext; -use language::{LanguageServerName, LspAdapter, LspAdapterDelegate}; +use language::{BundledFormatter, LanguageServerName, LspAdapter, LspAdapterDelegate}; use lsp::{CodeActionKind, LanguageServerBinary}; use node_runtime::NodeRuntime; use serde_json::{json, Value}; @@ -161,6 +161,10 @@ impl LspAdapter for TypeScriptLspAdapter { "provideFormatter": true })) } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::prettier("typescript")] + } } async fn get_cached_ts_server_binary( @@ -309,6 +313,10 @@ impl LspAdapter for EsLintLspAdapter { async fn initialization_options(&self) -> Option { None } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::prettier("babel")] + } } async fn get_cached_eslint_server_binary( diff --git a/crates/zed/src/languages/vue.rs b/crates/zed/src/languages/vue.rs new file mode 100644 index 0000000000000000000000000000000000000000..f0374452dfa6104c24709f975deffdf3cfe782af --- /dev/null +++ b/crates/zed/src/languages/vue.rs @@ -0,0 +1,214 @@ +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use futures::StreamExt; +pub use language::*; +use lsp::{CodeActionKind, LanguageServerBinary}; +use node_runtime::NodeRuntime; +use parking_lot::Mutex; +use serde_json::Value; +use smol::fs::{self}; +use std::{ + any::Any, + ffi::OsString, + path::{Path, PathBuf}, + sync::Arc, +}; +use util::ResultExt; + +pub struct VueLspVersion { + vue_version: String, + ts_version: String, +} + +pub struct VueLspAdapter { + node: Arc, + typescript_install_path: Mutex>, +} + +impl VueLspAdapter { + const SERVER_PATH: &'static str = + "node_modules/@vue/language-server/bin/vue-language-server.js"; + // TODO: this can't be hardcoded, yet we have to figure out how to pass it in initialization_options. + const TYPESCRIPT_PATH: &'static str = "node_modules/typescript/lib"; + pub fn new(node: Arc) -> Self { + let typescript_install_path = Mutex::new(None); + Self { + node, + typescript_install_path, + } + } +} +#[async_trait] +impl super::LspAdapter for VueLspAdapter { + async fn name(&self) -> LanguageServerName { + LanguageServerName("vue-language-server".into()) + } + + fn short_name(&self) -> &'static str { + "vue-language-server" + } + + async fn fetch_latest_server_version( + &self, + _: &dyn LspAdapterDelegate, + ) -> Result> { + Ok(Box::new(VueLspVersion { + vue_version: self + .node + .npm_package_latest_version("@vue/language-server") + .await?, + ts_version: self.node.npm_package_latest_version("typescript").await?, + }) as Box<_>) + } + async fn initialization_options(&self) -> Option { + let typescript_sdk_path = self.typescript_install_path.lock(); + let typescript_sdk_path = typescript_sdk_path + .as_ref() + .expect("initialization_options called without a container_dir for typescript"); + + Some(serde_json::json!({ + "typescript": { + "tsdk": typescript_sdk_path + } + })) + } + fn code_action_kinds(&self) -> Option> { + // REFACTOR is explicitly disabled, as vue-lsp does not adhere to LSP protocol for code actions with these - it + // sends back a CodeAction with neither `command` nor `edits` fields set, which is against the spec. + Some(vec![ + CodeActionKind::EMPTY, + CodeActionKind::QUICKFIX, + CodeActionKind::REFACTOR_REWRITE, + ]) + } + async fn fetch_server_binary( + &self, + version: Box, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Result { + let version = version.downcast::().unwrap(); + let server_path = container_dir.join(Self::SERVER_PATH); + let ts_path = container_dir.join(Self::TYPESCRIPT_PATH); + if fs::metadata(&server_path).await.is_err() { + self.node + .npm_install_packages( + &container_dir, + &[("@vue/language-server", version.vue_version.as_str())], + ) + .await?; + } + assert!(fs::metadata(&server_path).await.is_ok()); + if fs::metadata(&ts_path).await.is_err() { + self.node + .npm_install_packages( + &container_dir, + &[("typescript", version.ts_version.as_str())], + ) + .await?; + } + + assert!(fs::metadata(&ts_path).await.is_ok()); + *self.typescript_install_path.lock() = Some(ts_path); + Ok(LanguageServerBinary { + path: self.node.binary_path().await?, + arguments: vue_server_binary_arguments(&server_path), + }) + } + + async fn cached_server_binary( + &self, + container_dir: PathBuf, + _: &dyn LspAdapterDelegate, + ) -> Option { + let (server, ts_path) = get_cached_server_binary(container_dir, self.node.clone()).await?; + *self.typescript_install_path.lock() = Some(ts_path); + Some(server) + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + let (server, ts_path) = get_cached_server_binary(container_dir, self.node.clone()) + .await + .map(|(mut binary, ts_path)| { + binary.arguments = vec!["--help".into()]; + (binary, ts_path) + })?; + *self.typescript_install_path.lock() = Some(ts_path); + Some(server) + } + + async fn label_for_completion( + &self, + item: &lsp::CompletionItem, + language: &Arc, + ) -> Option { + use lsp::CompletionItemKind as Kind; + let len = item.label.len(); + let grammar = language.grammar()?; + let highlight_id = match item.kind? { + Kind::CLASS | Kind::INTERFACE => grammar.highlight_id_for_name("type"), + Kind::CONSTRUCTOR => grammar.highlight_id_for_name("type"), + Kind::CONSTANT => grammar.highlight_id_for_name("constant"), + Kind::FUNCTION | Kind::METHOD => grammar.highlight_id_for_name("function"), + Kind::PROPERTY | Kind::FIELD => grammar.highlight_id_for_name("tag"), + Kind::VARIABLE => grammar.highlight_id_for_name("type"), + Kind::KEYWORD => grammar.highlight_id_for_name("keyword"), + Kind::VALUE => grammar.highlight_id_for_name("tag"), + _ => None, + }?; + + let text = match &item.detail { + Some(detail) => format!("{} {}", item.label, detail), + None => item.label.clone(), + }; + + Some(language::CodeLabel { + text, + runs: vec![(0..len, highlight_id)], + filter_range: 0..len, + }) + } +} + +fn vue_server_binary_arguments(server_path: &Path) -> Vec { + vec![server_path.into(), "--stdio".into()] +} + +type TypescriptPath = PathBuf; +async fn get_cached_server_binary( + container_dir: PathBuf, + node: Arc, +) -> Option<(LanguageServerBinary, TypescriptPath)> { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(VueLspAdapter::SERVER_PATH); + let typescript_path = last_version_dir.join(VueLspAdapter::TYPESCRIPT_PATH); + if server_path.exists() && typescript_path.exists() { + Ok(( + LanguageServerBinary { + path: node.binary_path().await?, + arguments: vue_server_binary_arguments(&server_path), + }, + typescript_path, + )) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +} diff --git a/crates/zed/src/languages/vue/brackets.scm b/crates/zed/src/languages/vue/brackets.scm new file mode 100644 index 0000000000000000000000000000000000000000..2d12b17daab82f7209c3b32cc4047d805e3b2415 --- /dev/null +++ b/crates/zed/src/languages/vue/brackets.scm @@ -0,0 +1,2 @@ +("<" @open ">" @close) +("\"" @open "\"" @close) diff --git a/crates/zed/src/languages/vue/config.toml b/crates/zed/src/languages/vue/config.toml new file mode 100644 index 0000000000000000000000000000000000000000..c41a667b752c53bd2fed270490d2bc56fe1f04ed --- /dev/null +++ b/crates/zed/src/languages/vue/config.toml @@ -0,0 +1,14 @@ +name = "Vue.js" +path_suffixes = ["vue"] +block_comment = [""] +autoclose_before = ";:.,=}])>" +brackets = [ + { start = "{", end = "}", close = true, newline = true }, + { start = "[", end = "]", close = true, newline = true }, + { start = "(", end = ")", close = true, newline = true }, + { start = "<", end = ">", close = true, newline = true, not_in = ["string", "comment"] }, + { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, + { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, + { start = "`", end = "`", close = true, newline = false, not_in = ["string"] }, +] +word_characters = ["-"] diff --git a/crates/zed/src/languages/vue/highlights.scm b/crates/zed/src/languages/vue/highlights.scm new file mode 100644 index 0000000000000000000000000000000000000000..1a80c84f68edadf0417cd159c91840102719add7 --- /dev/null +++ b/crates/zed/src/languages/vue/highlights.scm @@ -0,0 +1,15 @@ +(attribute) @property +(directive_attribute) @property +(quoted_attribute_value) @string +(interpolation) @punctuation.special +(raw_text) @embedded + +((tag_name) @type + (#match? @type "^[A-Z]")) + +((directive_name) @keyword + (#match? @keyword "^v-")) + +(start_tag) @tag +(end_tag) @tag +(self_closing_tag) @tag diff --git a/crates/zed/src/languages/vue/injections.scm b/crates/zed/src/languages/vue/injections.scm new file mode 100644 index 0000000000000000000000000000000000000000..9084e373f217b95cf70bad9cc907d5d9cd127391 --- /dev/null +++ b/crates/zed/src/languages/vue/injections.scm @@ -0,0 +1,7 @@ +(script_element + (raw_text) @content + (#set! "language" "javascript")) + +(style_element + (raw_text) @content + (#set! "language" "css")) diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index 8b438d0949dc0ef1f514f3c315c3eab98174d506..1c1ce1866866260b909262ae66e0771713b0df0d 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -3,7 +3,8 @@ use async_trait::async_trait; use futures::{future::BoxFuture, FutureExt, StreamExt}; use gpui::AppContext; use language::{ - language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate, + language_settings::all_language_settings, BundledFormatter, LanguageServerName, LspAdapter, + LspAdapterDelegate, }; use lsp::LanguageServerBinary; use node_runtime::NodeRuntime; @@ -108,6 +109,10 @@ impl LspAdapter for YamlLspAdapter { })) .boxed() } + + fn enabled_formatters(&self) -> Vec { + vec![BundledFormatter::prettier("yaml")] + } } async fn get_cached_server_binary( diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 0e3bb6ef4349f7b3eab8c7a9ea3be9a211785b7f..dce579e8478c2ef0a2de400e685fa4a71e0f3f30 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -143,7 +143,12 @@ fn main() { semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx); vim::init(cx); terminal_view::init(cx); - copilot::init(copilot_language_server_id, http.clone(), node_runtime, cx); + copilot::init( + copilot_language_server_id, + http.clone(), + node_runtime.clone(), + cx, + ); assistant::init(cx); component_test::init(cx); @@ -170,6 +175,7 @@ fn main() { initialize_workspace, background_actions, workspace_store, + node_runtime, }); cx.set_global(Arc::downgrade(&app_state)); diff --git a/script/bundle b/script/bundle index a1d0b305c8fc3b4732c3dac7a4722f3ae3838ae9..4775e15837bf2bf8bd53ceb65e7f1ae1353dd3d0 100755 --- a/script/bundle +++ b/script/bundle @@ -134,6 +134,8 @@ else cp -R target/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/" fi +cp crates/zed/contents/$channel/embedded.provisionprofile "${app_path}/Contents/" + if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then echo "Signing bundle with Apple-issued certificate" security create-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain || echo "" @@ -143,14 +145,32 @@ if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTAR security import /tmp/zed-certificate.p12 -k zed.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign rm /tmp/zed-certificate.p12 security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CERTIFICATE_PASSWORD" zed.keychain - /usr/bin/codesign --force --deep --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}" -v + + # sequence of codesign commands modeled after this example: https://developer.apple.com/forums/thread/701514 + /usr/bin/codesign --force --timestamp --sign "Zed Industries, Inc." "${app_path}/Contents/Frameworks/WebRTC.framework" -v + /usr/bin/codesign --force --timestamp --options runtime --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/cli" -v + /usr/bin/codesign --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}" -v + security default-keychain -s login.keychain else echo "One or more of the following variables are missing: MACOS_CERTIFICATE, MACOS_CERTIFICATE_PASSWORD, APPLE_NOTARIZATION_USERNAME, APPLE_NOTARIZATION_PASSWORD" - echo "Performing an ad-hoc signature, but this bundle should not be distributed" - echo "If you see 'The application cannot be opened for an unexpected reason,' you likely don't have the necessary entitlements to run the application in your signing keychain" - echo "You will need to download a new signing key from developer.apple.com, add it to keychain, and export MACOS_SIGNING_KEY=" - codesign --force --deep --entitlements crates/zed/resources/zed.entitlements --sign ${MACOS_SIGNING_KEY:- -} "${app_path}" -v + if [[ "$local_only" = false ]]; then + echo "To create a self-signed local build use ./scripts/build.sh -ldf" + exit 1 + fi + + echo "====== WARNING ======" + echo "This bundle is being signed without all entitlements, some features (e.g. universal links) will not work" + echo "====== WARNING ======" + + # NOTE: if you need to test universal links you have a few paths forward: + # - create a PR and tag it with the `run-build-dmg` label, and download the .dmg file from there. + # - get a signing key for the MQ55VZLNZQ team from Nathan. + # - create your own signing key, and update references to MQ55VZLNZQ to your own team ID + # then comment out this line. + cat crates/zed/resources/zed.entitlements | sed '/com.apple.developer.associated-domains/,+1d' > "${app_path}/Contents/Resources/zed.entitlements" + + codesign --force --deep --entitlements "${app_path}/Contents/Resources/zed.entitlements" --sign ${MACOS_SIGNING_KEY:- -} "${app_path}" -v fi if [[ "$target_dir" = "debug" && "$local_only" = false ]]; then diff --git a/styles/src/style_tree/editor.ts b/styles/src/style_tree/editor.ts index e55a73c365faba50c97001b417167f00e33a534f..27a6eaf19579fa9ba891d03d92f7408e83c68ba3 100644 --- a/styles/src/style_tree/editor.ts +++ b/styles/src/style_tree/editor.ts @@ -206,9 +206,13 @@ export default function editor(): any { match_highlight: foreground(theme.middle, "accent", "active"), background: background(theme.middle, "active"), }, - server_name_container: { padding: { left: 40 } }, - server_name_color: text(theme.middle, "sans", "disabled", {}).color, - server_name_size_percent: 0.75, + completion_min_width: 300, + completion_max_width: 700, + inline_docs_container: { padding: { left: 40 } }, + inline_docs_color: text(theme.middle, "sans", "disabled", {}).color, + inline_docs_size_percent: 0.75, + alongside_docs_max_width: 700, + alongside_docs_container: { padding: autocomplete_item.padding } }, diagnostic_header: { background: background(theme.middle),