diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7072a3fe94..fce5717ca9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,16 +41,19 @@ jobs: with: clean: false submodules: 'recursive' - + - name: Run tests run: cargo test --workspace --no-fail-fast - + - name: Build collab run: cargo build -p collab - name: Build other binaries run: cargo build --workspace --bins --all-features + - name: Generate license file + run: script/generate-licenses + bundle: name: Bundle app runs-on: @@ -109,6 +112,9 @@ jobs: exit 1 fi + - name: Generate license file + run: script/generate-licenses + - name: Create app bundle run: script/bundle diff --git a/.gitignore b/.gitignore index 356f4d97cd..8bca2eafac 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /assets/themes/*.json /assets/themes/Internal/*.json /assets/themes/Experiments/*.json +/assets/licenses.md **/venv .build Packages diff --git a/Cargo.lock b/Cargo.lock index abdf8b8a55..f75fd3648b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2021,6 +2021,33 @@ dependencies = [ "instant", ] +[[package]] +name = "feedback" +version = "0.1.0" +dependencies = [ + "anyhow", + "client", + "editor", + "futures 0.3.25", + "gpui", + "human_bytes", + "isahc", + "language", + "lazy_static", + "log", + "postage", + "project", + "search", + "serde", + "settings", + "sysinfo", + "theme", + "tree-sitter-markdown", + "urlencoding", + "util", + "workspace", +] + [[package]] name = "file-per-thread-logger" version = "0.1.5" @@ -6241,9 +6268,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.27.1" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccb297c0afb439440834b4bcf02c5c9da8ec2e808e70f36b0d8e815ff403bd24" +checksum = "1620f9573034c573376acc550f3b9a2be96daeb08abb3c12c8523e1cee06e80f" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys", @@ -8214,6 +8241,7 @@ dependencies = [ "easy-parallel", "editor", "env_logger", + "feedback", "file_finder", "fs", "fsevent", @@ -8221,7 +8249,6 @@ dependencies = [ "fuzzy", "go_to_line", "gpui", - "human_bytes", "ignore", "image", "indexmap", @@ -8255,7 +8282,6 @@ dependencies = [ "smallvec", "smol", "sum_tree", - "sysinfo", "tempdir", "terminal_view", "text", @@ -8284,7 +8310,6 @@ dependencies = [ "tree-sitter-typescript", "unindent", "url", - "urlencoding", "util", "vim", "workspace", diff --git a/Cargo.toml b/Cargo.toml index 1ace51dbd5..77469c0623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "crates/diagnostics", "crates/drag_and_drop", "crates/editor", + "crates/feedback", "crates/file_finder", "crates/fs", "crates/fsevent", diff --git a/crates/activity_indicator/Cargo.toml b/crates/activity_indicator/Cargo.toml index 63998fa47b..78a4e752b2 100644 --- a/crates/activity_indicator/Cargo.toml +++ b/crates/activity_indicator/Cargo.toml @@ -2,6 +2,7 @@ name = "activity_indicator" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/activity_indicator.rs" diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml index 71db57f320..ca96c92602 100644 --- a/crates/assets/Cargo.toml +++ b/crates/assets/Cargo.toml @@ -2,6 +2,7 @@ name = "assets" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/assets.rs" diff --git a/crates/auto_update/Cargo.toml b/crates/auto_update/Cargo.toml index b1ca061614..5f672e759f 100644 --- a/crates/auto_update/Cargo.toml +++ b/crates/auto_update/Cargo.toml @@ -2,6 +2,7 @@ name = "auto_update" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/auto_update.rs" diff --git a/crates/breadcrumbs/Cargo.toml b/crates/breadcrumbs/Cargo.toml index e5cae74e8f..99476fdc0a 100644 --- a/crates/breadcrumbs/Cargo.toml +++ b/crates/breadcrumbs/Cargo.toml @@ -2,6 +2,7 @@ name = "breadcrumbs" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/breadcrumbs.rs" diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml index e6aa357bef..156925fb72 100644 --- a/crates/call/Cargo.toml +++ b/crates/call/Cargo.toml @@ -2,6 +2,7 @@ name = "call" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/call.rs" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index fafcc5ab68..f2bab22ea7 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -2,6 +2,7 @@ name = "cli" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/cli.rs" diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 4bc2d6e73d..a31e59587f 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -9,7 +9,13 @@ use core_foundation::{ use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType}; use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; use serde::Deserialize; -use std::{ffi::OsStr, fs, path::PathBuf, ptr}; +use std::{ + ffi::OsStr, + fs::{self, OpenOptions}, + io, + path::{Path, PathBuf}, + ptr, +}; #[derive(Parser)] #[clap(name = "zed", global_setting(clap::AppSettings::NoAutoVersion))] @@ -54,6 +60,12 @@ fn main() -> Result<()> { return Ok(()); } + for path in args.paths.iter() { + if !path.exists() { + touch(path.as_path())?; + } + } + let (tx, rx) = launch_app(bundle_path)?; tx.send(CliRequest::Open { @@ -77,6 +89,13 @@ fn main() -> Result<()> { Ok(()) } +fn touch(path: &Path) -> io::Result<()> { + match OpenOptions::new().create(true).write(true).open(path) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } +} + fn locate_bundle() -> Result { let cli_path = std::env::current_exe()?.canonicalize()?; let mut app_path = cli_path.clone(); diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 74533fbc3b..347424d34e 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -2,6 +2,7 @@ name = "client" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/client.rs" diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 213b2cf042..4d129fab2e 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1315,6 +1315,10 @@ impl Client { pub fn telemetry_log_file_path(&self) -> Option { self.telemetry.log_file_path() } + + pub fn metrics_id(&self) -> Option> { + self.telemetry.metrics_id() + } } impl WeakSubscriber { diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index e5d2dad41e..2aa33e6435 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -278,6 +278,10 @@ impl Telemetry { } } + pub fn metrics_id(self: &Arc) -> Option> { + self.state.lock().metrics_id.clone() + } + fn flush(self: &Arc) { let mut state = self.state.lock(); let mut events = mem::take(&mut state.queue); diff --git a/crates/clock/Cargo.toml b/crates/clock/Cargo.toml index 8e17e15e5e..1705fdc6d5 100644 --- a/crates/clock/Cargo.toml +++ b/crates/clock/Cargo.toml @@ -2,6 +2,7 @@ name = "clock" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/clock.rs" diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 8c21af7273..456bcf6531 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -4,6 +4,7 @@ default-run = "collab" edition = "2021" name = "collab" version = "0.5.3" +publish = false [[bin]] name = "collab" diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 20db066ce7..ac13e361fd 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -2,6 +2,7 @@ name = "collab_ui" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/collab_ui.rs" diff --git a/crates/collections/Cargo.toml b/crates/collections/Cargo.toml index 8e18cbd11d..dcbc642c4c 100644 --- a/crates/collections/Cargo.toml +++ b/crates/collections/Cargo.toml @@ -2,6 +2,7 @@ name = "collections" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/collections.rs" diff --git a/crates/command_palette/Cargo.toml b/crates/command_palette/Cargo.toml index 85f5b36ed6..555deff1ce 100644 --- a/crates/command_palette/Cargo.toml +++ b/crates/command_palette/Cargo.toml @@ -2,6 +2,7 @@ name = "command_palette" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/command_palette.rs" diff --git a/crates/context_menu/Cargo.toml b/crates/context_menu/Cargo.toml index 817893f43e..d764d4ddb8 100644 --- a/crates/context_menu/Cargo.toml +++ b/crates/context_menu/Cargo.toml @@ -2,6 +2,7 @@ name = "context_menu" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/context_menu.rs" diff --git a/crates/db/Cargo.toml b/crates/db/Cargo.toml index 8e12b06027..496e61b811 100644 --- a/crates/db/Cargo.toml +++ b/crates/db/Cargo.toml @@ -2,6 +2,7 @@ name = "db" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/db.rs" diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 616f69117f..ebb57e0636 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -2,6 +2,7 @@ name = "diagnostics" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/diagnostics.rs" diff --git a/crates/drag_and_drop/Cargo.toml b/crates/drag_and_drop/Cargo.toml index 4ab54ad8e6..e378a5bc31 100644 --- a/crates/drag_and_drop/Cargo.toml +++ b/crates/drag_and_drop/Cargo.toml @@ -2,6 +2,7 @@ name = "drag_and_drop" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/drag_and_drop.rs" diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index f992ed5116..26dd371041 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -2,6 +2,7 @@ name = "editor" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/editor.rs" diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4fbaaa8058..84b97468e0 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1008,6 +1008,15 @@ impl Editor { Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx) } + pub fn multi_line( + field_editor_style: Option>, + cx: &mut ViewContext, + ) -> Self { + let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx)); + let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx)); + Self::new(EditorMode::Full, buffer, None, field_editor_style, cx) + } + pub fn auto_height( max_lines: usize, field_editor_style: Option>, diff --git a/crates/feedback/Cargo.toml b/crates/feedback/Cargo.toml new file mode 100644 index 0000000000..b224f91a2f --- /dev/null +++ b/crates/feedback/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "feedback" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/feedback.rs" + +[features] +test-support = [] + +[dependencies] +anyhow = "1.0.38" +client = { path = "../client" } +editor = { path = "../editor" } +language = { path = "../language" } +log = "0.4" +futures = "0.3" +gpui = { path = "../gpui" } +human_bytes = "0.4.1" +isahc = "1.7" +lazy_static = "1.4.0" +postage = { version = "0.4", features = ["futures-traits"] } +project = { path = "../project" } +search = { path = "../search" } +serde = { version = "1.0", features = ["derive", "rc"] } +settings = { path = "../settings" } +sysinfo = "0.27.1" +theme = { path = "../theme" } +tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" } +urlencoding = "2.1.2" +util = { path = "../util" } +workspace = { path = "../workspace" } \ No newline at end of file diff --git a/crates/feedback/src/feedback.rs b/crates/feedback/src/feedback.rs new file mode 100644 index 0000000000..4b0dfc4df9 --- /dev/null +++ b/crates/feedback/src/feedback.rs @@ -0,0 +1,61 @@ +use std::sync::Arc; + +pub mod feedback_editor; +mod system_specs; +use gpui::{actions, impl_actions, ClipboardItem, ViewContext}; +use serde::Deserialize; +use system_specs::SystemSpecs; +use workspace::Workspace; + +#[derive(Deserialize, Clone, PartialEq)] +pub struct OpenBrowser { + pub url: Arc, +} + +impl_actions!(zed, [OpenBrowser]); + +actions!( + zed, + [CopySystemSpecsIntoClipboard, FileBugReport, RequestFeature,] +); + +pub fn init(cx: &mut gpui::MutableAppContext) { + feedback_editor::init(cx); + + cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); + + cx.add_action( + |_: &mut Workspace, _: &CopySystemSpecsIntoClipboard, cx: &mut ViewContext| { + let system_specs = SystemSpecs::new(cx).to_string(); + let item = ClipboardItem::new(system_specs.clone()); + cx.prompt( + gpui::PromptLevel::Info, + &format!("Copied into clipboard:\n\n{system_specs}"), + &["OK"], + ); + cx.write_to_clipboard(item); + }, + ); + + cx.add_action( + |_: &mut Workspace, _: &RequestFeature, cx: &mut ViewContext| { + let url = "https://github.com/zed-industries/feedback/issues/new?assignees=&labels=enhancement%2Ctriage&template=0_feature_request.yml"; + cx.dispatch_action(OpenBrowser { + url: url.into(), + }); + }, + ); + + cx.add_action( + |_: &mut Workspace, _: &FileBugReport, cx: &mut ViewContext| { + let system_specs_text = SystemSpecs::new(cx).to_string(); + let url = format!( + "https://github.com/zed-industries/feedback/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml&environment={}", + urlencoding::encode(&system_specs_text) + ); + cx.dispatch_action(OpenBrowser { + url: url.into(), + }); + }, + ); +} diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs new file mode 100644 index 0000000000..8185fbad9a --- /dev/null +++ b/crates/feedback/src/feedback_editor.rs @@ -0,0 +1,417 @@ +use std::{ops::Range, sync::Arc}; + +use anyhow::bail; +use client::{Client, ZED_SECRET_CLIENT_TOKEN}; +use editor::{Anchor, Editor}; +use futures::AsyncReadExt; +use gpui::{ + actions, + elements::{ChildView, Flex, Label, MouseEventHandler, ParentElement, Stack, Text}, + serde_json, AnyViewHandle, AppContext, CursorStyle, Element, ElementBox, Entity, ModelHandle, + MouseButton, MutableAppContext, PromptLevel, RenderContext, Task, View, ViewContext, + ViewHandle, +}; +use isahc::Request; +use language::Buffer; +use postage::prelude::Stream; + +use lazy_static::lazy_static; +use project::Project; +use serde::Serialize; +use settings::Settings; +use workspace::{ + item::{Item, ItemHandle}, + searchable::{SearchableItem, SearchableItemHandle}, + StatusItemView, Workspace, +}; + +use crate::system_specs::SystemSpecs; + +lazy_static! { + pub static ref ZED_SERVER_URL: String = + std::env::var("ZED_SERVER_URL").unwrap_or_else(|_| "https://zed.dev".to_string()); +} + +const FEEDBACK_CHAR_COUNT_RANGE: Range = Range { + start: 10, + end: 1000, +}; + +const FEEDBACK_PLACEHOLDER_TEXT: &str = "Thanks for spending time with Zed. Enter your feedback here as Markdown. Save the tab to submit your feedback."; +const FEEDBACK_SUBMISSION_ERROR_TEXT: &str = + "Feedback failed to submit, see error log for details."; + +actions!(feedback, [SubmitFeedback, GiveFeedback, DeployFeedback]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(FeedbackEditor::deploy); +} + +pub struct FeedbackButton; + +impl Entity for FeedbackButton { + type Event = (); +} + +impl View for FeedbackButton { + fn ui_name() -> &'static str { + "FeedbackButton" + } + + fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |state, cx| { + let theme = &cx.global::().theme; + let theme = &theme.workspace.status_bar.feedback; + + Text::new( + "Give Feedback".to_string(), + theme.style_for(state, true).clone(), + ) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(GiveFeedback)) + .boxed(), + ) + .boxed() + } +} + +impl StatusItemView for FeedbackButton { + fn set_active_pane_item( + &mut self, + _: Option<&dyn ItemHandle>, + _: &mut gpui::ViewContext, + ) { + } +} + +#[derive(Serialize)] +struct FeedbackRequestBody<'a> { + feedback_text: &'a str, + metrics_id: Option>, + system_specs: SystemSpecs, + token: &'a str, +} + +#[derive(Clone)] +struct FeedbackEditor { + editor: ViewHandle, + project: ModelHandle, +} + +impl FeedbackEditor { + fn new_with_buffer( + project: ModelHandle, + buffer: ModelHandle, + cx: &mut ViewContext, + ) -> Self { + let editor = cx.add_view(|cx| { + let mut editor = Editor::for_buffer(buffer, Some(project.clone()), cx); + editor.set_vertical_scroll_margin(5, cx); + editor.set_placeholder_text(FEEDBACK_PLACEHOLDER_TEXT, cx); + editor + }); + + cx.subscribe(&editor, |_, _, e, cx| cx.emit(e.clone())) + .detach(); + + let this = Self { editor, project }; + this + } + + fn new(project: ModelHandle, cx: &mut ViewContext) -> Self { + let markdown_language = project.read(cx).languages().get_language("Markdown"); + + let buffer = project + .update(cx, |project, cx| { + project.create_buffer("", markdown_language, cx) + }) + .expect("creating buffers on a local workspace always succeeds"); + + Self::new_with_buffer(project, buffer, cx) + } + + fn handle_save( + &mut self, + _: gpui::ModelHandle, + cx: &mut ViewContext, + ) -> Task> { + let feedback_text_length = self.editor.read(cx).buffer().read(cx).len(cx); + + if feedback_text_length <= FEEDBACK_CHAR_COUNT_RANGE.start { + cx.prompt( + PromptLevel::Critical, + &format!( + "Feedback must be longer than {} characters", + FEEDBACK_CHAR_COUNT_RANGE.start + ), + &["OK"], + ); + + return Task::ready(Ok(())); + } + + let mut answer = cx.prompt( + PromptLevel::Info, + "Ready to submit your feedback?", + &["Yes, Submit!", "No"], + ); + + let this = cx.handle(); + let client = cx.global::>().clone(); + let feedback_text = self.editor.read(cx).text(cx); + let specs = SystemSpecs::new(cx); + + cx.spawn(|_, mut cx| async move { + let answer = answer.recv().await; + + if answer == Some(0) { + match FeedbackEditor::submit_feedback(&feedback_text, client, specs).await { + Ok(_) => { + cx.update(|cx| { + this.update(cx, |_, cx| { + cx.dispatch_action(workspace::CloseActiveItem); + }) + }); + } + Err(error) => { + log::error!("{}", error); + + cx.update(|cx| { + this.update(cx, |_, cx| { + cx.prompt( + PromptLevel::Critical, + FEEDBACK_SUBMISSION_ERROR_TEXT, + &["OK"], + ); + }) + }); + } + } + } + }) + .detach(); + + Task::ready(Ok(())) + } + + async fn submit_feedback( + feedback_text: &str, + zed_client: Arc, + system_specs: SystemSpecs, + ) -> anyhow::Result<()> { + let feedback_endpoint = format!("{}/api/feedback", *ZED_SERVER_URL); + + let metrics_id = zed_client.metrics_id(); + let http_client = zed_client.http_client(); + + let request = FeedbackRequestBody { + feedback_text: &feedback_text, + metrics_id, + system_specs, + token: ZED_SECRET_CLIENT_TOKEN, + }; + + let json_bytes = serde_json::to_vec(&request)?; + + let request = Request::post(feedback_endpoint) + .header("content-type", "application/json") + .body(json_bytes.into())?; + + let mut response = http_client.send(request).await?; + let mut body = String::new(); + response.body_mut().read_to_string(&mut body).await?; + + let response_status = response.status(); + + if !response_status.is_success() { + bail!("Feedback API failed with error: {}", response_status) + } + + Ok(()) + } +} + +impl FeedbackEditor { + pub fn deploy(workspace: &mut Workspace, _: &GiveFeedback, cx: &mut ViewContext) { + let feedback_editor = + cx.add_view(|cx| FeedbackEditor::new(workspace.project().clone(), cx)); + workspace.add_item(Box::new(feedback_editor), cx); + } +} + +impl View for FeedbackEditor { + fn ui_name() -> &'static str { + "FeedbackEditor" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(&self.editor, cx).boxed() + } + + fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + if cx.is_self_focused() { + cx.focus(&self.editor); + } + } +} + +impl Entity for FeedbackEditor { + type Event = editor::Event; +} + +impl Item for FeedbackEditor { + fn tab_content( + &self, + _: Option, + style: &theme::Tab, + _: &gpui::AppContext, + ) -> ElementBox { + Flex::row() + .with_child( + Label::new("Feedback".to_string(), style.label.clone()) + .aligned() + .contained() + .boxed(), + ) + .boxed() + } + + fn for_each_project_item(&self, cx: &AppContext, f: &mut dyn FnMut(usize, &dyn project::Item)) { + self.editor.for_each_project_item(cx, f) + } + + fn to_item_events(_: &Self::Event) -> Vec { + Vec::new() + } + + fn is_singleton(&self, _: &gpui::AppContext) -> bool { + true + } + + fn set_nav_history(&mut self, _: workspace::ItemNavHistory, _: &mut ViewContext) {} + + fn can_save(&self, _: &gpui::AppContext) -> bool { + true + } + + fn save( + &mut self, + project: gpui::ModelHandle, + cx: &mut ViewContext, + ) -> Task> { + self.handle_save(project, cx) + } + + fn save_as( + &mut self, + project: gpui::ModelHandle, + _: std::path::PathBuf, + cx: &mut ViewContext, + ) -> Task> { + self.handle_save(project, cx) + } + + fn reload( + &mut self, + _: gpui::ModelHandle, + _: &mut ViewContext, + ) -> Task> { + unreachable!("reload should not have been called") + } + + fn clone_on_split( + &self, + _workspace_id: workspace::WorkspaceId, + cx: &mut ViewContext, + ) -> Option + where + Self: Sized, + { + let buffer = self + .editor + .read(cx) + .buffer() + .read(cx) + .as_singleton() + .expect("Feedback buffer is only ever singleton"); + + Some(Self::new_with_buffer( + self.project.clone(), + buffer.clone(), + cx, + )) + } + + fn serialized_item_kind() -> Option<&'static str> { + None + } + + fn deserialize( + _: gpui::ModelHandle, + _: gpui::WeakViewHandle, + _: workspace::WorkspaceId, + _: workspace::ItemId, + _: &mut ViewContext, + ) -> Task>> { + unreachable!() + } + + fn as_searchable(&self, handle: &ViewHandle) -> Option> { + Some(Box::new(handle.clone())) + } +} + +impl SearchableItem for FeedbackEditor { + type Match = Range; + + fn to_search_event(event: &Self::Event) -> Option { + Editor::to_search_event(event) + } + + fn clear_matches(&mut self, cx: &mut ViewContext) { + self.editor + .update(cx, |editor, cx| editor.clear_matches(cx)) + } + + fn update_matches(&mut self, matches: Vec, cx: &mut ViewContext) { + self.editor + .update(cx, |editor, cx| editor.update_matches(matches, cx)) + } + + fn query_suggestion(&mut self, cx: &mut ViewContext) -> String { + self.editor + .update(cx, |editor, cx| editor.query_suggestion(cx)) + } + + fn activate_match( + &mut self, + index: usize, + matches: Vec, + cx: &mut ViewContext, + ) { + self.editor + .update(cx, |editor, cx| editor.activate_match(index, matches, cx)) + } + + fn find_matches( + &mut self, + query: project::search::SearchQuery, + cx: &mut ViewContext, + ) -> Task> { + self.editor + .update(cx, |editor, cx| editor.find_matches(query, cx)) + } + + fn active_match_index( + &mut self, + matches: Vec, + cx: &mut ViewContext, + ) -> Option { + self.editor + .update(cx, |editor, cx| editor.active_match_index(matches, cx)) + } +} diff --git a/crates/zed/src/system_specs.rs b/crates/feedback/src/system_specs.rs similarity index 92% rename from crates/zed/src/system_specs.rs rename to crates/feedback/src/system_specs.rs index b6c2c0fcba..17e51a6815 100644 --- a/crates/zed/src/system_specs.rs +++ b/crates/feedback/src/system_specs.rs @@ -2,9 +2,11 @@ use std::{env, fmt::Display}; use gpui::AppContext; use human_bytes::human_bytes; +use serde::Serialize; use sysinfo::{System, SystemExt}; use util::channel::ReleaseChannel; +#[derive(Debug, Serialize)] pub struct SystemSpecs { app_version: &'static str, release_channel: &'static str, @@ -40,7 +42,7 @@ impl Display for SystemSpecs { None => format!("OS: {}", self.os_name), }; let system_specs = [ - format!("Zed: {} ({})", self.app_version, self.release_channel), + format!("Zed: v{} ({})", self.app_version, self.release_channel), os_information, format!("Memory: {}", human_bytes(self.memory as f64)), format!("Architecture: {}", self.architecture), diff --git a/crates/file_finder/Cargo.toml b/crates/file_finder/Cargo.toml index 6fd792940d..1d1a4dfb1b 100644 --- a/crates/file_finder/Cargo.toml +++ b/crates/file_finder/Cargo.toml @@ -2,6 +2,7 @@ name = "file_finder" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/file_finder.rs" diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 5b9082d114..cd6c8f969c 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -2,6 +2,7 @@ name = "fs" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/fs.rs" diff --git a/crates/fsevent/Cargo.toml b/crates/fsevent/Cargo.toml index 3bf7ae885b..e71e247bf2 100644 --- a/crates/fsevent/Cargo.toml +++ b/crates/fsevent/Cargo.toml @@ -3,6 +3,7 @@ name = "fsevent" version = "2.0.2" license = "MIT" edition = "2021" +publish = false [lib] path = "src/fsevent.rs" diff --git a/crates/fuzzy/Cargo.toml b/crates/fuzzy/Cargo.toml index e36c22055a..553c0497a5 100644 --- a/crates/fuzzy/Cargo.toml +++ b/crates/fuzzy/Cargo.toml @@ -2,6 +2,7 @@ name = "fuzzy" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/fuzzy.rs" diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index 66202a489a..3e88d72313 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -2,6 +2,7 @@ name = "git" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/git.rs" diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index 93ae96f93e..def6361dc2 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -2,6 +2,7 @@ name = "go_to_line" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/go_to_line.rs" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 5153c1f7c1..e1b6e11b46 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -4,6 +4,7 @@ edition = "2021" name = "gpui" version = "0.1.0" description = "A GPU-accelerated UI framework" +publish = false [lib] path = "src/gpui.rs" diff --git a/crates/gpui_macros/Cargo.toml b/crates/gpui_macros/Cargo.toml index e35e0b1d2b..76daeae2a8 100644 --- a/crates/gpui_macros/Cargo.toml +++ b/crates/gpui_macros/Cargo.toml @@ -2,6 +2,7 @@ name = "gpui_macros" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/gpui_macros.rs" diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml index 9622049a9c..b532397dd1 100644 --- a/crates/journal/Cargo.toml +++ b/crates/journal/Cargo.toml @@ -2,6 +2,7 @@ name = "journal" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/journal.rs" diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 64db58c847..8f1d3d39ed 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -2,6 +2,7 @@ name = "language" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/language.rs" diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index d0f54782b9..0145737508 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -3,6 +3,7 @@ name = "live_kit_client" version = "0.1.0" edition = "2021" description = "Bindings to LiveKit Swift client SDK" +publish = false [lib] path = "src/live_kit_client.rs" diff --git a/crates/live_kit_server/Cargo.toml b/crates/live_kit_server/Cargo.toml index 64267f62d1..17ee3cd62e 100644 --- a/crates/live_kit_server/Cargo.toml +++ b/crates/live_kit_server/Cargo.toml @@ -3,6 +3,7 @@ name = "live_kit_server" version = "0.1.0" edition = "2021" description = "SDK for the LiveKit server API" +publish = false [lib] path = "src/live_kit_server.rs" diff --git a/crates/lsp/Cargo.toml b/crates/lsp/Cargo.toml index 94780a9472..eb6e02aac9 100644 --- a/crates/lsp/Cargo.toml +++ b/crates/lsp/Cargo.toml @@ -2,6 +2,7 @@ name = "lsp" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/lsp.rs" diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index aad2b74c02..4c230819e2 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -2,6 +2,7 @@ name = "media" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/media.rs" diff --git a/crates/menu/Cargo.toml b/crates/menu/Cargo.toml index cdcacd4416..c473df7ef0 100644 --- a/crates/menu/Cargo.toml +++ b/crates/menu/Cargo.toml @@ -2,6 +2,7 @@ name = "menu" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/menu.rs" diff --git a/crates/outline/Cargo.toml b/crates/outline/Cargo.toml index 5b4751e620..661c84c8cd 100644 --- a/crates/outline/Cargo.toml +++ b/crates/outline/Cargo.toml @@ -2,6 +2,7 @@ name = "outline" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/outline.rs" diff --git a/crates/picker/Cargo.toml b/crates/picker/Cargo.toml index 6438697910..e7a8079caa 100644 --- a/crates/picker/Cargo.toml +++ b/crates/picker/Cargo.toml @@ -2,6 +2,7 @@ name = "picker" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/picker.rs" diff --git a/crates/plugin/Cargo.toml b/crates/plugin/Cargo.toml index 6f37c458d9..7bf5510465 100644 --- a/crates/plugin/Cargo.toml +++ b/crates/plugin/Cargo.toml @@ -2,6 +2,7 @@ name = "plugin" version = "0.1.0" edition = "2021" +publish = false [dependencies] serde = "1.0" diff --git a/crates/plugin_macros/Cargo.toml b/crates/plugin_macros/Cargo.toml index b0a2c4e09b..32bfc6a01a 100644 --- a/crates/plugin_macros/Cargo.toml +++ b/crates/plugin_macros/Cargo.toml @@ -2,6 +2,7 @@ name = "plugin_macros" version = "0.1.0" edition = "2021" +publish = false [lib] proc-macro = true diff --git a/crates/plugin_runtime/Cargo.toml b/crates/plugin_runtime/Cargo.toml index a8c0a063a8..b5cfb9514f 100644 --- a/crates/plugin_runtime/Cargo.toml +++ b/crates/plugin_runtime/Cargo.toml @@ -2,6 +2,7 @@ name = "plugin_runtime" version = "0.1.0" edition = "2021" +publish = false [dependencies] wasmtime = "0.38" diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 6d6560ea38..949dbdf916 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -2,6 +2,7 @@ name = "project" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/project.rs" diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index 8704f57c8c..cc27f40954 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -2,6 +2,7 @@ name = "project_panel" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/project_panel.rs" diff --git a/crates/project_symbols/Cargo.toml b/crates/project_symbols/Cargo.toml index a426e2e0d4..e9283b14c9 100644 --- a/crates/project_symbols/Cargo.toml +++ b/crates/project_symbols/Cargo.toml @@ -2,6 +2,7 @@ name = "project_symbols" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/project_symbols.rs" diff --git a/crates/recent_projects/Cargo.toml b/crates/recent_projects/Cargo.toml index d633381365..037c6fd4fb 100644 --- a/crates/recent_projects/Cargo.toml +++ b/crates/recent_projects/Cargo.toml @@ -2,6 +2,7 @@ name = "recent_projects" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/recent_projects.rs" diff --git a/crates/rope/Cargo.toml b/crates/rope/Cargo.toml index e9ddcc8195..8257ef4a6e 100644 --- a/crates/rope/Cargo.toml +++ b/crates/rope/Cargo.toml @@ -2,6 +2,7 @@ name = "rope" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/rope.rs" diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index cd959e75a1..25c2ce1ca7 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -3,6 +3,7 @@ description = "Shared logic for communication between the Zed app and the zed.de edition = "2021" name = "rpc" version = "0.1.0" +publish = false [lib] path = "src/rpc.rs" diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index 3a5d9468fc..f36865a89b 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -2,6 +2,7 @@ name = "search" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/search.rs" diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index a292358e75..c1ec2a6210 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -2,6 +2,7 @@ name = "settings" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/settings.rs" diff --git a/crates/snippet/Cargo.toml b/crates/snippet/Cargo.toml index 03a0f8314a..429f5d416e 100644 --- a/crates/snippet/Cargo.toml +++ b/crates/snippet/Cargo.toml @@ -2,6 +2,7 @@ name = "snippet" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/snippet.rs" diff --git a/crates/sqlez/Cargo.toml b/crates/sqlez/Cargo.toml index 78bf83dc30..8409a1dff5 100644 --- a/crates/sqlez/Cargo.toml +++ b/crates/sqlez/Cargo.toml @@ -2,6 +2,7 @@ name = "sqlez" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/crates/sqlez_macros/Cargo.toml b/crates/sqlez_macros/Cargo.toml index 423b494500..8d650074c3 100644 --- a/crates/sqlez_macros/Cargo.toml +++ b/crates/sqlez_macros/Cargo.toml @@ -2,6 +2,7 @@ name = "sqlez_macros" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/sqlez_macros.rs" diff --git a/crates/sum_tree/Cargo.toml b/crates/sum_tree/Cargo.toml index 02cad4fb9d..3aab16cc3e 100644 --- a/crates/sum_tree/Cargo.toml +++ b/crates/sum_tree/Cargo.toml @@ -2,6 +2,7 @@ name = "sum_tree" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/sum_tree.rs" diff --git a/crates/terminal/Cargo.toml b/crates/terminal/Cargo.toml index 0dea7bfbcf..c6f33a0fc7 100644 --- a/crates/terminal/Cargo.toml +++ b/crates/terminal/Cargo.toml @@ -2,6 +2,7 @@ name = "terminal" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/terminal.rs" diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index 05fda2c75f..f12e4be03b 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -2,6 +2,7 @@ name = "terminal_view" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/terminal_view.rs" diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index ad960ec93e..5fda4b613c 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -2,6 +2,7 @@ name = "text" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/text.rs" diff --git a/crates/theme/Cargo.toml b/crates/theme/Cargo.toml index 36de158afe..1e9883860f 100644 --- a/crates/theme/Cargo.toml +++ b/crates/theme/Cargo.toml @@ -2,6 +2,7 @@ name = "theme" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/theme.rs" diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index 59cb5fbc2c..8f6fc74600 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -2,6 +2,7 @@ name = "theme_selector" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/theme_selector.rs" diff --git a/crates/theme_testbench/Cargo.toml b/crates/theme_testbench/Cargo.toml index 5fb263501f..763727fc68 100644 --- a/crates/theme_testbench/Cargo.toml +++ b/crates/theme_testbench/Cargo.toml @@ -2,6 +2,7 @@ name = "theme_testbench" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/theme_testbench.rs" diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 0a0bacf53c..4cbaa382e8 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -2,6 +2,7 @@ name = "util" version = "0.1.0" edition = "2021" +publish = false [lib] doctest = false diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index daefebdbdd..bd94e48b9e 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -2,6 +2,7 @@ name = "vim" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/vim.rs" diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 5894a2a44e..60680f82a2 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -2,6 +2,7 @@ name = "workspace" version = "0.1.0" edition = "2021" +publish = false [lib] path = "src/workspace.rs" diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 8d0d731551..7e56b864bf 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -103,7 +103,7 @@ impl_internal_actions!( DeploySplitMenu, DeployNewMenu, DeployDockMenu, - MoveItem, + MoveItem ] ); @@ -1150,7 +1150,7 @@ impl Pane { row.add_child({ enum Tab {} - dragged_item_receiver::(ix, ix, true, None, cx, { + let mut receiver = dragged_item_receiver::(ix, ix, true, None, cx, { let item = item.clone(); let pane = pane.clone(); let detail = detail.clone(); @@ -1162,50 +1162,51 @@ impl Pane { let hovered = mouse_state.hovered(); Self::render_tab(&item, pane, ix == 0, detail, hovered, tab_style, cx) } - }) - .with_cursor_style(if pane_active && tab_active { - CursorStyle::Arrow - } else { - CursorStyle::PointingHand - }) - .on_down(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ActivateItem(ix)); - cx.propagate_event(); - }) - .on_click(MouseButton::Middle, { - let item = item.clone(); - let pane = pane.clone(); - move |_, cx: &mut EventContext| { - cx.dispatch_action(CloseItem { - item_id: item.id(), - pane: pane.clone(), - }) - } - }) - .as_draggable( - DraggedItem { - item, - pane: pane.clone(), - }, - { - let theme = cx.global::().theme.clone(); + }); - let detail = detail.clone(); - move |dragged_item, cx: &mut RenderContext| { - let tab_style = &theme.workspace.tab_bar.dragged_tab; - Self::render_tab( - &dragged_item.item, - dragged_item.pane.clone(), - false, - detail, - false, - &tab_style, - cx, - ) + if !pane_active || !tab_active { + receiver = receiver.with_cursor_style(CursorStyle::PointingHand); + } + + receiver + .on_down(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ActivateItem(ix)); + cx.propagate_event(); + }) + .on_click(MouseButton::Middle, { + let item = item.clone(); + let pane = pane.clone(); + move |_, cx: &mut EventContext| { + cx.dispatch_action(CloseItem { + item_id: item.id(), + pane: pane.clone(), + }) } - }, - ) - .boxed() + }) + .as_draggable( + DraggedItem { + item, + pane: pane.clone(), + }, + { + let theme = cx.global::().theme.clone(); + + let detail = detail.clone(); + move |dragged_item, cx: &mut RenderContext| { + let tab_style = &theme.workspace.tab_bar.dragged_tab; + Self::render_tab( + &dragged_item.item, + dragged_item.pane.clone(), + false, + detail, + false, + &tab_style, + cx, + ) + } + }, + ) + .boxed() }) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 8735f33fbe..ec7ba8fae0 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -99,6 +99,7 @@ actions!( ToggleRightSidebar, NewTerminal, NewSearch, + Feedback, ShowNotif, ] ); @@ -233,6 +234,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { workspace.toggle_sidebar(SidebarSide::Right, cx); }); cx.add_action(Workspace::activate_pane_at_index); + cx.add_action(Workspace::split_pane_with_item); cx.add_action(Workspace::split_pane_with_project_entry); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 63719b7003..d159271f99 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -4,6 +4,7 @@ description = "The fast, collaborative code editor." edition = "2021" name = "zed" version = "0.71.0" +publish = false [lib] name = "zed" @@ -29,8 +30,8 @@ client = { path = "../client" } clock = { path = "../clock" } diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } +feedback = { path = "../feedback" } file_finder = { path = "../file_finder" } -human_bytes = "0.4.1" search = { path = "../search" } fs = { path = "../fs" } fsevent = { path = "../fsevent" } @@ -49,7 +50,6 @@ recent_projects = { path = "../recent_projects" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } -sysinfo = "0.27.1" text = { path = "../text" } terminal_view = { path = "../terminal_view" } theme = { path = "../theme" } @@ -110,7 +110,6 @@ tree-sitter-html = "0.19.0" tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"} tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a"} url = "2.2" -urlencoding = "2.1.2" [dev-dependencies] call = { path = "../call", features = ["test-support"] } diff --git a/crates/zed/src/feedback.rs b/crates/zed/src/feedback.rs deleted file mode 100644 index 55597312ae..0000000000 --- a/crates/zed/src/feedback.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::OpenBrowser; -use gpui::{ - elements::{MouseEventHandler, Text}, - platform::CursorStyle, - Element, Entity, MouseButton, RenderContext, View, -}; -use settings::Settings; -use workspace::{item::ItemHandle, StatusItemView}; - -pub const NEW_ISSUE_URL: &str = "https://github.com/zed-industries/feedback/issues/new/choose"; - -pub struct FeedbackLink; - -impl Entity for FeedbackLink { - type Event = (); -} - -impl View for FeedbackLink { - fn ui_name() -> &'static str { - "FeedbackLink" - } - - fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> gpui::ElementBox { - MouseEventHandler::::new(0, cx, |state, cx| { - let theme = &cx.global::().theme; - let theme = &theme.workspace.status_bar.feedback; - Text::new( - "Give Feedback".to_string(), - theme.style_for(state, false).clone(), - ) - .boxed() - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(OpenBrowser { - url: NEW_ISSUE_URL.into(), - }) - }) - .boxed() - } -} - -impl StatusItemView for FeedbackLink { - fn set_active_pane_item( - &mut self, - _: Option<&dyn ItemHandle>, - _: &mut gpui::ViewContext, - ) { - } -} diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 16fd0c1eea..fe7e95cf24 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -14,6 +14,7 @@ use client::{ http::{self, HttpClient}, UserStore, ZED_SECRET_CLIENT_TOKEN, }; + use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, @@ -125,11 +126,14 @@ fn main() { watch_keymap_file(keymap_file, cx); + cx.set_global(client.clone()); + context_menu::init(cx); project::Project::init(&client); client::init(client.clone(), cx); command_palette::init(cx); editor::init(cx); + feedback::init(cx); go_to_line::init(cx); file_finder::init(cx); outline::init(cx); diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index b46e8ad703..834eb751e1 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -340,15 +340,15 @@ pub fn menus() -> Vec> { MenuItem::Separator, MenuItem::Action { name: "Copy System Specs Into Clipboard", - action: Box::new(crate::CopySystemSpecsIntoClipboard), + action: Box::new(feedback::CopySystemSpecsIntoClipboard), }, MenuItem::Action { name: "File Bug Report", - action: Box::new(crate::FileBugReport), + action: Box::new(feedback::FileBugReport), }, MenuItem::Action { name: "Request Feature", - action: Box::new(crate::RequestFeature), + action: Box::new(feedback::RequestFeature), }, MenuItem::Separator, MenuItem::Action { diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 19c6bae6eb..c9f8b2d408 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,10 +1,7 @@ -mod feedback; pub mod languages; pub mod menus; -pub mod system_specs; #[cfg(any(test, feature = "test-support"))] pub mod test; - use anyhow::{anyhow, Context, Result}; use assets::Assets; use breadcrumbs::Breadcrumbs; @@ -23,8 +20,7 @@ use gpui::{ }, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, AsyncAppContext, ClipboardItem, PromptLevel, TitlebarOptions, ViewContext, - WindowKind, + AssetSource, AsyncAppContext, PromptLevel, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; use lazy_static::lazy_static; @@ -35,14 +31,13 @@ use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; use serde_json::to_string_pretty; use settings::{keymap_file_json_schema, settings_file_json_schema, Settings}; -use std::{env, path::Path, str, sync::Arc}; -use system_specs::SystemSpecs; +use std::{borrow::Cow, env, path::Path, str, sync::Arc}; use util::{channel::ReleaseChannel, paths, ResultExt}; pub use workspace; use workspace::{sidebar::SidebarSide, AppState, Workspace}; #[derive(Deserialize, Clone, PartialEq)] -struct OpenBrowser { +pub struct OpenBrowser { url: Arc, } @@ -62,6 +57,7 @@ actions!( DebugElements, OpenSettings, OpenLog, + OpenLicenses, OpenTelemetryLog, OpenKeymap, OpenDefaultSettings, @@ -71,9 +67,6 @@ actions!( ResetBufferFontSize, InstallCommandLineInterface, ResetDatabase, - CopySystemSpecsIntoClipboard, - RequestFeature, - FileBugReport ] ); @@ -184,6 +177,19 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { open_log_file(workspace, app_state.clone(), cx); } }); + cx.add_action({ + let app_state = app_state.clone(); + move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { + open_bundled_file( + workspace, + app_state.clone(), + "licenses.md", + "Open Source License Attribution", + "Markdown", + cx, + ); + } + }); cx.add_action({ let app_state = app_state.clone(); move |workspace: &mut Workspace, _: &OpenTelemetryLog, cx: &mut ViewContext| { @@ -199,11 +205,12 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.add_action({ let app_state = app_state.clone(); move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { - open_bundled_config_file( + open_bundled_file( workspace, app_state.clone(), "keymaps/default.json", "Default Key Bindings", + "JSON", cx, ); } @@ -213,11 +220,12 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { move |workspace: &mut Workspace, _: &OpenDefaultSettings, cx: &mut ViewContext| { - open_bundled_config_file( + open_bundled_file( workspace, app_state.clone(), "settings/default.json", "Default Settings", + "JSON", cx, ); } @@ -256,41 +264,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }, ); - cx.add_action( - |_: &mut Workspace, _: &CopySystemSpecsIntoClipboard, cx: &mut ViewContext| { - let system_specs = SystemSpecs::new(cx).to_string(); - let item = ClipboardItem::new(system_specs.clone()); - cx.prompt( - gpui::PromptLevel::Info, - &format!("Copied into clipboard:\n\n{system_specs}"), - &["OK"], - ); - cx.write_to_clipboard(item); - }, - ); - - cx.add_action( - |_: &mut Workspace, _: &RequestFeature, cx: &mut ViewContext| { - let url = "https://github.com/zed-industries/feedback/issues/new?assignees=&labels=enhancement%2Ctriage&template=0_feature_request.yml"; - cx.dispatch_action(OpenBrowser { - url: url.into(), - }); - }, - ); - - cx.add_action( - |_: &mut Workspace, _: &FileBugReport, cx: &mut ViewContext| { - let system_specs_text = SystemSpecs::new(cx).to_string(); - let url = format!( - "https://github.com/zed-industries/feedback/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml&environment={}", - urlencoding::encode(&system_specs_text) - ); - cx.dispatch_action(OpenBrowser { - url: url.into(), - }); - }, - ); - activity_indicator::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); settings::KeymapFileContent::load_defaults(cx); @@ -375,12 +348,12 @@ pub fn initialize_workspace( let activity_indicator = activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); - let feedback_link = cx.add_view(|_| feedback::FeedbackLink); + let feedback_button = cx.add_view(|_| feedback::feedback_editor::FeedbackButton {}); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(cursor_position, cx); - status_bar.add_right_item(feedback_link, cx); + status_bar.add_right_item(feedback_button, cx); }); auto_update::notify_of_any_new_update(cx.weak_handle(), cx); @@ -660,21 +633,24 @@ fn open_telemetry_log_file( }).detach(); } -fn open_bundled_config_file( +fn open_bundled_file( workspace: &mut Workspace, app_state: Arc, asset_path: &'static str, title: &'static str, + language: &'static str, cx: &mut ViewContext, ) { workspace .with_local_workspace(&app_state, cx, |workspace, cx| { let project = workspace.project().clone(); let buffer = project.update(cx, |project, cx| { - let text = Assets::get(asset_path).unwrap().data; + let text = Assets::get(asset_path) + .map(|f| f.data) + .unwrap_or_else(|| Cow::Borrowed(b"File not found")); let text = str::from_utf8(text.as_ref()).unwrap(); project - .create_buffer(text, project.languages().language_for_name("JSON"), cx) + .create_buffer(text, project.languages().language_for_name(language), cx) .expect("creating buffers on a local workspace always succeeds") }); let buffer = diff --git a/script/generate-licenses b/script/generate-licenses new file mode 100755 index 0000000000..e1a917292c --- /dev/null +++ b/script/generate-licenses @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +[[ "$(cargo about --version)" == "cargo-about 0.5.2" ]] || cargo install cargo-about --locked --git https://github.com/zed-industries/cargo-about --branch error-code-on-warn + +cargo about generate --fail-on-missing-license -o assets/licenses.md -c script/licenses/zed-licenses.toml script/licenses/template.hbs.md + +# cargo about automatically html-escapes all output, so we need to undo it here: +sed -i '' 's/"/"/g' assets/licenses.md +sed -i '' 's/'/'\''/g' assets/licenses.md # `'\''` ends the string, appends a single quote, and re-opens the string +sed -i '' 's/=/=/g' assets/licenses.md +sed -i '' 's/`/`/g' assets/licenses.md +sed -i '' 's/<//g' assets/licenses.md \ No newline at end of file diff --git a/script/licenses/template.hbs.md b/script/licenses/template.hbs.md new file mode 100644 index 0000000000..a51b714dae --- /dev/null +++ b/script/licenses/template.hbs.md @@ -0,0 +1,27 @@ +# Third Party Licenses + +This page lists the licenses of the projects used in Zed. + +## Overview of licenses: + +{{#each overview}} +* {{name}} ({{count}}) +{{/each}} + +## All license texts: + +{{#each licenses}} + +### {{name}} + +#### Used by: + +{{#each used_by}} +* [{{crate.name}} {{crate.version}}]({{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}) +{{/each}} + +{{text}} + +-------------------------------------------------------------------------------- + +{{/each}} \ No newline at end of file diff --git a/script/licenses/zed-licenses.toml b/script/licenses/zed-licenses.toml new file mode 100644 index 0000000000..d338e7ab0b --- /dev/null +++ b/script/licenses/zed-licenses.toml @@ -0,0 +1,37 @@ +no-clearly-defined = true +private = { ignore = true } +accepted = [ + "Apache-2.0", + "MIT", + "Apache-2.0 WITH LLVM-exception", + "MPL-2.0", + "BSD-3-Clause", + "BSD-2-Clause", + "ISC", + "CC0-1.0", + "Unicode-DFS-2016", + "OpenSSL", + "Zlib", +] +workarounds = [ + "ring", + "wasmtime", +] + +[procinfo.clarify] +license = "MIT" +[[procinfo.clarify.git]] +path = 'LICENSE.md' +checksum = '37db33bbbd7348969eda397b89a16f252d56c1ca7481b6ccaf56ccdcbab5dcca' + +[webpki.clarify] +license = "ISC" # It actually says 'ISC-style' but I don't know the SPDX expression for that. +[[webpki.clarify.files]] +path = 'LICENSE' +checksum = '5b698ca13897be3afdb7174256fa1574f8c6892b8bea1a66dd6469d3fe27885a' + +[fuchsia-cprng.clarify] +license = "BSD-3-Clause" +[[fuchsia-cprng.clarify.files]] +path = 'LICENSE' +checksum = '03b114f53e6587a398931762ee11e2395bfdba252a329940e2c8c9e81813845b' diff --git a/styles/src/themes/one-light.ts b/styles/src/themes/one-light.ts index d8c8e5272c..a5ac1f7158 100644 --- a/styles/src/themes/one-light.ts +++ b/styles/src/themes/one-light.ts @@ -11,15 +11,15 @@ const license = { export const light = createColorScheme(`${name}`, true, { neutral: chroma.scale([ - "#090a0b", - "#202227", - "#383a42", - "#696c77", - "#a0a1a7", - "#e5e5e6", - "#f0f0f1", - "#fafafa", - ]) + "#090a0b", + "#202227", + "#383a42", + "#696c77", + "#a0a1a7", + "#e5e5e6", + "#f0f0f1", + "#fafafa", + ]) .domain([0.05, 0.22, 0.25, 0.45, 0.62, 0.8, 0.9, 1]), red: colorRamp(chroma("#ca1243")),