From cc8ae45012cf3dee93362e6492613629ac436d4d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 10 Oct 2022 17:10:42 -0700 Subject: [PATCH 01/22] Added theme writing code, really bad race condition --- Cargo.lock | 14 +- Cargo.toml | 5 + constellation.txt | 10 + crates/editor/Cargo.toml | 2 +- crates/paths/Cargo.toml | 12 ++ crates/{zed => paths}/src/paths.rs | 0 crates/settings/Cargo.toml | 10 +- crates/settings/src/settings.rs | 194 +++++++++++++++++++- crates/theme_selector/Cargo.toml | 2 + crates/theme_selector/src/theme_selector.rs | 38 +++- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 39 ++-- crates/zed/src/zed.rs | 1 - 13 files changed, 297 insertions(+), 32 deletions(-) create mode 100644 constellation.txt create mode 100644 crates/paths/Cargo.toml rename crates/{zed => paths}/src/paths.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 0448538c35..177b64c2ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3738,6 +3738,14 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "paths" +version = "0.1.0" +dependencies = [ + "dirs 3.0.2", + "lazy_static", +] + [[package]] name = "pbkdf2" version = "0.8.0" @@ -5021,6 +5029,9 @@ dependencies = [ "serde_path_to_error", "theme", "toml", + "tree-sitter", + "tree-sitter-json 0.19.0", + "unindent", "util", ] @@ -5626,6 +5637,7 @@ dependencies = [ "gpui", "log", "parking_lot 0.11.2", + "paths", "picker", "postage", "settings", @@ -7251,7 +7263,6 @@ dependencies = [ "context_menu", "ctor", "diagnostics", - "dirs 3.0.2", "easy-parallel", "editor", "env_logger", @@ -7274,6 +7285,7 @@ dependencies = [ "num_cpus", "outline", "parking_lot 0.11.2", + "paths", "plugin_runtime", "postage", "project", diff --git a/Cargo.toml b/Cargo.toml index 31a9118a1a..b64df300c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,10 @@ members = ["crates/*"] default-members = ["crates/zed"] resolver = "2" +[workspace.dependencies] +serde = { version = "1.0", features = ["derive", "rc"] } +serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } + [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "366210ae925d7ea0891bc7a0c738f60c77c04d7b" } async-task = { git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e" } @@ -21,3 +25,4 @@ split-debuginfo = "unpacked" [profile.release] debug = true + diff --git a/constellation.txt b/constellation.txt new file mode 100644 index 0000000000..b8b8f77e03 --- /dev/null +++ b/constellation.txt @@ -0,0 +1,10 @@ +Burn down pain points constellation + +--------------------- +| | +| * | Mikayla +| * | Julia +| | +| * | Kay +| | +--------------------- \ No newline at end of file diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index e8695e5ddc..db634376d0 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -48,7 +48,7 @@ ordered-float = "2.1.1" parking_lot = "0.11" postage = { version = "0.4", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } -serde = { version = "1.0", features = ["derive", "rc"] } +serde = { workspace = true } smallvec = { version = "1.6", features = ["union"] } smol = "1.2" tree-sitter-rust = { version = "*", optional = true } diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml new file mode 100644 index 0000000000..3da33cce1b --- /dev/null +++ b/crates/paths/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "paths" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/paths.rs" + +[dependencies] +lazy_static = "1.4.0" +dirs = "3.0" + diff --git a/crates/zed/src/paths.rs b/crates/paths/src/paths.rs similarity index 100% rename from crates/zed/src/paths.rs rename to crates/paths/src/paths.rs diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 78440a2418..e8fb58cd61 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -19,7 +19,13 @@ util = { path = "../util" } anyhow = "1.0.38" json_comments = "0.2" schemars = "0.8" -serde = { version = "1.0", features = ["derive", "rc"] } -serde_json = { version = "1.0", features = ["preserve_order"] } +serde = { workspace = true } +serde_json = { workspace = true } serde_path_to_error = "0.1.4" toml = "0.5" +tree-sitter = "*" +tree-sitter-json = "*" + +[dev-dependencies] +unindent = "0.1" +gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index d661eb0f21..921b36051f 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -12,8 +12,9 @@ use schemars::{ }; use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; -use std::{collections::HashMap, num::NonZeroU32, str, sync::Arc}; +use std::{collections::HashMap, fmt::Write as _, num::NonZeroU32, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; +use tree_sitter::Query; use util::ResultExt as _; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; @@ -501,6 +502,92 @@ pub fn settings_file_json_schema( serde_json::to_value(root_schema).unwrap() } +pub fn write_theme(mut settings_content: String, new_val: &str) -> String { + let mut parser = tree_sitter::Parser::new(); + parser.set_language(tree_sitter_json::language()).unwrap(); + let tree = parser.parse(&settings_content, None).unwrap(); + + let mut cursor = tree_sitter::QueryCursor::new(); + + let query = Query::new( + tree_sitter_json::language(), + " + (document + (object + (pair + key: (string) @key + value: (_) @value))) + ", + ) + .unwrap(); + + let mut first_key_start = None; + let mut existing_value_range = None; + let matches = cursor.matches(&query, tree.root_node(), settings_content.as_bytes()); + for mat in matches { + if mat.captures.len() != 2 { + continue; + } + + let key = mat.captures[0]; + let value = mat.captures[1]; + + first_key_start.get_or_insert_with(|| key.node.start_byte()); + + if let Some(key_text) = settings_content.get(key.node.byte_range()) { + if key_text == "\"theme\"" { + existing_value_range = Some(value.node.byte_range()); + break; + } + } + } + + match (first_key_start, existing_value_range) { + (None, None) => { + // No document, create a new object and overwrite + settings_content.clear(); + write!(settings_content, "{{\n \"theme\": \"{new_val}\"\n}}\n").unwrap(); + } + + (_, Some(existing_value_range)) => { + // Existing theme key, overwrite + settings_content.replace_range(existing_value_range, &format!("\"{new_val}\"")); + } + + (Some(first_key_start), None) => { + // No existing theme key, but other settings. Prepend new theme settings and + // match style of first key + let mut row = 0; + let mut column = 0; + for (ix, char) in settings_content.char_indices() { + if ix == first_key_start { + break; + } + if char == '\n' { + row += 1; + column = 0; + } else { + column += char.len_utf8(); + } + } + + let content = format!(r#""theme": "{new_val}","#); + settings_content.insert_str(first_key_start, &content); + + if row > 0 { + settings_content.insert_str( + first_key_start + content.len(), + &format!("\n{:width$}", ' ', width = column), + ) + } else { + settings_content.insert_str(first_key_start + content.len(), " ") + } + } + } + + settings_content +} + fn merge(target: &mut T, value: Option) { if let Some(value) = value { *target = value; @@ -512,3 +599,108 @@ pub fn parse_json_with_comments(content: &str) -> Result json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), )?) } + +#[cfg(test)] +mod tests { + use crate::write_theme; + use unindent::Unindent; + + #[test] + fn test_write_theme_into_settings_with_theme() { + let settings = r#" + { + "theme": "one-dark" + } + "# + .unindent(); + + let new_settings = r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_empty_settings() { + let settings = r#" + { + } + "# + .unindent(); + + let new_settings = r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_no_settings() { + let settings = "".to_string(); + + let new_settings = r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_single_line_settings_without_theme() { + let settings = r#"{ "a": "", "ok": true }"#.to_string(); + let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_pre_object_whitespace() { + let settings = r#" { "a": "", "ok": true }"#.to_string(); + let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } + + #[test] + fn test_write_theme_into_multi_line_settings_without_theme() { + let settings = r#" + { + "a": "b" + } + "# + .unindent(); + + let new_settings = r#" + { + "theme": "summerfruit-light", + "a": "b" + } + "# + .unindent(); + + let settings_after_theme = write_theme(settings, "summerfruit-light"); + + assert_eq!(settings_after_theme, new_settings) + } +} diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index 804eff2c7a..e323677d3b 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -14,8 +14,10 @@ gpui = { path = "../gpui" } picker = { path = "../picker" } theme = { path = "../theme" } settings = { path = "../settings" } +paths = { path = "../paths" } workspace = { path = "../workspace" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } smol = "1.2.5" + diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 59b0bc7e6a..a7d17ad533 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,10 +1,12 @@ use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, MouseState, - MutableAppContext, RenderContext, View, ViewContext, ViewHandle, + actions, anyhow::Result, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, + MouseState, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; +use paths::SETTINGS; use picker::{Picker, PickerDelegate}; -use settings::Settings; +use settings::{write_theme, Settings}; +use smol::{fs::read_to_string, io::AsyncWriteExt}; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; use workspace::{AppState, Workspace}; @@ -107,7 +109,20 @@ impl ThemeSelector { fn show_selected_theme(&mut self, cx: &mut ViewContext) { if let Some(mat) = self.matches.get(self.selected_index) { match self.registry.get(&mat.string) { - Ok(theme) => Self::set_theme(theme, cx), + Ok(theme) => { + Self::set_theme(theme, cx); + + let theme_name = mat.string.clone(); + + cx.background() + .spawn(async move { + match write_theme_name(theme_name).await { + Ok(_) => {} + Err(_) => return, //TODO Pop toast + } + }) + .detach() + } Err(error) => { log::error!("error loading theme {}: {}", mat.string, error) } @@ -264,3 +279,18 @@ impl View for ThemeSelector { } } } + +async fn write_theme_name(theme_name: String) -> Result<()> { + let mut settings = read_to_string(SETTINGS.as_path()).await?; + settings = write_theme(settings, &theme_name); + + let mut file = smol::fs::OpenOptions::new() + .truncate(true) + .write(true) + .open(SETTINGS.as_path()) + .await?; + + file.write_all(settings.as_bytes()).await?; + + Ok(()) +} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d0b41b08f1..d57a2c21b1 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -40,6 +40,7 @@ journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } outline = { path = "../outline" } +paths = { path = "../paths" } plugin_runtime = { path = "../plugin_runtime" } project = { path = "../project" } project_panel = { path = "../project_panel" } @@ -61,7 +62,6 @@ async-trait = "0.1" backtrace = "0.3" chrono = "0.4" ctor = "0.1.20" -dirs = "3.0" easy-parallel = "3.1.0" env_logger = "0.9" futures = "0.3" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f48f8b723e..519f04fe35 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -53,7 +53,7 @@ fn main() { .map_or("dev".to_string(), |v| v.to_string()); init_panic_hook(app_version, http.clone(), app.background()); let db = app.background().spawn(async move { - project::Db::open(&*zed::paths::DB) + project::Db::open(&*paths::DB) .log_err() .unwrap_or_else(project::Db::null) }); @@ -90,7 +90,7 @@ fn main() { app.run(move |cx| { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); - languages.set_language_server_download_dir(zed::paths::LANGUAGES_DIR.clone()); + languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); let init_languages = cx .background() @@ -200,23 +200,21 @@ fn main() { } fn init_paths() { - fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); - fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); - fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); - fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); + fs::create_dir_all(&*paths::CONFIG_DIR).expect("could not create config path"); + fs::create_dir_all(&*paths::LANGUAGES_DIR).expect("could not create languages path"); + fs::create_dir_all(&*paths::DB_DIR).expect("could not create database path"); + fs::create_dir_all(&*paths::LOGS_DIR).expect("could not create logs path"); // Copy setting files from legacy locations. TODO: remove this after a few releases. thread::spawn(|| { - if fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() - && fs::metadata(&*zed::paths::SETTINGS).is_err() + if fs::metadata(&*paths::legacy::SETTINGS).is_ok() + && fs::metadata(&*paths::SETTINGS).is_err() { - fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); + fs::copy(&*paths::legacy::SETTINGS, &*paths::SETTINGS).log_err(); } - if fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() - && fs::metadata(&*zed::paths::KEYMAP).is_err() - { - fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); + if fs::metadata(&*paths::legacy::KEYMAP).is_ok() && fs::metadata(&*paths::KEYMAP).is_err() { + fs::copy(&*paths::legacy::KEYMAP, &*paths::KEYMAP).log_err(); } }); } @@ -231,15 +229,14 @@ fn init_logger() { const KIB: u64 = 1024; const MIB: u64 = 1024 * KIB; const MAX_LOG_BYTES: u64 = MIB; - if fs::metadata(&*zed::paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) - { - let _ = fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); + if fs::metadata(&*paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) { + let _ = fs::rename(&*paths::LOG, &*paths::OLD_LOG); } let log_file = OpenOptions::new() .create(true) .append(true) - .open(&*zed::paths::LOG) + .open(&*paths::LOG) .expect("could not open logfile"); simplelog::WriteLogger::init(level, simplelog::Config::default(), log_file) .expect("could not initialize logger"); @@ -251,7 +248,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A .spawn({ async move { let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL); - let mut children = smol::fs::read_dir(&*zed::paths::LOGS_DIR).await?; + let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?; while let Some(child) = children.next().await { let child = child?; let child_path = child.path(); @@ -339,7 +336,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A let panic_filename = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); fs::write( - zed::paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), + paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), &message, ) .context("error writing panic to disk") @@ -473,8 +470,8 @@ fn load_config_files( .clone() .spawn(async move { let settings_file = - WatchedJsonFile::new(fs.clone(), &executor, zed::paths::SETTINGS.clone()).await; - let keymap_file = WatchedJsonFile::new(fs, &executor, zed::paths::KEYMAP.clone()).await; + WatchedJsonFile::new(fs.clone(), &executor, paths::SETTINGS.clone()).await; + let keymap_file = WatchedJsonFile::new(fs, &executor, paths::KEYMAP.clone()).await; tx.send((settings_file, keymap_file)).ok() }) .detach(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f6f3a34242..01b549e438 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,7 +1,6 @@ mod feedback; pub mod languages; pub mod menus; -pub mod paths; pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; From 7ce758b3438e49b9c8bf945b207915ef51b4881b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 10 Oct 2022 18:37:44 -0700 Subject: [PATCH 02/22] Added notes from working with Nathan --- crates/theme_selector/src/theme_selector.rs | 58 +++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index a7d17ad533..6484e0f627 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -114,6 +114,64 @@ impl ThemeSelector { let theme_name = mat.string.clone(); + // cx.global:: + // cx.global::() // lock + + // 1) Truncation can cause data loss, make it atomic by creating tmp file and moving + // 2) Maybe firing too often? Conceptually want to commit + // Having a lock on the settings file + + // - + // | | + // FS _ + + // General problem: whenever we want to persist stuff + // In memory representation -> File on disk + // Write font size, + // Write theme to disk + // Write -> See your own write -> Another Write + + // Memory Write 1 -> Write To Disk 1, | Memory Write 2, + // Blocking ->>>>>> | Read From Disk 1, + // Discard | Read WHATEVER is from disk | + + // Blocking lock -> + + // Whenever we update the settings in memory, we enqueue a write to disk + // When we receive a file system event, we only honor it if all pending disk writes are complete. + + // When the settings become dirty in memory, schedule a write to disk + // When we are sure the write is completed, consider the settings clean + // Only read settings from disk into memory when in memory settings are clean + // read settings just does not happen, if the settings are dirty + + // 10 settings queued up: + // lock() -> Only needs to be on the file + // How to set a setting: + // write to memory + // Read the whole file from disk + // Surgically inject the setting string + // Write to disk + // unlock() + + // Write 10 x change font size + // Read-open-write font size + // Read-open-write font size + // Read-open-write font size + // Read-open-write font size + // Read-open-write font size + // .. + // Read from file system, only gets the latest font size and uselessly sets font size + + // `SettingsFile` + // You can non-blocking, write to it as much as you need + // Debounces your changes, waits for you to be done, and then flushes them all to the file system + // And blocks the read + + // Read and write to memory. ^ up from the file system + + // If there's pendings writes, we need to wait until this whole thing is done' + cx.background() .spawn(async move { match write_theme_name(theme_name).await { From a833652077b37a4944de97e3c297aa23910081bf Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 11:29:57 -0700 Subject: [PATCH 03/22] Undid change to paths variables and cleaned up leftovers --- Cargo.lock | 11 +-- constellation.txt | 10 --- crates/paths/Cargo.toml | 12 --- crates/theme_selector/Cargo.toml | 1 - crates/theme_selector/src/theme_selector.rs | 92 +-------------------- crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 39 +++++---- crates/{paths => zed}/src/paths.rs | 0 crates/zed/src/zed.rs | 1 + 9 files changed, 27 insertions(+), 141 deletions(-) delete mode 100644 constellation.txt delete mode 100644 crates/paths/Cargo.toml rename crates/{paths => zed}/src/paths.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 177b64c2ae..cea25c027a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3738,14 +3738,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "paths" -version = "0.1.0" -dependencies = [ - "dirs 3.0.2", - "lazy_static", -] - [[package]] name = "pbkdf2" version = "0.8.0" @@ -5637,7 +5629,6 @@ dependencies = [ "gpui", "log", "parking_lot 0.11.2", - "paths", "picker", "postage", "settings", @@ -7263,6 +7254,7 @@ dependencies = [ "context_menu", "ctor", "diagnostics", + "dirs 3.0.2", "easy-parallel", "editor", "env_logger", @@ -7285,7 +7277,6 @@ dependencies = [ "num_cpus", "outline", "parking_lot 0.11.2", - "paths", "plugin_runtime", "postage", "project", diff --git a/constellation.txt b/constellation.txt deleted file mode 100644 index b8b8f77e03..0000000000 --- a/constellation.txt +++ /dev/null @@ -1,10 +0,0 @@ -Burn down pain points constellation - ---------------------- -| | -| * | Mikayla -| * | Julia -| | -| * | Kay -| | ---------------------- \ No newline at end of file diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml deleted file mode 100644 index 3da33cce1b..0000000000 --- a/crates/paths/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "paths" -version = "0.1.0" -edition = "2021" - -[lib] -path = "src/paths.rs" - -[dependencies] -lazy_static = "1.4.0" -dirs = "3.0" - diff --git a/crates/theme_selector/Cargo.toml b/crates/theme_selector/Cargo.toml index e323677d3b..59cb5fbc2c 100644 --- a/crates/theme_selector/Cargo.toml +++ b/crates/theme_selector/Cargo.toml @@ -14,7 +14,6 @@ gpui = { path = "../gpui" } picker = { path = "../picker" } theme = { path = "../theme" } settings = { path = "../settings" } -paths = { path = "../paths" } workspace = { path = "../workspace" } log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 6484e0f627..1cd7b3f926 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,12 +1,10 @@ use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, anyhow::Result, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, - MouseState, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, MouseState, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; -use paths::SETTINGS; use picker::{Picker, PickerDelegate}; -use settings::{write_theme, Settings}; -use smol::{fs::read_to_string, io::AsyncWriteExt}; +use settings::Settings; use std::sync::Arc; use theme::{Theme, ThemeMeta, ThemeRegistry}; use workspace::{AppState, Workspace}; @@ -111,75 +109,6 @@ impl ThemeSelector { match self.registry.get(&mat.string) { Ok(theme) => { Self::set_theme(theme, cx); - - let theme_name = mat.string.clone(); - - // cx.global:: - // cx.global::() // lock - - // 1) Truncation can cause data loss, make it atomic by creating tmp file and moving - // 2) Maybe firing too often? Conceptually want to commit - // Having a lock on the settings file - - // - - // | | - // FS _ - - // General problem: whenever we want to persist stuff - // In memory representation -> File on disk - // Write font size, - // Write theme to disk - // Write -> See your own write -> Another Write - - // Memory Write 1 -> Write To Disk 1, | Memory Write 2, - // Blocking ->>>>>> | Read From Disk 1, - // Discard | Read WHATEVER is from disk | - - // Blocking lock -> - - // Whenever we update the settings in memory, we enqueue a write to disk - // When we receive a file system event, we only honor it if all pending disk writes are complete. - - // When the settings become dirty in memory, schedule a write to disk - // When we are sure the write is completed, consider the settings clean - // Only read settings from disk into memory when in memory settings are clean - // read settings just does not happen, if the settings are dirty - - // 10 settings queued up: - // lock() -> Only needs to be on the file - // How to set a setting: - // write to memory - // Read the whole file from disk - // Surgically inject the setting string - // Write to disk - // unlock() - - // Write 10 x change font size - // Read-open-write font size - // Read-open-write font size - // Read-open-write font size - // Read-open-write font size - // Read-open-write font size - // .. - // Read from file system, only gets the latest font size and uselessly sets font size - - // `SettingsFile` - // You can non-blocking, write to it as much as you need - // Debounces your changes, waits for you to be done, and then flushes them all to the file system - // And blocks the read - - // Read and write to memory. ^ up from the file system - - // If there's pendings writes, we need to wait until this whole thing is done' - - cx.background() - .spawn(async move { - match write_theme_name(theme_name).await { - Ok(_) => {} - Err(_) => return, //TODO Pop toast - } - }) - .detach() } Err(error) => { log::error!("error loading theme {}: {}", mat.string, error) @@ -337,18 +266,3 @@ impl View for ThemeSelector { } } } - -async fn write_theme_name(theme_name: String) -> Result<()> { - let mut settings = read_to_string(SETTINGS.as_path()).await?; - settings = write_theme(settings, &theme_name); - - let mut file = smol::fs::OpenOptions::new() - .truncate(true) - .write(true) - .open(SETTINGS.as_path()) - .await?; - - file.write_all(settings.as_bytes()).await?; - - Ok(()) -} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d57a2c21b1..d0b41b08f1 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -40,7 +40,6 @@ journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } outline = { path = "../outline" } -paths = { path = "../paths" } plugin_runtime = { path = "../plugin_runtime" } project = { path = "../project" } project_panel = { path = "../project_panel" } @@ -62,6 +61,7 @@ async-trait = "0.1" backtrace = "0.3" chrono = "0.4" ctor = "0.1.20" +dirs = "3.0" easy-parallel = "3.1.0" env_logger = "0.9" futures = "0.3" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 519f04fe35..f48f8b723e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -53,7 +53,7 @@ fn main() { .map_or("dev".to_string(), |v| v.to_string()); init_panic_hook(app_version, http.clone(), app.background()); let db = app.background().spawn(async move { - project::Db::open(&*paths::DB) + project::Db::open(&*zed::paths::DB) .log_err() .unwrap_or_else(project::Db::null) }); @@ -90,7 +90,7 @@ fn main() { app.run(move |cx| { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); - languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); + languages.set_language_server_download_dir(zed::paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); let init_languages = cx .background() @@ -200,21 +200,23 @@ fn main() { } fn init_paths() { - fs::create_dir_all(&*paths::CONFIG_DIR).expect("could not create config path"); - fs::create_dir_all(&*paths::LANGUAGES_DIR).expect("could not create languages path"); - fs::create_dir_all(&*paths::DB_DIR).expect("could not create database path"); - fs::create_dir_all(&*paths::LOGS_DIR).expect("could not create logs path"); + fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); + fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); + fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); + fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); // Copy setting files from legacy locations. TODO: remove this after a few releases. thread::spawn(|| { - if fs::metadata(&*paths::legacy::SETTINGS).is_ok() - && fs::metadata(&*paths::SETTINGS).is_err() + if fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() + && fs::metadata(&*zed::paths::SETTINGS).is_err() { - fs::copy(&*paths::legacy::SETTINGS, &*paths::SETTINGS).log_err(); + fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); } - if fs::metadata(&*paths::legacy::KEYMAP).is_ok() && fs::metadata(&*paths::KEYMAP).is_err() { - fs::copy(&*paths::legacy::KEYMAP, &*paths::KEYMAP).log_err(); + if fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() + && fs::metadata(&*zed::paths::KEYMAP).is_err() + { + fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); } }); } @@ -229,14 +231,15 @@ fn init_logger() { const KIB: u64 = 1024; const MIB: u64 = 1024 * KIB; const MAX_LOG_BYTES: u64 = MIB; - if fs::metadata(&*paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) { - let _ = fs::rename(&*paths::LOG, &*paths::OLD_LOG); + if fs::metadata(&*zed::paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) + { + let _ = fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); } let log_file = OpenOptions::new() .create(true) .append(true) - .open(&*paths::LOG) + .open(&*zed::paths::LOG) .expect("could not open logfile"); simplelog::WriteLogger::init(level, simplelog::Config::default(), log_file) .expect("could not initialize logger"); @@ -248,7 +251,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A .spawn({ async move { let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL); - let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?; + let mut children = smol::fs::read_dir(&*zed::paths::LOGS_DIR).await?; while let Some(child) = children.next().await { let child = child?; let child_path = child.path(); @@ -336,7 +339,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A let panic_filename = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); fs::write( - paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), + zed::paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), &message, ) .context("error writing panic to disk") @@ -470,8 +473,8 @@ fn load_config_files( .clone() .spawn(async move { let settings_file = - WatchedJsonFile::new(fs.clone(), &executor, paths::SETTINGS.clone()).await; - let keymap_file = WatchedJsonFile::new(fs, &executor, paths::KEYMAP.clone()).await; + WatchedJsonFile::new(fs.clone(), &executor, zed::paths::SETTINGS.clone()).await; + let keymap_file = WatchedJsonFile::new(fs, &executor, zed::paths::KEYMAP.clone()).await; tx.send((settings_file, keymap_file)).ok() }) .detach(); diff --git a/crates/paths/src/paths.rs b/crates/zed/src/paths.rs similarity index 100% rename from crates/paths/src/paths.rs rename to crates/zed/src/paths.rs diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 01b549e438..6210242c51 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2,6 +2,7 @@ mod feedback; pub mod languages; pub mod menus; pub mod settings_file; +pub mod paths; #[cfg(any(test, feature = "test-support"))] pub mod test; From 0a8e2f6bb0720f57944e482930cc827785ad851d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 13:03:36 -0700 Subject: [PATCH 04/22] Moved fs to it's own crate, build failing due to cyclic dependency on rope --- Cargo.lock | 16 ++++++++ crates/fs/Cargo.toml | 18 +++++++++ crates/{project => fs}/src/fs.rs | 59 +++++++++++++++++++++++++++- crates/{git => fs}/src/repository.rs | 0 crates/text/Cargo.toml | 1 + crates/text/src/text.rs | 56 -------------------------- crates/zed/src/settings_file.rs | 4 ++ 7 files changed, 96 insertions(+), 58 deletions(-) create mode 100644 crates/fs/Cargo.toml rename crates/{project => fs}/src/fs.rs (96%) rename crates/{git => fs}/src/repository.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index cea25c027a..408758f7ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1984,6 +1984,22 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "fs" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "collections", + "fsevent", + "futures", + "git", + "parking_lot 0.11.2", + "smol", + "text", + "util", +] + [[package]] name = "fs-set-times" version = "0.15.0" diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml new file mode 100644 index 0000000000..1a4e325fa4 --- /dev/null +++ b/crates/fs/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fs" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/fs.rs" + +[dependencies] +collections = { path = "../collections" } +fsevent = { path = "../fsevent" } +anyhow = "1.0.57" +async-trait = "0.1" +futures = "0.3" +parking_lot = "0.11.1" +smol = "1.2.5" +text = { path = "../text" } +util = { path = "../util" } diff --git a/crates/project/src/fs.rs b/crates/fs/src/fs.rs similarity index 96% rename from crates/project/src/fs.rs rename to crates/fs/src/fs.rs index a9a0a1707f..c24387844c 100644 --- a/crates/project/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -1,8 +1,8 @@ +pub mod repository; + use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; -use git::repository::{GitRepository, LibGitRepository}; -use language::LineEnding; use parking_lot::Mutex as SyncMutex; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::sync::Arc; @@ -25,6 +25,61 @@ use git::repository::FakeGitRepositoryState; #[cfg(any(test, feature = "test-support"))] use std::sync::Weak; +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LineEnding { + Unix, + Windows, +} + +impl Default for LineEnding { + fn default() -> Self { + #[cfg(unix)] + return Self::Unix; + + #[cfg(not(unix))] + return Self::CRLF; + } +} + +impl LineEnding { + pub fn as_str(&self) -> &'static str { + match self { + LineEnding::Unix => "\n", + LineEnding::Windows => "\r\n", + } + } + + pub fn detect(text: &str) -> Self { + let mut max_ix = cmp::min(text.len(), 1000); + while !text.is_char_boundary(max_ix) { + max_ix -= 1; + } + + if let Some(ix) = text[..max_ix].find(&['\n']) { + if ix > 0 && text.as_bytes()[ix - 1] == b'\r' { + Self::Windows + } else { + Self::Unix + } + } else { + Self::default() + } + } + + pub fn normalize(text: &mut String) { + if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(text, "\n") { + *text = replaced; + } + } + + fn normalize_arc(text: Arc) -> Arc { + if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { + replaced.into() + } else { + text + } + } +} #[async_trait::async_trait] pub trait Fs: Send + Sync { async fn create_dir(&self, path: &Path) -> Result<()>; diff --git a/crates/git/src/repository.rs b/crates/fs/src/repository.rs similarity index 100% rename from crates/git/src/repository.rs rename to crates/fs/src/repository.rs diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 4fc09eff46..0d47525021 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -13,6 +13,7 @@ test-support = ["rand"] [dependencies] clock = { path = "../clock" } collections = { path = "../collections" } +fs = { path = "../fs" } sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" arrayvec = "0.7.1" diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index d201f87443..52859b7515 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -96,12 +96,6 @@ pub struct Transaction { pub start: clock::Global, } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LineEnding { - Unix, - Windows, -} - impl HistoryEntry { pub fn transaction_id(&self) -> TransactionId { self.transaction.id @@ -2370,56 +2364,6 @@ impl operation_queue::Operation for Operation { } } -impl Default for LineEnding { - fn default() -> Self { - #[cfg(unix)] - return Self::Unix; - - #[cfg(not(unix))] - return Self::CRLF; - } -} - -impl LineEnding { - pub fn as_str(&self) -> &'static str { - match self { - LineEnding::Unix => "\n", - LineEnding::Windows => "\r\n", - } - } - - pub fn detect(text: &str) -> Self { - let mut max_ix = cmp::min(text.len(), 1000); - while !text.is_char_boundary(max_ix) { - max_ix -= 1; - } - - if let Some(ix) = text[..max_ix].find(&['\n']) { - if ix > 0 && text.as_bytes()[ix - 1] == b'\r' { - Self::Windows - } else { - Self::Unix - } - } else { - Self::default() - } - } - - pub fn normalize(text: &mut String) { - if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(text, "\n") { - *text = replaced; - } - } - - fn normalize_arc(text: Arc) -> Arc { - if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { - replaced.into() - } else { - text - } - } -} - pub trait ToOffset { fn to_offset(&self, snapshot: &BufferSnapshot) -> usize; } diff --git a/crates/zed/src/settings_file.rs b/crates/zed/src/settings_file.rs index 14c9f63e95..25bd065b47 100644 --- a/crates/zed/src/settings_file.rs +++ b/crates/zed/src/settings_file.rs @@ -12,6 +12,10 @@ use util::ResultExt; #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); +// 1) Do the refactoring to pull WatchedJSON and fs out and into everything else +// 2) Scaffold this by making the basic structs we'll need SettingsFile::atomic_write_theme() +// 3) Fix the overeager settings writing, if that works, and there's no data loss, call it? + impl WatchedJsonFile where T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, From 0beb97547efe5bd9efb3005c72c575b184e90daa Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 15:25:54 -0700 Subject: [PATCH 05/22] Finished refactoring out fs and rope --- Cargo.lock | 44 +++++++++++++++++--- Cargo.toml | 1 + crates/collab/Cargo.toml | 3 +- crates/collab/src/integration_tests.rs | 10 ++--- crates/diagnostics/Cargo.toml | 1 + crates/diagnostics/src/diagnostics.rs | 7 ++-- crates/editor/Cargo.toml | 1 + crates/editor/src/display_map.rs | 9 +++-- crates/editor/src/display_map/block_map.rs | 7 ++-- crates/editor/src/display_map/fold_map.rs | 9 +++-- crates/editor/src/display_map/tab_map.rs | 22 +++++----- crates/editor/src/display_map/wrap_map.rs | 12 +++--- crates/editor/src/editor.rs | 5 ++- crates/editor/src/editor_tests.rs | 2 +- crates/editor/src/element.rs | 3 +- crates/editor/src/items.rs | 3 +- crates/editor/src/movement.rs | 5 ++- crates/editor/src/multi_buffer.rs | 16 ++++---- crates/editor/src/multi_buffer/anchor.rs | 2 +- crates/editor/src/selections_collection.rs | 3 +- crates/fs/Cargo.toml | 18 +++++++-- crates/fs/src/fs.rs | 47 +++++++++++++++++++--- crates/git/Cargo.toml | 3 +- crates/git/src/diff.rs | 3 +- crates/git/src/git.rs | 1 - crates/go_to_line/Cargo.toml | 1 + crates/go_to_line/src/go_to_line.rs | 3 +- crates/language/Cargo.toml | 2 + crates/language/src/buffer.rs | 10 ++--- crates/language/src/buffer_tests.rs | 4 +- crates/language/src/diagnostic_set.rs | 3 +- crates/language/src/language.rs | 1 + crates/language/src/proto.rs | 14 +++---- crates/language/src/syntax_map.rs | 5 ++- crates/project/Cargo.toml | 4 +- crates/project/src/lsp_command.rs | 3 +- crates/project/src/project.rs | 34 ++-------------- crates/project/src/project_tests.rs | 6 ++- crates/project/src/worktree.rs | 17 ++++---- crates/rope/Cargo.toml | 20 +++++++++ crates/{text => rope}/src/offset_utf16.rs | 0 crates/{text => rope}/src/point.rs | 0 crates/{text => rope}/src/point_utf16.rs | 0 crates/{text => rope}/src/rope.rs | 11 +++-- crates/text/Cargo.toml | 8 ++-- crates/text/src/anchor.rs | 5 ++- crates/text/src/random_char_iter.rs | 36 ----------------- crates/text/src/selection.rs | 6 ++- crates/text/src/text.rs | 26 +++--------- crates/util/Cargo.toml | 5 +-- crates/util/src/lib.rs | 36 +++++++++++++++++ crates/vim/Cargo.toml | 1 + crates/vim/src/normal.rs | 3 +- crates/workspace/Cargo.toml | 1 + crates/workspace/src/workspace.rs | 3 +- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 40 +++++++++--------- crates/zed/src/zed.rs | 5 ++- 58 files changed, 328 insertions(+), 223 deletions(-) create mode 100644 crates/rope/Cargo.toml rename crates/{text => rope}/src/offset_utf16.rs (100%) rename crates/{text => rope}/src/point.rs (100%) rename crates/{text => rope}/src/point_utf16.rs (100%) rename crates/{text => rope}/src/rope.rs (99%) delete mode 100644 crates/text/src/random_char_iter.rs diff --git a/Cargo.lock b/Cargo.lock index 408758f7ad..ebf2d4376c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,6 +1045,7 @@ dependencies = [ "editor", "env_logger", "envy", + "fs", "futures", "git", "gpui", @@ -1060,6 +1061,7 @@ dependencies = [ "prometheus", "rand 0.8.5", "reqwest", + "rope", "rpc", "scrypt", "serde", @@ -1551,6 +1553,7 @@ dependencies = [ "language", "postage", "project", + "rope", "serde_json", "settings", "smallvec", @@ -1704,6 +1707,7 @@ dependencies = [ "postage", "project", "rand 0.8.5", + "rope", "rpc", "serde", "settings", @@ -1993,10 +1997,18 @@ dependencies = [ "collections", "fsevent", "futures", - "git", + "git2", + "gpui", + "lazy_static", + "libc", + "log", + "lsp", "parking_lot 0.11.2", + "regex", + "rope", + "serde", + "serde_json", "smol", - "text", "util", ] @@ -2250,6 +2262,7 @@ dependencies = [ "lazy_static", "log", "parking_lot 0.11.2", + "rope", "smol", "sum_tree", "text", @@ -2297,6 +2310,7 @@ dependencies = [ "gpui", "menu", "postage", + "rope", "settings", "text", "workspace", @@ -2884,6 +2898,7 @@ dependencies = [ "collections", "ctor", "env_logger", + "fs", "futures", "fuzzy", "git", @@ -2895,6 +2910,7 @@ dependencies = [ "postage", "rand 0.8.5", "regex", + "rope", "rpc", "serde", "serde_json", @@ -4028,6 +4044,7 @@ dependencies = [ "clock", "collections", "db", + "fs", "fsevent", "futures", "fuzzy", @@ -4036,7 +4053,6 @@ dependencies = [ "ignore", "language", "lazy_static", - "libc", "log", "lsp", "parking_lot 0.11.2", @@ -4045,6 +4061,7 @@ dependencies = [ "rand 0.8.5", "regex", "rocksdb", + "rope", "rpc", "serde", "serde_json", @@ -4531,6 +4548,20 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rope" +version = "0.1.0" +dependencies = [ + "arrayvec 0.7.2", + "bromberg_sl2", + "gpui", + "log", + "rand 0.8.5", + "smallvec", + "sum_tree", + "util", +] + [[package]] name = "roxmltree" version = "0.14.1" @@ -5588,13 +5619,12 @@ name = "text" version = "0.1.0" dependencies = [ "anyhow", - "arrayvec 0.7.2", - "bromberg_sl2", "clock", "collections", "ctor", "digest 0.9.0", "env_logger", + "fs", "gpui", "lazy_static", "log", @@ -5602,6 +5632,7 @@ dependencies = [ "postage", "rand 0.8.5", "regex", + "rope", "smallvec", "sum_tree", "util", @@ -6504,6 +6535,7 @@ dependencies = [ "language", "log", "project", + "rope", "search", "serde", "settings", @@ -7192,6 +7224,7 @@ dependencies = [ "collections", "context_menu", "drag_and_drop", + "fs", "futures", "gpui", "language", @@ -7275,6 +7308,7 @@ dependencies = [ "editor", "env_logger", "file_finder", + "fs", "fsevent", "futures", "fuzzy", diff --git a/Cargo.toml b/Cargo.toml index b64df300c0..8d2a3fcc40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ resolver = "2" [workspace.dependencies] serde = { version = "1.0", features = ["derive", "rc"] } serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] } +rand = { version = "0.8" } [patch.crates-io] tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "366210ae925d7ea0891bc7a0c738f60c77c04d7b" } diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 840199c2bb..cf6b7f8b68 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -16,7 +16,8 @@ required-features = ["seed-support"] collections = { path = "../collections" } rpc = { path = "../rpc" } util = { path = "../util" } - +fs = { path = "../fs" } +rope = { path = "../rope" } anyhow = "1.0.40" async-trait = "0.1.50" async-tungstenite = "0.16" diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index bdd240fda8..a0a6f5feea 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -15,6 +15,7 @@ use editor::{ self, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename, ToOffset, ToggleCodeActions, Undo, }; +use fs::{FakeFs, Fs as _, LineEnding}; use futures::{channel::mpsc, Future, StreamExt as _}; use gpui::{ executor::{self, Deterministic}, @@ -24,17 +25,16 @@ use gpui::{ }; use language::{ range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language, - LanguageConfig, LanguageRegistry, LineEnding, OffsetRangeExt, Point, Rope, + LanguageConfig, LanguageRegistry, OffsetRangeExt, Rope, }; use lsp::{self, FakeLanguageServer}; use parking_lot::Mutex; use project::{ - fs::{FakeFs, Fs as _}, - search::SearchQuery, - worktree::WorktreeHandle, - DiagnosticSummary, Project, ProjectPath, ProjectStore, WorktreeId, + search::SearchQuery, worktree::WorktreeHandle, DiagnosticSummary, Project, ProjectPath, + ProjectStore, WorktreeId, }; use rand::prelude::*; +use rope::point::Point; use rpc::PeerId; use serde_json::json; use settings::{Formatter, Settings}; diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 616f69117f..c4b851917e 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -15,6 +15,7 @@ editor = { path = "../editor" } language = { path = "../language" } gpui = { path = "../gpui" } project = { path = "../project" } +rope = { path = "../rope" } settings = { path = "../settings" } theme = { path = "../theme" } util = { path = "../util" } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 3111d7a9f1..608b333d0d 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -14,10 +14,10 @@ use gpui::{ ViewHandle, WeakViewHandle, }; use language::{ - Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, - SelectionGoal, + Anchor, Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Selection, SelectionGoal, }; use project::{DiagnosticSummary, Project, ProjectPath}; +use rope::point::Point; use serde_json::json; use settings::Settings; use smallvec::SmallVec; @@ -738,7 +738,8 @@ mod tests { DisplayPoint, }; use gpui::TestAppContext; - use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16}; + use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity}; + use rope::point_utf16::PointUtf16; use serde_json::json; use unindent::Unindent as _; use workspace::AppState; diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index db634376d0..4e0a10b70d 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -30,6 +30,7 @@ gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } project = { path = "../project" } +rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } snippet = { path = "../snippet" } diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 58fc2e4fe7..525af0d599 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -11,7 +11,8 @@ use gpui::{ fonts::{FontId, HighlightStyle}, Entity, ModelContext, ModelHandle, }; -use language::{OffsetUtf16, Point, Subscription as BufferSubscription}; +use language::Subscription as BufferSubscription; +use rope::{offset_utf16::OffsetUtf16, point::Point}; use settings::Settings; use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc}; use sum_tree::{Bias, TreeMap}; @@ -565,7 +566,7 @@ pub mod tests { use super::*; use crate::{movement, test::marked_display_snapshot}; use gpui::{color::Color, elements::*, test::observe, MutableAppContext}; - use language::{Buffer, Language, LanguageConfig, RandomCharIter, SelectionGoal}; + use language::{Buffer, Language, LanguageConfig, SelectionGoal}; use rand::{prelude::*, Rng}; use smol::stream::StreamExt; use std::{env, sync::Arc}; @@ -609,7 +610,9 @@ pub mod tests { let buffer = cx.update(|cx| { if rng.gen() { let len = rng.gen_range(0..10); - let text = RandomCharIter::new(&mut rng).take(len).collect::(); + let text = util::RandomCharIter::new(&mut rng) + .take(len) + .collect::(); MultiBuffer::build_simple(&text, cx) } else { MultiBuffer::build_random(&mut rng, cx) diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 210daccac2..f0f2720f1a 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -7,6 +7,7 @@ use collections::{Bound, HashMap, HashSet}; use gpui::{ElementBox, RenderContext}; use language::{BufferSnapshot, Chunk, Patch}; use parking_lot::Mutex; +use rope::point::Point; use std::{ cell::RefCell, cmp::{self, Ordering}, @@ -18,7 +19,7 @@ use std::{ }, }; use sum_tree::{Bias, SumTree}; -use text::{Edit, Point}; +use text::Edit; const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize]; @@ -42,7 +43,7 @@ pub struct BlockSnapshot { pub struct BlockId(usize); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct BlockPoint(pub super::Point); +pub struct BlockPoint(pub Point); #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] struct BlockRow(u32); @@ -994,7 +995,7 @@ mod tests { use rand::prelude::*; use settings::Settings; use std::env; - use text::RandomCharIter; + use util::RandomCharIter; #[gpui::test] fn test_offset_for_row() { diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index c17cfa39f2..5bd1670542 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -5,8 +5,9 @@ use crate::{ }; use collections::BTreeMap; use gpui::fonts::HighlightStyle; -use language::{Chunk, Edit, Point, TextSummary}; +use language::{Chunk, Edit, TextSummary}; use parking_lot::Mutex; +use rope::point::Point; use std::{ any::TypeId, cmp::{self, Ordering}, @@ -18,11 +19,11 @@ use std::{ use sum_tree::{Bias, Cursor, FilterCursor, SumTree}; #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct FoldPoint(pub super::Point); +pub struct FoldPoint(pub Point); impl FoldPoint { pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) + Self(Point::new(row, column)) } pub fn row(self) -> u32 { @@ -1196,8 +1197,8 @@ mod tests { use settings::Settings; use std::{cmp::Reverse, env, mem, sync::Arc}; use sum_tree::TreeMap; - use text::RandomCharIter; use util::test::sample_text; + use util::RandomCharIter; use Bias::{Left, Right}; #[gpui::test] diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index 4d89767a19..b7d8fac770 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -3,11 +3,12 @@ use super::{ TextHighlights, }; use crate::MultiBufferSnapshot; -use language::{rope, Chunk}; +use language::Chunk; use parking_lot::Mutex; +use rope; +use rope::point::Point; use std::{cmp, mem, num::NonZeroU32, ops::Range}; use sum_tree::Bias; -use text::Point; pub struct TabMap(Mutex); @@ -332,11 +333,11 @@ impl TabSnapshot { } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct TabPoint(pub super::Point); +pub struct TabPoint(pub Point); impl TabPoint { pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) + Self(Point::new(row, column)) } pub fn zero() -> Self { @@ -352,8 +353,8 @@ impl TabPoint { } } -impl From for TabPoint { - fn from(point: super::Point) -> Self { +impl From for TabPoint { + fn from(point: Point) -> Self { Self(point) } } @@ -362,7 +363,7 @@ pub type TabEdit = text::Edit; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct TextSummary { - pub lines: super::Point, + pub lines: Point, pub first_line_chars: u32, pub last_line_chars: u32, pub longest_row: u32, @@ -485,7 +486,6 @@ mod tests { use super::*; use crate::{display_map::fold_map::FoldMap, MultiBuffer}; use rand::{prelude::StdRng, Rng}; - use text::{RandomCharIter, Rope}; #[test] fn test_expand_tabs() { @@ -508,7 +508,9 @@ mod tests { let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap(); let len = rng.gen_range(0..30); let buffer = if rng.gen() { - let text = RandomCharIter::new(&mut rng).take(len).collect::(); + let text = util::RandomCharIter::new(&mut rng) + .take(len) + .collect::(); MultiBuffer::build_simple(&text, cx) } else { MultiBuffer::build_random(&mut rng, cx) @@ -522,7 +524,7 @@ mod tests { log::info!("FoldMap text: {:?}", folds_snapshot.text()); let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size); - let text = Rope::from(tabs_snapshot.text().as_str()); + let text = rope::Rope::from(tabs_snapshot.text().as_str()); log::info!( "TabMap text (tab size: {}): {:?}", tab_size, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index ee6ce2860d..42156b905f 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -3,13 +3,14 @@ use super::{ tab_map::{self, TabEdit, TabPoint, TabSnapshot}, TextHighlights, }; -use crate::{MultiBufferSnapshot, Point}; +use crate::MultiBufferSnapshot; use gpui::{ fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext, Task, }; use language::Chunk; use lazy_static::lazy_static; +use rope::point::Point; use smol::future::yield_now; use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration}; use sum_tree::{Bias, Cursor, SumTree}; @@ -52,7 +53,7 @@ struct TransformSummary { } #[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)] -pub struct WrapPoint(pub super::Point); +pub struct WrapPoint(pub Point); pub struct WrapChunks<'a> { input_chunks: tab_map::TabChunks<'a>, @@ -959,7 +960,7 @@ impl SumTreeExt for SumTree { impl WrapPoint { pub fn new(row: u32, column: u32) -> Self { - Self(super::Point::new(row, column)) + Self(Point::new(row, column)) } pub fn row(self) -> u32 { @@ -1029,7 +1030,6 @@ mod tests { MultiBuffer, }; use gpui::test::observe; - use language::RandomCharIter; use rand::prelude::*; use settings::Settings; use smol::stream::StreamExt; @@ -1067,7 +1067,9 @@ mod tests { MultiBuffer::build_random(&mut rng, cx) } else { let len = rng.gen_range(0..10); - let text = RandomCharIter::new(&mut rng).take(len).collect::(); + let text = util::RandomCharIter::new(&mut rng) + .take(len) + .collect::(); MultiBuffer::build_simple(&text, cx) } }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d1dfed3c85..5f424cd20b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -43,8 +43,8 @@ pub use items::MAX_TAB_TITLE_LEN; pub use language::{char_kind, CharKind}; use language::{ AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, - DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, - Selection, SelectionGoal, TransactionId, + DiagnosticSeverity, IndentKind, IndentSize, Language, OffsetRangeExt, Selection, SelectionGoal, + TransactionId, }; use link_go_to_definition::{hide_link_definition, LinkGoToDefinitionState}; pub use multi_buffer::{ @@ -54,6 +54,7 @@ pub use multi_buffer::{ use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use ordered_float::OrderedFloat; use project::{FormatTrigger, LocationLink, Project, ProjectPath, ProjectTransaction}; +use rope::{offset_utf16::OffsetUtf16, point::Point}; use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection}; use serde::{Deserialize, Serialize}; use settings::Settings; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 063dcf6098..e152c85e4e 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -11,9 +11,9 @@ use gpui::{ use indoc::indoc; use language::{FakeLspAdapter, LanguageConfig, LanguageRegistry}; use project::FakeFs; +use rope::point::Point; use settings::EditorSettings; use std::{cell::RefCell, rc::Rc, time::Instant}; -use text::Point; use unindent::Unindent; use util::{ assert_set_eq, diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index a3b5ad43f6..114a1d72c9 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -35,8 +35,9 @@ use gpui::{ WeakViewHandle, }; use json::json; -use language::{Bias, DiagnosticSeverity, OffsetUtf16, Selection}; +use language::{Bias, DiagnosticSeverity, Selection}; use project::ProjectPath; +use rope::offset_utf16::OffsetUtf16; use settings::{GitGutter, Settings}; use smallvec::SmallVec; use std::{ diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index c1082020e5..727ee1f094 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -11,6 +11,7 @@ use gpui::{ }; use language::{Bias, Buffer, File as _, OffsetRangeExt, SelectionGoal}; use project::{File, FormatTrigger, Project, ProjectEntryId, ProjectPath}; +use rope::point::Point; use rpc::proto::{self, update_view}; use settings::Settings; use smallvec::SmallVec; @@ -21,7 +22,7 @@ use std::{ ops::Range, path::{Path, PathBuf}, }; -use text::{Point, Selection}; +use text::Selection; use util::TryFutureExt; use workspace::{ searchable::{Direction, SearchEvent, SearchableItem, SearchableItemHandle}, diff --git a/crates/editor/src/movement.rs b/crates/editor/src/movement.rs index 0db5cc0812..79d041fbab 100644 --- a/crates/editor/src/movement.rs +++ b/crates/editor/src/movement.rs @@ -1,6 +1,7 @@ +use rope::point::Point; + use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use crate::{char_kind, CharKind, ToPoint}; -use language::Point; use std::ops::Range; pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint { @@ -273,7 +274,7 @@ pub fn surrounding_word(map: &DisplaySnapshot, position: DisplayPoint) -> Range< mod tests { use super::*; use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer}; - use language::Point; + use rope::point::Point; use settings::Settings; #[gpui::test] diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index a0eedb850c..23ee7df657 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -12,6 +12,7 @@ use language::{ DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, Outline, OutlineItem, Selection, ToOffset as _, ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, }; +use rope::{offset_utf16::OffsetUtf16, point::Point, point_utf16::PointUtf16, TextDimension}; use smallvec::SmallVec; use std::{ borrow::Cow, @@ -27,9 +28,8 @@ use std::{ use sum_tree::{Bias, Cursor, SumTree}; use text::{ locator::Locator, - rope::TextDimension, subscription::{Subscription, Topic}, - Edit, OffsetUtf16, Point, PointUtf16, TextSummary, + Edit, TextSummary, }; use theme::SyntaxTheme; use util::post_inc; @@ -168,7 +168,7 @@ struct ExcerptChunks<'a> { } struct ExcerptBytes<'a> { - content_bytes: language::rope::Bytes<'a>, + content_bytes: rope::Bytes<'a>, footer_height: usize, } @@ -1412,7 +1412,7 @@ impl MultiBuffer { edit_count: usize, cx: &mut ModelContext, ) { - use text::RandomCharIter; + use util::RandomCharIter; let snapshot = self.read(cx); let mut edits: Vec<(Range, Arc)> = Vec::new(); @@ -1451,7 +1451,7 @@ impl MultiBuffer { ) { use rand::prelude::*; use std::env; - use text::RandomCharIter; + use util::RandomCharIter; let max_excerpts = env::var("MAX_EXCERPTS") .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable")) @@ -3337,7 +3337,7 @@ mod tests { use rand::prelude::*; use settings::Settings; use std::{env, rc::Rc}; - use text::{Point, RandomCharIter}; + use util::test::sample_text; #[gpui::test] @@ -3955,7 +3955,9 @@ mod tests { } _ => { let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) { - let base_text = RandomCharIter::new(&mut rng).take(10).collect::(); + let base_text = util::RandomCharIter::new(&mut rng) + .take(10) + .collect::(); buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx))); buffers.last().unwrap() } else { diff --git a/crates/editor/src/multi_buffer/anchor.rs b/crates/editor/src/multi_buffer/anchor.rs index cb8a1692b9..b30e4b5780 100644 --- a/crates/editor/src/multi_buffer/anchor.rs +++ b/crates/editor/src/multi_buffer/anchor.rs @@ -1,10 +1,10 @@ use super::{ExcerptId, MultiBufferSnapshot, ToOffset, ToOffsetUtf16, ToPoint}; +use rope::{offset_utf16::OffsetUtf16, point::Point, TextDimension}; use std::{ cmp::Ordering, ops::{Range, Sub}, }; use sum_tree::Bias; -use text::{rope::TextDimension, OffsetUtf16, Point}; #[derive(Clone, Eq, PartialEq, Debug, Hash)] pub struct Anchor { diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 9d6450f8ec..ed983d22d9 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -8,7 +8,8 @@ use std::{ use collections::HashMap; use gpui::{AppContext, ModelHandle, MutableAppContext}; use itertools::Itertools; -use language::{rope::TextDimension, Bias, Point, Selection, SelectionGoal, ToPoint}; +use language::{Bias, Selection, SelectionGoal, ToPoint}; +use rope::{point::Point, TextDimension}; use util::post_inc; use crate::{ diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 1a4e325fa4..182d13894d 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -8,11 +8,23 @@ path = "src/fs.rs" [dependencies] collections = { path = "../collections" } -fsevent = { path = "../fsevent" } +gpui = { path = "../gpui" } +lsp = { path = "../lsp" } +rope = { path = "../rope" } +util = { path = "../util" } anyhow = "1.0.57" async-trait = "0.1" futures = "0.3" +fsevent = { path = "../fsevent" } +lazy_static = "1.4.0" parking_lot = "0.11.1" smol = "1.2.5" -text = { path = "../text" } -util = { path = "../util" } +regex = "1.5" +git2 = { version = "0.15", default-features = false } +serde = { workspace = true } +serde_json = { workspace = true } +log = { version = "0.4.16", features = ["kv_unstable_serde"] } +libc = "0.2" + +[features] +test-support = [] diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index c24387844c..26045f2776 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -3,8 +3,15 @@ pub mod repository; use anyhow::{anyhow, Result}; use fsevent::EventStream; use futures::{future::BoxFuture, Stream, StreamExt}; +use git2::Repository as LibGitRepository; +use lazy_static::lazy_static; use parking_lot::Mutex as SyncMutex; +use regex::Regex; +use repository::GitRepository; +use rope::Rope; use smol::io::{AsyncReadExt, AsyncWriteExt}; +use std::borrow::Cow; +use std::cmp; use std::sync::Arc; use std::{ io, @@ -13,7 +20,6 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; -use text::Rope; use util::ResultExt; #[cfg(any(test, feature = "test-support"))] @@ -21,10 +27,14 @@ use collections::{btree_map, BTreeMap}; #[cfg(any(test, feature = "test-support"))] use futures::lock::Mutex; #[cfg(any(test, feature = "test-support"))] -use git::repository::FakeGitRepositoryState; +use repository::FakeGitRepositoryState; #[cfg(any(test, feature = "test-support"))] use std::sync::Weak; +lazy_static! { + static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); +} + #[derive(Clone, Copy, Debug, PartialEq)] pub enum LineEnding { Unix, @@ -72,7 +82,7 @@ impl LineEnding { } } - fn normalize_arc(text: Arc) -> Arc { + pub fn normalize_arc(text: Arc) -> Arc { if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { replaced.into() } else { @@ -141,6 +151,33 @@ pub struct Metadata { pub is_dir: bool, } +impl From for CreateOptions { + fn from(options: lsp::CreateFileOptions) -> Self { + Self { + overwrite: options.overwrite.unwrap_or(false), + ignore_if_exists: options.ignore_if_exists.unwrap_or(false), + } + } +} + +impl From for RenameOptions { + fn from(options: lsp::RenameFileOptions) -> Self { + Self { + overwrite: options.overwrite.unwrap_or(false), + ignore_if_exists: options.ignore_if_exists.unwrap_or(false), + } + } +} + +impl From for RemoveOptions { + fn from(options: lsp::DeleteFileOptions) -> Self { + Self { + recursive: options.recursive.unwrap_or(false), + ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), + } + } +} + pub struct RealFs; #[async_trait::async_trait] @@ -340,7 +377,7 @@ enum FakeFsEntry { inode: u64, mtime: SystemTime, entries: BTreeMap>>, - git_repo_state: Option>>, + git_repo_state: Option>>, }, Symlink { target: PathBuf, @@ -952,7 +989,7 @@ impl Fs for FakeFs { Arc::new(SyncMutex::new(FakeGitRepositoryState::default())) }) .clone(); - Some(git::repository::FakeGitRepository::open(state)) + Some(repository::FakeGitRepository::open(state)) } else { None } diff --git a/crates/git/Cargo.toml b/crates/git/Cargo.toml index b8f3aac0b9..1d15c2b123 100644 --- a/crates/git/Cargo.toml +++ b/crates/git/Cargo.toml @@ -9,7 +9,7 @@ path = "src/git.rs" [dependencies] anyhow = "1.0.38" clock = { path = "../clock" } -git2 = { version = "0.15", default-features = false } +rope = { path = "../rope" } lazy_static = "1.4.0" sum_tree = { path = "../sum_tree" } text = { path = "../text" } @@ -20,6 +20,7 @@ smol = "1.2" parking_lot = "0.11.1" async-trait = "0.1" futures = "0.3" +git2 = { version = "0.15", default-features = false } [dev-dependencies] unindent = "0.1.7" diff --git a/crates/git/src/diff.rs b/crates/git/src/diff.rs index 4191e5d260..7f3f6101ce 100644 --- a/crates/git/src/diff.rs +++ b/crates/git/src/diff.rs @@ -1,7 +1,8 @@ use std::ops::Range; +use rope::point::Point; use sum_tree::SumTree; -use text::{Anchor, BufferSnapshot, OffsetRangeExt, Point}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt}; pub use git2 as libgit; use libgit::{DiffLineType as GitDiffLineType, DiffOptions as GitOptions, Patch as GitPatch}; diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs index 36f54e706a..b1b885eca2 100644 --- a/crates/git/src/git.rs +++ b/crates/git/src/git.rs @@ -4,7 +4,6 @@ pub use git2 as libgit; pub use lazy_static::lazy_static; pub mod diff; -pub mod repository; lazy_static! { pub static ref DOT_GIT: &'static OsStr = OsStr::new(".git"); diff --git a/crates/go_to_line/Cargo.toml b/crates/go_to_line/Cargo.toml index 93ae96f93e..d69b1f239c 100644 --- a/crates/go_to_line/Cargo.toml +++ b/crates/go_to_line/Cargo.toml @@ -13,5 +13,6 @@ gpui = { path = "../gpui" } menu = { path = "../menu" } settings = { path = "../settings" } text = { path = "../text" } +rope = { path = "../rope" } workspace = { path = "../workspace" } postage = { version = "0.4", features = ["futures-traits"] } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index 3ca50cee42..51ff87a943 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -4,8 +4,9 @@ use gpui::{ MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use menu::{Cancel, Confirm}; +use rope::point::Point; use settings::Settings; -use text::{Bias, Point}; +use text::Bias; use workspace::Workspace; actions!(go_to_line, [Toggle]); diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index e5b6f4c79b..54d2147929 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -25,9 +25,11 @@ client = { path = "../client" } clock = { path = "../clock" } collections = { path = "../collections" } fuzzy = { path = "../fuzzy" } +fs = { path = "../fs" } git = { path = "../git" } gpui = { path = "../gpui" } lsp = { path = "../lsp" } +rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index a3c0c54d01..56aad18ead 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -13,9 +13,11 @@ use crate::{ }; use anyhow::{anyhow, Result}; use clock::ReplicaId; +use fs::LineEnding; use futures::FutureExt as _; use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task}; use parking_lot::Mutex; +use rope::point::Point; use settings::Settings; use similar::{ChangeTag, TextDiff}; use smol::future::yield_now; @@ -38,7 +40,7 @@ use sum_tree::TreeMap; use text::operation_queue::OperationQueue; pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Operation as _, *}; use theme::SyntaxTheme; -use util::TryFutureExt as _; +use util::{RandomCharIter, TryFutureExt as _}; #[cfg(any(test, feature = "test-support"))] pub use {tree_sitter_rust, tree_sitter_typescript}; @@ -368,7 +370,7 @@ impl Buffer { file, ); this.text.set_line_ending(proto::deserialize_line_ending( - proto::LineEnding::from_i32(message.line_ending) + rpc::proto::LineEnding::from_i32(message.line_ending) .ok_or_else(|| anyhow!("missing line_ending"))?, )); Ok(this) @@ -1633,9 +1635,7 @@ impl Buffer { last_end = Some(range.end); let new_text_len = rng.gen_range(0..10); - let new_text: String = crate::random_char_iter::RandomCharIter::new(&mut *rng) - .take(new_text_len) - .collect(); + let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); edits.push((range, new_text)); } diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 3cfddce71f..313c843b02 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1,9 +1,11 @@ use super::*; use clock::ReplicaId; use collections::BTreeMap; +use fs::LineEnding; use gpui::{ModelHandle, MutableAppContext}; use proto::deserialize_operation; use rand::prelude::*; +use rope::point::Point; use settings::Settings; use std::{ cell::RefCell, @@ -14,7 +16,7 @@ use std::{ }; use text::network::Network; use unindent::Unindent as _; -use util::{post_inc, test::marked_text_ranges}; +use util::{post_inc, test::marked_text_ranges, RandomCharIter}; #[cfg(test)] #[ctor::ctor] diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index b52327cac0..dfbc32149c 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -1,12 +1,13 @@ use crate::Diagnostic; use collections::HashMap; +use rope::point_utf16::PointUtf16; use std::{ cmp::{Ordering, Reverse}, iter, ops::Range, }; use sum_tree::{self, Bias, SumTree}; -use text::{Anchor, FromAnchor, PointUtf16, ToOffset}; +use text::{Anchor, FromAnchor, ToOffset}; #[derive(Clone, Debug, Default)] pub struct DiagnosticSet { diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index bb75edbc32..4f8615606c 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -22,6 +22,7 @@ use lazy_static::lazy_static; use parking_lot::{Mutex, RwLock}; use postage::watch; use regex::Regex; +use rope::point_utf16::PointUtf16; use serde::{de, Deserialize, Deserializer}; use serde_json::Value; use std::{ diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index fddfb7961f..9e3ee7d46b 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -8,19 +8,19 @@ use rpc::proto; use std::{ops::Range, sync::Arc}; use text::*; -pub use proto::{BufferState, LineEnding, Operation, SelectionSet}; +pub use proto::{BufferState, Operation, SelectionSet}; -pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding { +pub fn deserialize_line_ending(message: proto::LineEnding) -> fs::LineEnding { match message { - LineEnding::Unix => text::LineEnding::Unix, - LineEnding::Windows => text::LineEnding::Windows, + proto::LineEnding::Unix => fs::LineEnding::Unix, + proto::LineEnding::Windows => fs::LineEnding::Windows, } } -pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding { +pub fn serialize_line_ending(message: fs::LineEnding) -> proto::LineEnding { match message { - text::LineEnding::Unix => proto::LineEnding::Unix, - text::LineEnding::Windows => proto::LineEnding::Windows, + fs::LineEnding::Unix => proto::LineEnding::Unix, + fs::LineEnding::Windows => proto::LineEnding::Windows, } } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 64145e535b..3992d41081 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1,6 +1,7 @@ use crate::{Grammar, InjectionConfig, Language, LanguageRegistry}; use lazy_static::lazy_static; use parking_lot::Mutex; +use rope::point::Point; use std::{ borrow::Cow, cell::RefCell, @@ -10,7 +11,7 @@ use std::{ sync::Arc, }; use sum_tree::{Bias, SeekTarget, SumTree}; -use text::{rope, Anchor, BufferSnapshot, OffsetRangeExt, Point, Rope, ToOffset, ToPoint}; +use text::{Anchor, BufferSnapshot, OffsetRangeExt, Rope, ToOffset, ToPoint}; use tree_sitter::{ Node, Parser, Query, QueryCapture, QueryCaptures, QueryCursor, QueryMatches, Tree, }; @@ -1242,7 +1243,7 @@ mod tests { use crate::LanguageConfig; use rand::rngs::StdRng; use std::env; - use text::{Buffer, Point}; + use text::Buffer; use unindent::Unindent as _; use util::test::marked_text_ranges; diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 1e45e3c6ed..7a41318b86 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -22,12 +22,14 @@ client = { path = "../client" } clock = { path = "../clock" } collections = { path = "../collections" } db = { path = "../db" } +fs = { path = "../fs" } fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } git = { path = "../git" } gpui = { path = "../gpui" } language = { path = "../language" } lsp = { path = "../lsp" } +rope = { path = "../rope" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } @@ -38,7 +40,6 @@ async-trait = "0.1" futures = "0.3" ignore = "0.4" lazy_static = "1.4.0" -libc = "0.2" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11.1" postage = { version = "0.4.1", features = ["futures-traits"] } @@ -58,6 +59,7 @@ rocksdb = "0.18" client = { path = "../client", features = ["test-support"] } collections = { path = "../collections", features = ["test-support"] } db = { path = "../db", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } language = { path = "../language", features = ["test-support"] } lsp = { path = "../lsp", features = ["test-support"] } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 37f6e76340..42098d2e8b 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -8,10 +8,11 @@ use gpui::{AppContext, AsyncAppContext, ModelHandle}; use language::{ point_from_lsp, point_to_lsp, proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version}, - range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, PointUtf16, ToPointUtf16, + range_from_lsp, Anchor, Bias, Buffer, CachedLspAdapter, ToPointUtf16, }; use lsp::{DocumentHighlightKind, LanguageServer, ServerCapabilities}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; +use rope::point_utf16::PointUtf16; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; #[async_trait(?Send)] diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 84f45070fd..11792bcf1e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1,4 +1,3 @@ -pub mod fs; mod ignore; mod lsp_command; pub mod search; @@ -25,9 +24,8 @@ use language::{ }, range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, - File as _, Language, LanguageRegistry, LanguageServerName, LineEnding, LocalFile, - OffsetRangeExt, Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, - Transaction, + File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, + Operation, Patch, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, }; use lsp::{ DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString, @@ -37,6 +35,7 @@ use lsp_command::*; use parking_lot::Mutex; use postage::watch; use rand::prelude::*; +use rope::point_utf16::PointUtf16; use search::SearchQuery; use serde::Serialize; use settings::{FormatOnSave, Formatter, Settings}; @@ -6019,33 +6018,6 @@ impl> From<(WorktreeId, P)> for ProjectPath { } } -impl From for fs::CreateOptions { - fn from(options: lsp::CreateFileOptions) -> Self { - Self { - overwrite: options.overwrite.unwrap_or(false), - ignore_if_exists: options.ignore_if_exists.unwrap_or(false), - } - } -} - -impl From for fs::RenameOptions { - fn from(options: lsp::RenameFileOptions) -> Self { - Self { - overwrite: options.overwrite.unwrap_or(false), - ignore_if_exists: options.ignore_if_exists.unwrap_or(false), - } - } -} - -impl From for fs::RemoveOptions { - fn from(options: lsp::DeleteFileOptions) -> Self { - Self { - recursive: options.recursive.unwrap_or(false), - ignore_if_not_exists: options.ignore_if_not_exists.unwrap_or(false), - } - } -} - fn serialize_symbol(symbol: &Symbol) -> proto::Symbol { proto::Symbol { language_server_name: symbol.language_server_name.0.to_string(), diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index ceb5d033a7..12da0a75db 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -1,12 +1,14 @@ use crate::{worktree::WorktreeHandle, Event, *}; -use fs::RealFs; +use fs::LineEnding; +use fs::{FakeFs, RealFs}; use futures::{future, StreamExt}; use gpui::{executor::Deterministic, test::subscribe}; use language::{ tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig, - LineEnding, OffsetRangeExt, Point, ToPoint, + OffsetRangeExt, ToPoint, }; use lsp::Url; +use rope::point::Point; use serde_json::json; use std::{cell::RefCell, os::unix, rc::Rc, task::Poll}; use unindent::Unindent as _; diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 968c2d4bc7..3073392a52 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -1,14 +1,12 @@ -use super::{ - fs::{self, Fs}, - ignore::IgnoreStack, - DiagnosticSummary, -}; +use super::{ignore::IgnoreStack, DiagnosticSummary}; use crate::{copy_recursive, ProjectEntryId, RemoveOptions}; use ::ignore::gitignore::{Gitignore, GitignoreBuilder}; use anyhow::{anyhow, Context, Result}; use client::{proto, Client}; use clock::ReplicaId; use collections::{HashMap, VecDeque}; +use fs::LineEnding; +use fs::{repository::GitRepository, Fs}; use futures::{ channel::{ mpsc::{self, UnboundedSender}, @@ -17,7 +15,6 @@ use futures::{ Stream, StreamExt, }; use fuzzy::CharBag; -use git::repository::GitRepository; use git::{DOT_GIT, GITIGNORE}; use gpui::{ executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, @@ -25,13 +22,14 @@ use gpui::{ }; use language::{ proto::{deserialize_version, serialize_line_ending, serialize_version}, - Buffer, DiagnosticEntry, LineEnding, PointUtf16, Rope, + Buffer, DiagnosticEntry, Rope, }; use parking_lot::Mutex; use postage::{ prelude::{Sink as _, Stream as _}, watch, }; +use rope::point_utf16::PointUtf16; use smol::channel::{self, Sender}; use std::{ @@ -2970,11 +2968,10 @@ async fn send_worktree_update(client: &Arc, update: proto::UpdateWorktre #[cfg(test)] mod tests { use super::*; - use crate::fs::FakeFs; use anyhow::Result; use client::test::FakeHttpClient; - use fs::RealFs; - use git::repository::FakeGitRepository; + use fs::repository::FakeGitRepository; + use fs::{FakeFs, RealFs}; use gpui::{executor::Deterministic, TestAppContext}; use rand::prelude::*; use serde_json::json; diff --git a/crates/rope/Cargo.toml b/crates/rope/Cargo.toml new file mode 100644 index 0000000000..0f754c1fb3 --- /dev/null +++ b/crates/rope/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "rope" +version = "0.1.0" +edition = "2021" + +[lib] +path = "src/rope.rs" + +[dependencies] +bromberg_sl2 = "0.6" +smallvec = { version = "1.6", features = ["union"] } +sum_tree = { path = "../sum_tree" } +arrayvec = "0.7.1" +log = { version = "0.4.16", features = ["kv_unstable_serde"] } + + +[dev-dependencies] +rand = "0.8.3" +util = { path = "../util", features = ["test-support"] } +gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/text/src/offset_utf16.rs b/crates/rope/src/offset_utf16.rs similarity index 100% rename from crates/text/src/offset_utf16.rs rename to crates/rope/src/offset_utf16.rs diff --git a/crates/text/src/point.rs b/crates/rope/src/point.rs similarity index 100% rename from crates/text/src/point.rs rename to crates/rope/src/point.rs diff --git a/crates/text/src/point_utf16.rs b/crates/rope/src/point_utf16.rs similarity index 100% rename from crates/text/src/point_utf16.rs rename to crates/rope/src/point_utf16.rs diff --git a/crates/text/src/rope.rs b/crates/rope/src/rope.rs similarity index 99% rename from crates/text/src/rope.rs rename to crates/rope/src/rope.rs index e148c048bb..39dc9dc049 100644 --- a/crates/text/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -1,7 +1,12 @@ -use super::Point; -use crate::{OffsetUtf16, PointUtf16}; +pub mod offset_utf16; +pub mod point; +pub mod point_utf16; + use arrayvec::ArrayString; use bromberg_sl2::{DigestString, HashMatrix}; +use offset_utf16::OffsetUtf16; +use point::Point; +use point_utf16::PointUtf16; use smallvec::SmallVec; use std::{cmp, fmt, io, mem, ops::Range, str}; use sum_tree::{Bias, Dimension, SumTree}; @@ -1073,9 +1078,9 @@ fn find_split_ix(text: &str) -> usize { #[cfg(test)] mod tests { use super::*; - use crate::random_char_iter::RandomCharIter; use rand::prelude::*; use std::{cmp::Ordering, env, io::Read}; + use util::RandomCharIter; use Bias::{Left, Right}; #[test] diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index 0d47525021..ad960ec93e 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -14,23 +14,23 @@ test-support = ["rand"] clock = { path = "../clock" } collections = { path = "../collections" } fs = { path = "../fs" } +rope = { path = "../rope" } sum_tree = { path = "../sum_tree" } anyhow = "1.0.38" -arrayvec = "0.7.1" digest = { version = "0.9", features = ["std"] } -bromberg_sl2 = "0.6" lazy_static = "1.4" log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11" postage = { version = "0.4.1", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } -regex = "1.5" smallvec = { version = "1.6", features = ["union"] } +util = { path = "../util" } +regex = "1.5" + [dev-dependencies] collections = { path = "../collections", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } -util = { path = "../util", features = ["test-support"] } ctor = "0.1" env_logger = "0.9" rand = "0.8.3" diff --git a/crates/text/src/anchor.rs b/crates/text/src/anchor.rs index 9f70ae1cc7..024c7e643b 100644 --- a/crates/text/src/anchor.rs +++ b/crates/text/src/anchor.rs @@ -1,9 +1,10 @@ -use super::{Point, ToOffset}; -use crate::{rope::TextDimension, BufferSnapshot, PointUtf16, ToPoint, ToPointUtf16}; use anyhow::Result; +use rope::{point::Point, point_utf16::PointUtf16, TextDimension}; use std::{cmp::Ordering, fmt::Debug, ops::Range}; use sum_tree::Bias; +use crate::{BufferSnapshot, ToOffset, ToPoint, ToPointUtf16}; + #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, Default)] pub struct Anchor { pub timestamp: clock::Local, diff --git a/crates/text/src/random_char_iter.rs b/crates/text/src/random_char_iter.rs deleted file mode 100644 index 04cdcd3524..0000000000 --- a/crates/text/src/random_char_iter.rs +++ /dev/null @@ -1,36 +0,0 @@ -use rand::prelude::*; - -pub struct RandomCharIter(T); - -impl RandomCharIter { - pub fn new(rng: T) -> Self { - Self(rng) - } -} - -impl Iterator for RandomCharIter { - type Item = char; - - fn next(&mut self) -> Option { - if std::env::var("SIMPLE_TEXT").map_or(false, |v| !v.is_empty()) { - return if self.0.gen_range(0..100) < 5 { - Some('\n') - } else { - Some(self.0.gen_range(b'a'..b'z' + 1).into()) - }; - } - - match self.0.gen_range(0..100) { - // whitespace - 0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.0).copied(), - // two-byte greek letters - 20..=32 => char::from_u32(self.0.gen_range(('α' as u32)..('ω' as u32 + 1))), - // // three-byte characters - 33..=45 => ['✋', '✅', '❌', '❎', '⭐'].choose(&mut self.0).copied(), - // // four-byte characters - 46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.0).copied(), - // ascii letters - _ => Some(self.0.gen_range(b'a'..b'z' + 1).into()), - } - } -} diff --git a/crates/text/src/selection.rs b/crates/text/src/selection.rs index e5acbd21bc..881fc8c432 100644 --- a/crates/text/src/selection.rs +++ b/crates/text/src/selection.rs @@ -1,5 +1,7 @@ -use crate::Anchor; -use crate::{rope::TextDimension, BufferSnapshot}; +use rope::TextDimension; + +use crate::{Anchor, BufferSnapshot}; + use std::cmp::Ordering; use std::ops::Range; diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 52859b7515..2196e870f2 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -2,14 +2,8 @@ mod anchor; pub mod locator; #[cfg(any(test, feature = "test-support"))] pub mod network; -mod offset_utf16; pub mod operation_queue; mod patch; -mod point; -mod point_utf16; -#[cfg(any(test, feature = "test-support"))] -pub mod random_char_iter; -pub mod rope; mod selection; pub mod subscription; #[cfg(test)] @@ -20,22 +14,15 @@ pub use anchor::*; use anyhow::Result; use clock::ReplicaId; use collections::{HashMap, HashSet}; -use lazy_static::lazy_static; +use fs::LineEnding; use locator::Locator; -pub use offset_utf16::*; use operation_queue::OperationQueue; pub use patch::Patch; -pub use point::*; -pub use point_utf16::*; use postage::{barrier, oneshot, prelude::*}; -#[cfg(any(test, feature = "test-support"))] -pub use random_char_iter::*; -use regex::Regex; -use rope::TextDimension; +use rope::{offset_utf16::OffsetUtf16, point::Point, point_utf16::PointUtf16, TextDimension}; pub use rope::{Chunks, Rope, TextSummary}; pub use selection::*; use std::{ - borrow::Cow, cmp::{self, Ordering, Reverse}, future::Future, iter::Iterator, @@ -49,9 +36,8 @@ pub use sum_tree::Bias; use sum_tree::{FilterCursor, SumTree, TreeMap}; use undo_map::UndoMap; -lazy_static! { - static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); -} +#[cfg(any(test, feature = "test-support"))] +use util::RandomCharIter; pub type TransactionId = clock::Local; @@ -1458,9 +1444,7 @@ impl Buffer { last_end = Some(range.end); let new_text_len = rng.gen_range(0..10); - let new_text: String = crate::random_char_iter::RandomCharIter::new(&mut *rng) - .take(new_text_len) - .collect(); + let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect(); edits.push((range, new_text.into())); } diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 78416aa5b5..c083137156 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -7,21 +7,20 @@ edition = "2021" doctest = false [features] -test-support = ["rand", "serde_json", "tempdir", "git2"] +test-support = ["serde_json", "tempdir", "git2"] [dependencies] anyhow = "1.0.38" futures = "0.3" log = { version = "0.4.16", features = ["kv_unstable_serde"] } lazy_static = "1.4.0" -rand = { version = "0.8", optional = true } +rand = { workspace = true } tempdir = { version = "0.3.7", optional = true } serde_json = { version = "1.0", features = ["preserve_order"], optional = true } git2 = { version = "0.15", default-features = false, optional = true } [dev-dependencies] -rand = { version = "0.8" } tempdir = { version = "0.3.7" } serde_json = { version = "1.0", features = ["preserve_order"] } git2 = { version = "0.15", default-features = false } diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index 97f409f410..e35f2df7d4 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -2,6 +2,7 @@ pub mod test; use futures::Future; +use rand::{seq::SliceRandom, Rng}; use std::{ cmp::Ordering, ops::AddAssign, @@ -155,6 +156,41 @@ pub fn defer(f: F) -> impl Drop { Defer(Some(f)) } +pub struct RandomCharIter(T); + +impl RandomCharIter { + pub fn new(rng: T) -> Self { + Self(rng) + } +} + +impl Iterator for RandomCharIter { + type Item = char; + + fn next(&mut self) -> Option { + if std::env::var("SIMPLE_TEXT").map_or(false, |v| !v.is_empty()) { + return if self.0.gen_range(0..100) < 5 { + Some('\n') + } else { + Some(self.0.gen_range(b'a'..b'z' + 1).into()) + }; + } + + match self.0.gen_range(0..100) { + // whitespace + 0..=19 => [' ', '\n', '\r', '\t'].choose(&mut self.0).copied(), + // two-byte greek letters + 20..=32 => char::from_u32(self.0.gen_range(('α' as u32)..('ω' as u32 + 1))), + // // three-byte characters + 33..=45 => ['✋', '✅', '❌', '❎', '⭐'].choose(&mut self.0).copied(), + // // four-byte characters + 46..=58 => ['🍐', '🏀', '🍗', '🎉'].choose(&mut self.0).copied(), + // ascii letters + _ => Some(self.0.gen_range(b'a'..b'z' + 1).into()), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/vim/Cargo.toml b/crates/vim/Cargo.toml index 5bc32cd5bd..bf094c30fa 100644 --- a/crates/vim/Cargo.toml +++ b/crates/vim/Cargo.toml @@ -14,6 +14,7 @@ command_palette = { path = "../command_palette" } editor = { path = "../editor" } gpui = { path = "../gpui" } language = { path = "../language" } +rope = { path = "../rope" } search = { path = "../search" } serde = { version = "1.0", features = ["derive", "rc"] } settings = { path = "../settings" } diff --git a/crates/vim/src/normal.rs b/crates/vim/src/normal.rs index db5583b4ce..a06bcc54da 100644 --- a/crates/vim/src/normal.rs +++ b/crates/vim/src/normal.rs @@ -13,7 +13,8 @@ use change::init as change_init; use collections::HashSet; use editor::{Autoscroll, Bias, ClipboardSelection, DisplayPoint}; use gpui::{actions, MutableAppContext, ViewContext}; -use language::{AutoindentMode, Point, SelectionGoal}; +use language::{AutoindentMode, SelectionGoal}; +use rope::point::Point; use workspace::Workspace; use self::{change::change_over, delete::delete_over, yank::yank_over}; diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index 2fd43b7bcb..c0cb2e9edd 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -21,6 +21,7 @@ client = { path = "../client" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } drag_and_drop = { path = "../drag_and_drop" } +fs = { path = "../fs" } gpui = { path = "../gpui" } language = { path = "../language" } menu = { path = "../menu" } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 705823003f..e11cd80f09 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -32,7 +32,8 @@ use log::{error, warn}; pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; -use project::{fs, Fs, Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; +use fs::{self, Fs}; +use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use searchable::SearchableItemHandle; use serde::Deserialize; use settings::{Autosave, DockAnchor, Settings}; diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d0b41b08f1..6c92dd9bef 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -32,6 +32,7 @@ diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } file_finder = { path = "../file_finder" } search = { path = "../search" } +fs = { path = "../fs" } fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } go_to_line = { path = "../go_to_line" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index f48f8b723e..6c93ff5ba6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -14,7 +14,6 @@ use client::{ http::{self, HttpClient}, UserStore, ZED_SECRET_CLIENT_TOKEN, }; -use fs::OpenOptions; use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, @@ -28,16 +27,16 @@ use project::{Fs, ProjectStore}; use serde_json::json; use settings::{self, KeymapFileContent, Settings, SettingsFileContent, WorkingDirectory}; use smol::process::Command; -use std::{env, ffi::OsStr, fs, panic, path::PathBuf, sync::Arc, thread, time::Duration}; +use std::fs::OpenOptions; +use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Duration}; use terminal::terminal_container_view::{get_working_directory, TerminalContainer}; +use fs::RealFs; use theme::ThemeRegistry; use util::{ResultExt, TryFutureExt}; use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace}; use zed::{ - self, build_window_options, - fs::RealFs, - initialize_workspace, languages, menus, + self, build_window_options, initialize_workspace, languages, menus, settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}, }; @@ -200,23 +199,23 @@ fn main() { } fn init_paths() { - fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); - fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); - fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); - fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); + std::fs::create_dir_all(&*zed::paths::CONFIG_DIR).expect("could not create config path"); + std::fs::create_dir_all(&*zed::paths::LANGUAGES_DIR).expect("could not create languages path"); + std::fs::create_dir_all(&*zed::paths::DB_DIR).expect("could not create database path"); + std::fs::create_dir_all(&*zed::paths::LOGS_DIR).expect("could not create logs path"); // Copy setting files from legacy locations. TODO: remove this after a few releases. thread::spawn(|| { - if fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() - && fs::metadata(&*zed::paths::SETTINGS).is_err() + if std::fs::metadata(&*zed::paths::legacy::SETTINGS).is_ok() + && std::fs::metadata(&*zed::paths::SETTINGS).is_err() { - fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); + std::fs::copy(&*zed::paths::legacy::SETTINGS, &*zed::paths::SETTINGS).log_err(); } - if fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() - && fs::metadata(&*zed::paths::KEYMAP).is_err() + if std::fs::metadata(&*zed::paths::legacy::KEYMAP).is_ok() + && std::fs::metadata(&*zed::paths::KEYMAP).is_err() { - fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); + std::fs::copy(&*zed::paths::legacy::KEYMAP, &*zed::paths::KEYMAP).log_err(); } }); } @@ -231,9 +230,10 @@ fn init_logger() { const KIB: u64 = 1024; const MIB: u64 = 1024 * KIB; const MAX_LOG_BYTES: u64 = MIB; - if fs::metadata(&*zed::paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) + if std::fs::metadata(&*zed::paths::LOG) + .map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES) { - let _ = fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); + let _ = std::fs::rename(&*zed::paths::LOG, &*zed::paths::OLD_LOG); } let log_file = OpenOptions::new() @@ -289,7 +289,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A .body(body.into())?; let response = http.send(request).await.context("error sending panic")?; if response.status().is_success() { - fs::remove_file(child_path) + std::fs::remove_file(child_path) .context("error removing panic after sending it successfully") .log_err(); } else { @@ -338,7 +338,7 @@ fn init_panic_hook(app_version: String, http: Arc, background: A }; let panic_filename = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string(); - fs::write( + std::fs::write( zed::paths::LOGS_DIR.join(format!("zed-{}-{}.panic", app_version, panic_filename)), &message, ) @@ -395,7 +395,7 @@ fn stdout_is_a_pty() -> bool { fn collect_path_args() -> Vec { env::args() .skip(1) - .filter_map(|arg| match fs::canonicalize(arg) { + .filter_map(|arg| match std::fs::canonicalize(arg) { Ok(path) => Some(path), Err(error) => { log::error!("error parsing path argument: {}", error); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 6210242c51..a2857f01dc 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,8 +1,8 @@ mod feedback; pub mod languages; pub mod menus; -pub mod settings_file; pub mod paths; +pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; @@ -14,6 +14,7 @@ use collab_ui::CollabTitlebarItem; use collections::VecDeque; pub use editor; use editor::{Editor, MultiBuffer}; + use gpui::{ actions, geometry::vector::vec2f, @@ -23,7 +24,7 @@ use gpui::{ }; use language::Rope; pub use lsp; -pub use project::{self, fs}; +pub use project; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; use serde::Deserialize; From 5487f99ac78bf4bd8ac2dc73d19db2e18757faa8 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 16:03:38 -0700 Subject: [PATCH 06/22] Moved settings_file.rs into settings crate. Should be ready to start now :D --- Cargo.lock | 3 +++ crates/language/src/buffer.rs | 4 +++- crates/settings/Cargo.toml | 5 ++++- crates/settings/src/settings.rs | 1 + crates/{zed => settings}/src/settings_file.rs | 12 +++++++----- crates/zed/src/main.rs | 6 ++---- crates/zed/src/zed.rs | 1 - 7 files changed, 20 insertions(+), 12 deletions(-) rename crates/{zed => settings}/src/settings_file.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index ebf2d4376c..fca454f1f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5060,8 +5060,11 @@ dependencies = [ "anyhow", "assets", "collections", + "fs", + "futures", "gpui", "json_comments", + "postage", "schemars", "serde", "serde_json", diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 56aad18ead..a9af41bb23 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -40,7 +40,9 @@ use sum_tree::TreeMap; use text::operation_queue::OperationQueue; pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Operation as _, *}; use theme::SyntaxTheme; -use util::{RandomCharIter, TryFutureExt as _}; +#[cfg(any(test, feature = "test-support"))] +use util::RandomCharIter; +use util::TryFutureExt as _; #[cfg(any(test, feature = "test-support"))] pub use {tree_sitter_rust, tree_sitter_typescript}; diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index e8fb58cd61..64c906a833 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -14,10 +14,13 @@ test-support = [] assets = { path = "../assets" } collections = { path = "../collections" } gpui = { path = "../gpui" } +fs = { path = "../fs" } +anyhow = "1.0.38" +futures = "0.3" theme = { path = "../theme" } util = { path = "../util" } -anyhow = "1.0.38" json_comments = "0.2" +postage = { version = "0.4.1", features = ["futures-traits"] } schemars = "0.8" serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 921b36051f..883d7694c7 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -1,4 +1,5 @@ mod keymap_file; +pub mod settings_file; use anyhow::Result; use gpui::{ diff --git a/crates/zed/src/settings_file.rs b/crates/settings/src/settings_file.rs similarity index 96% rename from crates/zed/src/settings_file.rs rename to crates/settings/src/settings_file.rs index 25bd065b47..f12f191041 100644 --- a/crates/zed/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,14 +1,16 @@ +use fs::Fs; use futures::StreamExt; use gpui::{executor, MutableAppContext}; use postage::sink::Sink as _; use postage::{prelude::Stream, watch}; -use project::Fs; use serde::Deserialize; -use settings::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; + use std::{path::Path, sync::Arc, time::Duration}; use theme::ThemeRegistry; use util::ResultExt; +use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; + #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); @@ -77,7 +79,7 @@ pub fn watch_settings_file( pub fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { cx.clear_bindings(); - settings::KeymapFileContent::load_defaults(cx); + KeymapFileContent::load_defaults(cx); content.add_to_cx(cx).log_err(); } @@ -105,8 +107,8 @@ pub fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut #[cfg(test)] mod tests { use super::*; - use project::FakeFs; - use settings::{EditorSettings, SoftWrap}; + use crate::{EditorSettings, SoftWrap}; + use fs::FakeFs; #[gpui::test] async fn test_watch_settings_files(cx: &mut gpui::TestAppContext) { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 6c93ff5ba6..7dc30d34f5 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -32,13 +32,11 @@ use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Durati use terminal::terminal_container_view::{get_working_directory, TerminalContainer}; use fs::RealFs; +use settings::settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}; use theme::ThemeRegistry; use util::{ResultExt, TryFutureExt}; use workspace::{self, AppState, ItemHandle, NewFile, OpenPaths, Workspace}; -use zed::{ - self, build_window_options, initialize_workspace, languages, menus, - settings_file::{watch_keymap_file, watch_settings_file, WatchedJsonFile}, -}; +use zed::{self, build_window_options, initialize_workspace, languages, menus}; fn main() { let http = http::client(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a2857f01dc..9012bc89e2 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2,7 +2,6 @@ mod feedback; pub mod languages; pub mod menus; pub mod paths; -pub mod settings_file; #[cfg(any(test, feature = "test-support"))] pub mod test; From 7eceff1d7b81443595e985000fb794c744e15552 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 11 Oct 2022 18:50:04 -0700 Subject: [PATCH 07/22] Impose min scrollbar height in a way that doesn't impede scrollbar's movement Also, fix the editor's scroll max so that you can scroll to the last display row. --- crates/editor/src/editor.rs | 2 +- crates/editor/src/element.rs | 36 +++++++++++++++--------------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4be6d72017..a7acc9f609 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1271,7 +1271,7 @@ impl Editor { let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) { (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.) } else { - display_map.max_point().row().saturating_sub(1) as f32 + display_map.max_point().row() as f32 }; if scroll_position.y() > max_scroll_top { scroll_position.set_y(max_scroll_top); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index ec39bf1eb2..36d717641e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -916,36 +916,30 @@ impl EditorElement { let view = self.view.clone(); let style = &self.style.theme.scrollbar; - let min_thumb_height = - style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); let top = bounds.min_y(); let bottom = bounds.max_y(); let right = bounds.max_x(); let left = right - style.width; - let height = bounds.height(); let row_range = &layout.scrollbar_row_range; - let max_row = layout.max_row + ((row_range.end - row_range.start) as u32); - let scrollbar_start = row_range.start as f32 / max_row as f32; - let scrollbar_end = row_range.end as f32 / max_row as f32; + let max_row = layout.max_row as f32 + (row_range.end - row_range.start); - let mut thumb_top = top + scrollbar_start * height; - let mut thumb_bottom = top + scrollbar_end * height; - let thumb_center = (thumb_top + thumb_bottom) / 2.0; + let mut height = bounds.height(); + let mut first_row_y_offset = 0.0; - if thumb_bottom - thumb_top < min_thumb_height { - thumb_top = thumb_center - min_thumb_height / 2.0; - thumb_bottom = thumb_center + min_thumb_height / 2.0; - if thumb_top < top { - thumb_top = top; - thumb_bottom = top + min_thumb_height; - } - if thumb_bottom > bottom { - thumb_bottom = bottom; - thumb_top = bottom - min_thumb_height; - } + // Impose a minimum height on the scrollbar thumb + let min_thumb_height = + style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); + let thumb_height = (row_range.end - row_range.start) * height / max_row; + if thumb_height < min_thumb_height { + first_row_y_offset = (min_thumb_height - thumb_height) / 2.0; + height -= min_thumb_height - thumb_height; } + let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * height / max_row }; + + let thumb_top = y_for_row(row_range.start) - first_row_y_offset; + let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset; let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom)); let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); @@ -1723,7 +1717,7 @@ impl Element for EditorElement { let scroll_max = vec2f( ((scroll_width - text_size.x()) / em_width).max(0.0), - max_row.saturating_sub(1) as f32, + max_row as f32, ); self.update_view(cx.app, |view, cx| { From e7b6d1befeb0c44733b10a26157b56773c1bb061 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 19:18:29 -0700 Subject: [PATCH 08/22] Added theme and dock anchor saving :D --- Cargo.lock | 2 + crates/fs/Cargo.toml | 1 + crates/fs/src/fs.rs | 23 ++++++++++ crates/settings/Cargo.toml | 2 + crates/settings/src/settings.rs | 31 ++++++++----- crates/settings/src/settings_file.rs | 49 ++++++++++++++++++--- crates/theme_selector/src/theme_selector.rs | 4 ++ crates/workspace/src/dock.rs | 7 +++ crates/zed/src/main.rs | 11 +++-- 9 files changed, 111 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fca454f1f8..53f8aae9c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2009,6 +2009,7 @@ dependencies = [ "serde", "serde_json", "smol", + "tempfile", "util", ] @@ -5065,6 +5066,7 @@ dependencies = [ "gpui", "json_comments", "postage", + "rope", "schemars", "serde", "serde_json", diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 182d13894d..5b9082d114 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -15,6 +15,7 @@ util = { path = "../util" } anyhow = "1.0.57" async-trait = "0.1" futures = "0.3" +tempfile = "3" fsevent = { path = "../fsevent" } lazy_static = "1.4.0" parking_lot = "0.11.1" diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 26045f2776..2061d3734b 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -12,6 +12,7 @@ use rope::Rope; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::borrow::Cow; use std::cmp; +use std::io::Write; use std::sync::Arc; use std::{ io, @@ -20,6 +21,7 @@ use std::{ pin::Pin, time::{Duration, SystemTime}, }; +use tempfile::NamedTempFile; use util::ResultExt; #[cfg(any(test, feature = "test-support"))] @@ -100,6 +102,7 @@ pub trait Fs: Send + Sync { async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>; async fn open_sync(&self, path: &Path) -> Result>; async fn load(&self, path: &Path) -> Result; + async fn atomic_write(&self, path: PathBuf, text: String) -> Result<()>; async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()>; async fn canonicalize(&self, path: &Path) -> Result; async fn is_file(&self, path: &Path) -> bool; @@ -260,6 +263,18 @@ impl Fs for RealFs { Ok(text) } + async fn atomic_write(&self, path: PathBuf, data: String) -> Result<()> { + smol::unblock(move || { + let mut tmp_file = NamedTempFile::new()?; + tmp_file.write_all(data.as_bytes())?; + tmp_file.persist(path)?; + Ok::<(), anyhow::Error>(()) + }) + .await?; + + Ok(()) + } + async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { let buffer_size = text.summary().len.min(10 * 1024); let file = smol::fs::File::create(path).await?; @@ -880,6 +895,14 @@ impl Fs for FakeFs { entry.file_content(&path).cloned() } + async fn atomic_write(&self, path: PathBuf, data: String) -> Result<()> { + self.simulate_random_delay().await; + let path = normalize_path(path.as_path()); + self.insert_file(path, data.to_string()).await; + + Ok(()) + } + async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { self.simulate_random_delay().await; let path = normalize_path(path); diff --git a/crates/settings/Cargo.toml b/crates/settings/Cargo.toml index 64c906a833..1cc73fabc4 100644 --- a/crates/settings/Cargo.toml +++ b/crates/settings/Cargo.toml @@ -19,6 +19,7 @@ anyhow = "1.0.38" futures = "0.3" theme = { path = "../theme" } util = { path = "../util" } +rope = { path = "../rope" } json_comments = "0.2" postage = { version = "0.4.1", features = ["futures-traits"] } schemars = "0.8" @@ -32,3 +33,4 @@ tree-sitter-json = "*" [dev-dependencies] unindent = "0.1" gpui = { path = "../gpui", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 883d7694c7..2e7dc08d16 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -503,7 +503,11 @@ pub fn settings_file_json_schema( serde_json::to_value(root_schema).unwrap() } -pub fn write_theme(mut settings_content: String, new_val: &str) -> String { +pub fn write_top_level_setting( + mut settings_content: String, + top_level_key: &str, + new_val: &str, +) -> String { let mut parser = tree_sitter::Parser::new(); parser.set_language(tree_sitter_json::language()).unwrap(); let tree = parser.parse(&settings_content, None).unwrap(); @@ -536,7 +540,7 @@ pub fn write_theme(mut settings_content: String, new_val: &str) -> String { first_key_start.get_or_insert_with(|| key.node.start_byte()); if let Some(key_text) = settings_content.get(key.node.byte_range()) { - if key_text == "\"theme\"" { + if key_text == format!("\"{top_level_key}\"") { existing_value_range = Some(value.node.byte_range()); break; } @@ -547,7 +551,12 @@ pub fn write_theme(mut settings_content: String, new_val: &str) -> String { (None, None) => { // No document, create a new object and overwrite settings_content.clear(); - write!(settings_content, "{{\n \"theme\": \"{new_val}\"\n}}\n").unwrap(); + write!( + settings_content, + "{{\n \"{}\": \"{new_val}\"\n}}\n", + top_level_key + ) + .unwrap(); } (_, Some(existing_value_range)) => { @@ -572,7 +581,7 @@ pub fn write_theme(mut settings_content: String, new_val: &str) -> String { } } - let content = format!(r#""theme": "{new_val}","#); + let content = format!(r#""{top_level_key}": "{new_val}","#); settings_content.insert_str(first_key_start, &content); if row > 0 { @@ -603,7 +612,7 @@ pub fn parse_json_with_comments(content: &str) -> Result #[cfg(test)] mod tests { - use crate::write_theme; + use crate::write_top_level_setting; use unindent::Unindent; #[test] @@ -622,7 +631,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -642,7 +651,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -658,7 +667,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -668,7 +677,7 @@ mod tests { let settings = r#"{ "a": "", "ok": true }"#.to_string(); let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -678,7 +687,7 @@ mod tests { let settings = r#" { "a": "", "ok": true }"#.to_string(); let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } @@ -700,7 +709,7 @@ mod tests { "# .unindent(); - let settings_after_theme = write_theme(settings, "summerfruit-light"); + let settings_after_theme = write_top_level_setting(settings, "theme", "summerfruit-light"); assert_eq!(settings_after_theme, new_settings) } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index f12f191041..6a7c96fd81 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -9,15 +9,54 @@ use std::{path::Path, sync::Arc, time::Duration}; use theme::ThemeRegistry; use util::ResultExt; -use crate::{parse_json_with_comments, KeymapFileContent, Settings, SettingsFileContent}; +use crate::{ + parse_json_with_comments, write_top_level_setting, KeymapFileContent, Settings, + SettingsFileContent, +}; + +// TODO: Switch SettingsFile to open a worktree and buffer for synchronization +// And instant updates in the Zed editor +#[derive(Clone)] +pub struct SettingsFile { + path: &'static Path, + fs: Arc, +} + +impl SettingsFile { + pub fn new(path: &'static Path, fs: Arc) -> Self { + SettingsFile { path, fs } + } + + pub async fn rewrite_settings_file(&self, f: F) -> anyhow::Result<()> + where + F: Fn(String) -> String, + { + let content = self.fs.load(self.path).await?; + + let new_settings = f(content); + + self.fs + .atomic_write(self.path.to_path_buf(), new_settings) + .await?; + + Ok(()) + } +} + +pub fn write_setting(key: &'static str, val: String, cx: &mut MutableAppContext) { + let settings_file = cx.global::().clone(); + cx.background() + .spawn(async move { + settings_file + .rewrite_settings_file(|settings| write_top_level_setting(settings, key, &val)) + .await + }) + .detach_and_log_err(cx); +} #[derive(Clone)] pub struct WatchedJsonFile(pub watch::Receiver); -// 1) Do the refactoring to pull WatchedJSON and fs out and into everything else -// 2) Scaffold this by making the basic structs we'll need SettingsFile::atomic_write_theme() -// 3) Fix the overeager settings writing, if that works, and there's no data loss, call it? - impl WatchedJsonFile where T: 'static + for<'de> Deserialize<'de> + Clone + Default + Send + Sync, diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 1cd7b3f926..f3ca38b78b 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -153,6 +153,10 @@ impl PickerDelegate for ThemeSelector { fn confirm(&mut self, cx: &mut ViewContext) { self.selection_completed = true; + + let theme_name = cx.global::().theme.meta.name.clone(); + settings::settings_file::write_setting("theme", theme_name, cx); + cx.emit(Event::Dismissed); } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 7e626c60a9..30607afdff 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -35,16 +35,23 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Dock::move_dock); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockRight, cx: &mut ViewContext| { + settings::settings_file::write_setting("default_dock_anchor", "right".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Right), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockBottom, cx: &mut ViewContext| { + settings::settings_file::write_setting("default_dock_anchor", "bottom".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Bottom), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &ExpandDock, cx: &mut ViewContext| { + settings::settings_file::write_setting( + "default_dock_anchor", + "expanded".to_string(), + cx, + ); Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx) }, ); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 7dc30d34f5..1c6a818ef3 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -25,7 +25,10 @@ use log::LevelFilter; use parking_lot::Mutex; use project::{Fs, ProjectStore}; use serde_json::json; -use settings::{self, KeymapFileContent, Settings, SettingsFileContent, WorkingDirectory}; +use settings::{ + self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent, + WorkingDirectory, +}; use smol::process::Command; use std::fs::OpenOptions; use std::{env, ffi::OsStr, panic, path::PathBuf, sync::Arc, thread, time::Duration}; @@ -62,6 +65,7 @@ fn main() { let themes = ThemeRegistry::new(Assets, app.font_cache()); let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes); + let settings_file = SettingsFile::new(&*zed::paths::SETTINGS, fs.clone()); let config_files = load_config_files(&app, fs.clone()); let login_shell_env_loaded = if stdout_is_a_pty() { @@ -94,10 +98,11 @@ fn main() { .spawn(languages::init(languages.clone(), cx.background().clone())); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); - let (settings_file, keymap_file) = cx.background().block(config_files).unwrap(); + let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap(); //Setup settings global before binding actions - watch_settings_file(default_settings, settings_file, themes.clone(), cx); + cx.set_global(settings_file); + watch_settings_file(default_settings, settings_file_content, themes.clone(), cx); watch_keymap_file(keymap_file, cx); context_menu::init(cx); From b1f64d9550297f978984adf60d997a9c1f760920 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 11 Oct 2022 20:25:39 -0700 Subject: [PATCH 09/22] Updated new vim tests with new rope crate --- Cargo.lock | 251 ++++++++++++------ .../src/test/neovim_backed_test_context.rs | 2 +- crates/vim/src/test/neovim_connection.rs | 5 +- crates/workspace/Cargo.toml | 7 +- crates/workspace/src/workspace.rs | 7 +- 5 files changed, 190 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53f8aae9c5..fedb6258f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.1.0" dependencies = [ "auto_update", "editor", - "futures", + "futures 0.3.24", "gpui", "language", "project", @@ -183,6 +183,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-compat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b48b4ff0c2026db683dea961cd8ea874737f56cffca86fa84415eaddc51c00d" +dependencies = [ + "futures-core", + "futures-io", + "once_cell", + "pin-project-lite 0.2.9", + "tokio", +] + [[package]] name = "async-compression" version = "0.3.14" @@ -265,7 +278,7 @@ name = "async-pipe" version = "0.1.3" source = "git+https://github.com/zed-industries/async-pipe-rs?rev=82d00a04211cf4e1236029aa03e6b6ce2a74c553#82d00a04211cf4e1236029aa03e6b6ce2a74c553" dependencies = [ - "futures", + "futures 0.3.24", "log", ] @@ -338,9 +351,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -443,7 +456,7 @@ dependencies = [ "axum-core", "base64", "bitflags", - "bytes", + "bytes 1.2.1", "futures-util", "headers", "http", @@ -475,7 +488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d" dependencies = [ "async-trait", - "bytes", + "bytes 1.2.1", "futures-util", "http", "http-body", @@ -489,7 +502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "277c75e6c814b061ae4947d02335d9659db9771b9950cca670002ae986372f44" dependencies = [ "axum", - "bytes", + "bytes 1.2.1", "futures-util", "http", "mime", @@ -661,6 +674,16 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "1.2.1" @@ -691,7 +714,7 @@ dependencies = [ "anyhow", "client", "collections", - "futures", + "futures 0.3.24", "gpui", "postage", "project", @@ -772,12 +795,12 @@ dependencies = [ "bindgen", "block", "byteorder", - "bytes", + "bytes 1.2.1", "cocoa", "core-foundation", "core-graphics", "foreign-types", - "futures", + "futures 0.3.24", "gpui", "hmac 0.12.1", "jwt", @@ -960,7 +983,7 @@ dependencies = [ "async-tungstenite", "collections", "db", - "futures", + "futures 0.3.24", "gpui", "image", "isahc", @@ -1046,7 +1069,7 @@ dependencies = [ "env_logger", "envy", "fs", - "futures", + "futures 0.3.24", "git", "gpui", "hyper", @@ -1094,7 +1117,7 @@ dependencies = [ "clock", "collections", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "log", @@ -1692,7 +1715,7 @@ dependencies = [ "context_menu", "ctor", "env_logger", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -1996,7 +2019,7 @@ dependencies = [ "async-trait", "collections", "fsevent", - "futures", + "futures 0.3.24", "git2", "gpui", "lazy_static", @@ -2067,9 +2090,15 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.21" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -2082,9 +2111,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -2092,15 +2121,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -2120,9 +2149,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-lite" @@ -2141,9 +2170,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -2152,22 +2181,23 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ + "futures 0.1.31", "futures-channel", "futures-core", "futures-io", @@ -2178,6 +2208,7 @@ dependencies = [ "pin-project-lite 0.2.9", "pin-utils", "slab", + "tokio-io", ] [[package]] @@ -2258,7 +2289,7 @@ dependencies = [ "async-trait", "clock", "collections", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -2338,9 +2369,10 @@ dependencies = [ "etagere", "font-kit", "foreign-types", - "futures", + "futures 0.3.24", "gpui_macros", "image", + "itertools", "lazy_static", "log", "media", @@ -2386,7 +2418,7 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "futures-core", "futures-sink", @@ -2395,7 +2427,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tracing", ] @@ -2434,7 +2466,7 @@ checksum = "4cff78e5788be1e0ab65b04d306b2ed5092c815ec97ec70f4ebd5aee158aa55d" dependencies = [ "base64", "bitflags", - "bytes", + "bytes 1.2.1", "headers-core", "http", "httpdate", @@ -2527,7 +2559,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ - "bytes", + "bytes 1.2.1", "fnv", "itoa", ] @@ -2538,7 +2570,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "bytes", + "bytes 1.2.1", "http", "pin-project-lite 0.2.9", ] @@ -2573,7 +2605,7 @@ version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-channel", "futures-core", "futures-util", @@ -2609,7 +2641,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes", + "bytes 1.2.1", "hyper", "native-tls", "tokio", @@ -2900,7 +2932,7 @@ dependencies = [ "ctor", "env_logger", "fs", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -3062,7 +3094,7 @@ dependencies = [ "anyhow", "core-foundation", "core-graphics", - "futures", + "futures 0.3.24", "media", "parking_lot 0.11.2", "serde", @@ -3099,7 +3131,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "log", "lsp-types", @@ -3192,7 +3224,7 @@ dependencies = [ "anyhow", "bindgen", "block", - "bytes", + "bytes 1.2.1", "core-foundation", "foreign-types", "metal", @@ -3552,6 +3584,21 @@ dependencies = [ "libc", ] +[[package]] +name = "nvim-rs" +version = "0.5.0" +source = "git+https://github.com/KillTheMule/nvim-rs?branch=master#d701c2790dcb2579f8f4d7003ba30e2100a7d25b" +dependencies = [ + "async-trait", + "futures 0.3.24", + "log", + "parity-tokio-ipc", + "rmp", + "rmpv", + "tokio", + "tokio-util 0.7.4", +] + [[package]] name = "objc" version = "0.2.7" @@ -3672,6 +3719,20 @@ dependencies = [ "workspace", ] +[[package]] +name = "parity-tokio-ipc" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6" +dependencies = [ + "futures 0.3.24", + "libc", + "log", + "rand 0.7.3", + "tokio", + "winapi 0.3.9", +] + [[package]] name = "parking" version = "2.0.0" @@ -3976,7 +4037,7 @@ checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b" dependencies = [ "atomic", "crossbeam-queue", - "futures", + "futures 0.3.24", "log", "pin-project", "pollster", @@ -4047,7 +4108,7 @@ dependencies = [ "db", "fs", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "git", "gpui", @@ -4085,7 +4146,7 @@ version = "0.1.0" dependencies = [ "context_menu", "editor", - "futures", + "futures 0.3.24", "gpui", "menu", "postage", @@ -4104,7 +4165,7 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", - "futures", + "futures 0.3.24", "fuzzy", "gpui", "language", @@ -4141,7 +4202,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.8.0", ] @@ -4151,7 +4212,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ - "bytes", + "bytes 1.2.1", "prost-derive 0.9.0", ] @@ -4161,7 +4222,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ - "bytes", + "bytes 1.2.1", "heck 0.3.3", "itertools", "lazy_static", @@ -4207,7 +4268,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ - "bytes", + "bytes 1.2.1", "prost 0.9.0", ] @@ -4470,7 +4531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64", - "bytes", + "bytes 1.2.1", "encoding_rs", "futures-core", "futures-util", @@ -4540,6 +4601,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmpv" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754" +dependencies = [ + "num-traits", + "rmp", +] + [[package]] name = "rocksdb" version = "0.18.0" @@ -4584,7 +4666,7 @@ dependencies = [ "collections", "ctor", "env_logger", - "futures", + "futures 0.3.24", "gpui", "parking_lot 0.11.2", "prost 0.8.0", @@ -5062,7 +5144,7 @@ dependencies = [ "assets", "collections", "fs", - "futures", + "futures 0.3.24", "gpui", "json_comments", "postage", @@ -5345,7 +5427,7 @@ dependencies = [ "base64", "bitflags", "byteorder", - "bytes", + "bytes 1.2.1", "crc", "crossbeam-queue", "dirs 4.0.0", @@ -5598,7 +5680,7 @@ dependencies = [ "context_menu", "dirs 4.0.0", "editor", - "futures", + "futures 0.3.24", "gpui", "itertools", "lazy_static", @@ -5809,16 +5891,16 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" dependencies = [ - "bytes", + "autocfg 1.1.0", + "bytes 1.2.1", "libc", "memchr", "mio 0.8.4", "num_cpus", - "once_cell", "parking_lot 0.12.1", "pin-project-lite 0.2.9", "signal-hook-registry", @@ -5827,6 +5909,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tokio-io" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.31", + "log", +] + [[package]] name = "tokio-io-timeout" version = "1.2.0" @@ -5898,7 +5991,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", "futures-sink", "log", @@ -5908,12 +6001,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ - "bytes", + "bytes 1.2.1", "futures-core", + "futures-io", "futures-sink", "pin-project-lite 0.2.9", "tokio", @@ -5938,7 +6032,7 @@ dependencies = [ "async-stream", "async-trait", "base64", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "h2", @@ -5974,7 +6068,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util 0.7.4", "tower-layer", "tower-service", "tracing", @@ -5987,7 +6081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" dependencies = [ "bitflags", - "bytes", + "bytes 1.2.1", "futures-core", "futures-util", "http", @@ -6266,7 +6360,7 @@ checksum = "6ad3713a14ae247f22a728a0456a545df14acf3867f905adff84be99e23b3ad1" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6285,7 +6379,7 @@ checksum = "d96a2dea40e7570482f28eb57afbe42d97551905da6a9400acc5c328d24004f5" dependencies = [ "base64", "byteorder", - "bytes", + "bytes 1.2.1", "http", "httparse", "log", @@ -6461,7 +6555,7 @@ name = "util" version = "0.1.0" dependencies = [ "anyhow", - "futures", + "futures 0.3.24", "git2", "lazy_static", "log", @@ -6531,6 +6625,8 @@ name = "vim" version = "0.1.0" dependencies = [ "assets", + "async-compat", + "async-trait", "collections", "command_palette", "editor", @@ -6538,12 +6634,17 @@ dependencies = [ "indoc", "itertools", "language", + "lazy_static", "log", + "nvim-rs", + "parking_lot 0.11.2", "project", "rope", "search", "serde", + "serde_json", "settings", + "tokio", "util", "workspace", ] @@ -7230,7 +7331,7 @@ dependencies = [ "context_menu", "drag_and_drop", "fs", - "futures", + "futures 0.3.24", "gpui", "language", "log", @@ -7315,7 +7416,7 @@ dependencies = [ "file_finder", "fs", "fsevent", - "futures", + "futures 0.3.24", "fuzzy", "go_to_line", "gpui", diff --git a/crates/vim/src/test/neovim_backed_test_context.rs b/crates/vim/src/test/neovim_backed_test_context.rs index bb8ba26b74..bdac8fe158 100644 --- a/crates/vim/src/test/neovim_backed_test_context.rs +++ b/crates/vim/src/test/neovim_backed_test_context.rs @@ -51,7 +51,7 @@ impl<'a> NeovimBackedTestContext<'a> { pub async fn set_shared_state(&mut self, marked_text: &str) -> ContextHandle { let context_handle = self.set_state(marked_text, Mode::Normal); - let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); + let selection = self.editor(|editor, cx| editor.selections.newest::(cx)); let text = self.buffer_text(); self.neovim.set_state(selection, &text).await; diff --git a/crates/vim/src/test/neovim_connection.rs b/crates/vim/src/test/neovim_connection.rs index ff4e10cfe5..60ac345323 100644 --- a/crates/vim/src/test/neovim_connection.rs +++ b/crates/vim/src/test/neovim_connection.rs @@ -8,7 +8,10 @@ use async_compat::Compat; use async_trait::async_trait; #[cfg(feature = "neovim")] use gpui::keymap::Keystroke; -use language::{Point, Selection}; + +use language::Selection; +use rope::point::Point; + #[cfg(feature = "neovim")] use lazy_static::lazy_static; #[cfg(feature = "neovim")] diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index c0cb2e9edd..54e7eaf463 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -12,7 +12,9 @@ test-support = [ "call/test-support", "client/test-support", "project/test-support", - "settings/test-support" + "settings/test-support", + "gpui/test-support", + "fs/test-support" ] [dependencies] @@ -43,4 +45,5 @@ call = { path = "../call", features = ["test-support"] } client = { path = "../client", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } project = { path = "../project", features = ["test-support"] } -settings = { path = "../settings", features = ["test-support"] } \ No newline at end of file +settings = { path = "../settings", features = ["test-support"] } +fs = { path = "../fs", features = ["test-support"] } \ No newline at end of file diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e11cd80f09..9a6bc9e6ac 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -17,6 +17,7 @@ use client::{proto, Client, PeerId, TypedEnvelope, UserStore}; use collections::{hash_map, HashMap, HashSet}; use dock::{DefaultItemFactory, Dock, ToggleDockButton}; use drag_and_drop::DragAndDrop; +use fs::{self, Fs}; use futures::{channel::oneshot, FutureExt, StreamExt}; use gpui::{ actions, @@ -32,7 +33,6 @@ use log::{error, warn}; pub use pane::*; pub use pane_group::*; use postage::prelude::Stream; -use fs::{self, Fs}; use project::{Project, ProjectEntryId, ProjectPath, ProjectStore, Worktree, WorktreeId}; use searchable::SearchableItemHandle; use serde::Deserialize; @@ -931,7 +931,7 @@ impl AppState { let settings = Settings::test(cx); cx.set_global(settings); - let fs = project::FakeFs::new(cx.background().clone()); + let fs = fs::FakeFs::new(cx.background().clone()); let languages = Arc::new(LanguageRegistry::test()); let http_client = client::test::FakeHttpClient::with_404_response(); let client = Client::new(http_client.clone(), cx); @@ -2806,8 +2806,9 @@ mod tests { use crate::sidebar::SidebarItem; use super::*; + use fs::FakeFs; use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext}; - use project::{FakeFs, Project, ProjectEntryId}; + use project::{Project, ProjectEntryId}; use serde_json::json; pub fn default_item_factory( From 9adbab5d99f81025eda5469ed70cab0dd577eaee Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 10:28:17 +0200 Subject: [PATCH 10/22] Fix opening a buffer after leaving and joining the same project This bug existed prior to #1700 and was caused by not clearing the buffers that were already shared with a peer that left and opened a project using the same connection. When such peer would re-join the project and open a buffer that it had opened previously, the host assumed the peer had already seen that buffer and wouldn't bother sending it again. --- crates/collab/src/integration_tests.rs | 52 +++++++++++++++++++++++--- crates/collab/src/rpc/store.rs | 2 +- crates/project/src/project.rs | 1 + 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index f6f0f5c7f2..0809a7c61e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2017,7 +2017,7 @@ async fn test_leaving_project( .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 project_b1 = client_b.build_remote_project(project_id, cx_b).await; let project_c = client_c.build_remote_project(project_id, cx_c).await; // Client A sees that a guest has joined. @@ -2025,20 +2025,62 @@ async fn test_leaving_project( project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 2); }); - project_b.read_with(cx_b, |project, _| { + project_b1.read_with(cx_b, |project, _| { assert_eq!(project.collaborators().len(), 2); }); project_c.read_with(cx_c, |project, _| { assert_eq!(project.collaborators().len(), 2); }); - // Drop client B's connection and ensure client A and client C observe client B leaving the project. + // Client B opens a buffer. + let buffer_b1 = project_b1 + .update(cx_b, |project, cx| { + let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id(); + project.open_buffer((worktree_id, "a.txt"), cx) + }) + .await + .unwrap(); + buffer_b1.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents")); + + // Drop client B's project and ensure client A and client C observe client B leaving. + cx_b.update(|_| drop(project_b1)); + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 1); + }); + project_c.read_with(cx_c, |project, _| { + assert_eq!(project.collaborators().len(), 1); + }); + + // Client B re-joins the project and can open buffers as before. + let project_b2 = client_b.build_remote_project(project_id, cx_b).await; + deterministic.run_until_parked(); + project_a.read_with(cx_a, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + project_b2.read_with(cx_b, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + project_c.read_with(cx_c, |project, _| { + assert_eq!(project.collaborators().len(), 2); + }); + + let buffer_b2 = project_b2 + .update(cx_b, |project, cx| { + let worktree_id = project.worktrees(cx).next().unwrap().read(cx).id(); + project.open_buffer((worktree_id, "a.txt"), cx) + }) + .await + .unwrap(); + buffer_b2.read_with(cx_b, |buffer, _| assert_eq!(buffer.text(), "a-contents")); + + // Drop client B's connection and ensure client A and client C observe client B leaving. client_b.disconnect(&cx_b.to_async()).unwrap(); deterministic.run_until_parked(); project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 1); }); - project_b.read_with(cx_b, |project, _| { + project_b2.read_with(cx_b, |project, _| { assert!(project.is_read_only()); }); project_c.read_with(cx_c, |project, _| { @@ -2068,7 +2110,7 @@ async fn test_leaving_project( project_a.read_with(cx_a, |project, _| { assert_eq!(project.collaborators().len(), 0); }); - project_b.read_with(cx_b, |project, _| { + project_b2.read_with(cx_b, |project, _| { assert!(project.is_read_only()); }); project_c.read_with(cx_c, |project, _| { diff --git a/crates/collab/src/rpc/store.rs b/crates/collab/src/rpc/store.rs index cc34094782..b7dd39cff1 100644 --- a/crates/collab/src/rpc/store.rs +++ b/crates/collab/src/rpc/store.rs @@ -1205,7 +1205,7 @@ impl Store { let guest_connection = self.connections.get(guest_connection_id).unwrap(); assert!(guest_connection.projects.contains(project_id)); } - assert_eq!(project.active_replica_ids.len(), project.guests.len(),); + assert_eq!(project.active_replica_ids.len(), project.guests.len()); assert_eq!( project.active_replica_ids, project diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 11792bcf1e..6db9ce8aca 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -4561,6 +4561,7 @@ impl Project { buffer.update(cx, |buffer, cx| buffer.remove_peer(replica_id, cx)); } } + this.shared_buffers.remove(&peer_id); cx.emit(Event::CollaboratorLeft(peer_id)); cx.notify(); From 83d3fad80dbdaa7fc344172e978ff2a472e0d3c3 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 10:53:44 +0200 Subject: [PATCH 11/22] Clear auto-indent requests if they couldn't be computed --- crates/language/src/buffer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index a9af41bb23..a5cf24877c 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -866,6 +866,8 @@ impl Buffer { })); } } + } else { + self.autoindent_requests.clear(); } } From cc56fa9ea63a4c46ce673acdc707a412f3f1a9b9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 15:11:07 +0200 Subject: [PATCH 12/22] Introduce client-side timeout when trying to connect --- crates/client/src/client.rs | 131 +++++++++++++++++++++++++++++------- crates/client/src/test.rs | 10 +-- 2 files changed, 112 insertions(+), 29 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index e8f6b80173..cb1b17107b 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -53,6 +53,8 @@ lazy_static! { } pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894"; +pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100); +pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5); actions!(client, [Authenticate]); @@ -330,7 +332,7 @@ impl Client { let reconnect_interval = state.reconnect_interval; state._reconnect_task = Some(cx.spawn(|cx| async move { let mut rng = StdRng::from_entropy(); - let mut delay = Duration::from_millis(100); + let mut delay = INITIAL_RECONNECTION_DELAY; while let Err(error) = this.authenticate_and_connect(true, &cx).await { log::error!("failed to connect {}", error); if matches!(*this.status().borrow(), Status::ConnectionError) { @@ -661,33 +663,42 @@ impl Client { self.set_status(Status::Reconnecting, cx); } - match self.establish_connection(&credentials, cx).await { - Ok(conn) => { - self.state.write().credentials = Some(credentials.clone()); - if !read_from_keychain && IMPERSONATE_LOGIN.is_none() { - write_credentials_to_keychain(&credentials, cx).log_err(); - } - self.set_connection(conn, cx).await; - Ok(()) - } - Err(EstablishConnectionError::Unauthorized) => { - self.state.write().credentials.take(); - if read_from_keychain { - cx.platform().delete_credentials(&ZED_SERVER_URL).log_err(); - self.set_status(Status::SignedOut, cx); - self.authenticate_and_connect(false, cx).await - } else { - self.set_status(Status::ConnectionError, cx); - Err(EstablishConnectionError::Unauthorized)? + let mut timeout = cx.background().timer(CONNECTION_TIMEOUT).fuse(); + futures::select_biased! { + connection = self.establish_connection(&credentials, cx).fuse() => { + match connection { + Ok(conn) => { + self.state.write().credentials = Some(credentials.clone()); + if !read_from_keychain && IMPERSONATE_LOGIN.is_none() { + write_credentials_to_keychain(&credentials, cx).log_err(); + } + self.set_connection(conn, cx).await; + Ok(()) + } + Err(EstablishConnectionError::Unauthorized) => { + self.state.write().credentials.take(); + if read_from_keychain { + cx.platform().delete_credentials(&ZED_SERVER_URL).log_err(); + self.set_status(Status::SignedOut, cx); + self.authenticate_and_connect(false, cx).await + } else { + self.set_status(Status::ConnectionError, cx); + Err(EstablishConnectionError::Unauthorized)? + } + } + Err(EstablishConnectionError::UpgradeRequired) => { + self.set_status(Status::UpgradeRequired, cx); + Err(EstablishConnectionError::UpgradeRequired)? + } + Err(error) => { + self.set_status(Status::ConnectionError, cx); + Err(error)? + } } } - Err(EstablishConnectionError::UpgradeRequired) => { - self.set_status(Status::UpgradeRequired, cx); - Err(EstablishConnectionError::UpgradeRequired)? - } - Err(error) => { + _ = timeout => { self.set_status(Status::ConnectionError, cx); - Err(error)? + Err(anyhow!("timed out trying to establish connection")) } } } @@ -1169,6 +1180,76 @@ mod tests { assert_eq!(server.auth_count(), 2); // Client re-authenticated due to an invalid token } + #[gpui::test(iterations = 10)] + async fn test_connection_timeout(deterministic: Arc, cx: &mut TestAppContext) { + deterministic.forbid_parking(); + + let user_id = 5; + let client = cx.update(|cx| Client::new(FakeHttpClient::with_404_response(), cx)); + let mut status = client.status(); + + // Time out when client tries to connect. + client.override_authenticate(move |cx| { + cx.foreground().spawn(async move { + Ok(Credentials { + user_id, + access_token: "token".into(), + }) + }) + }); + client.override_establish_connection(|_, cx| { + cx.foreground().spawn(async move { + future::pending::<()>().await; + unreachable!() + }) + }); + let auth_and_connect = cx.spawn({ + let client = client.clone(); + |cx| async move { client.authenticate_and_connect(false, &cx).await } + }); + deterministic.run_until_parked(); + assert!(matches!(status.next().await, Some(Status::Connecting))); + + deterministic.advance_clock(CONNECTION_TIMEOUT); + assert!(matches!( + status.next().await, + Some(Status::ConnectionError { .. }) + )); + auth_and_connect.await.unwrap_err(); + + // Allow the connection to be established. + let server = FakeServer::for_client(user_id, &client, cx).await; + assert!(matches!( + status.next().await, + Some(Status::Connected { .. }) + )); + + // Disconnect client. + server.forbid_connections(); + server.disconnect(); + while !matches!(status.next().await, Some(Status::ReconnectionError { .. })) {} + + // Time out when re-establishing the connection. + server.allow_connections(); + client.override_establish_connection(|_, cx| { + cx.foreground().spawn(async move { + future::pending::<()>().await; + unreachable!() + }) + }); + deterministic.advance_clock(2 * INITIAL_RECONNECTION_DELAY); + assert!(matches!( + status.next().await, + Some(Status::Reconnecting { .. }) + )); + + deterministic.advance_clock(CONNECTION_TIMEOUT); + assert!(matches!( + status.next().await, + Some(Status::ReconnectionError { .. }) + )); + } + #[gpui::test(iterations = 10)] async fn test_authenticating_more_than_once( cx: &mut TestAppContext, diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index 288c9a31fa..d7b7883b17 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -101,10 +101,12 @@ impl FakeServer { } pub fn disconnect(&self) { - self.peer.disconnect(self.connection_id()); - let mut state = self.state.lock(); - state.connection_id.take(); - state.incoming.take(); + if self.state.lock().connection_id.is_some() { + self.peer.disconnect(self.connection_id()); + let mut state = self.state.lock(); + state.connection_id.take(); + state.incoming.take(); + } } pub fn auth_count(&self) -> usize { From ec19f0f8e958ad80d85b017050ebd759e2b21a6a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 15:13:41 +0200 Subject: [PATCH 13/22] Remove unnecessary async from `Peer::add_connection` --- crates/client/src/client.rs | 7 ++--- crates/client/src/test.rs | 2 +- crates/collab/src/rpc.rs | 3 +- crates/rpc/src/peer.rs | 55 +++++++++++++++---------------------- 4 files changed, 27 insertions(+), 40 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index cb1b17107b..32db5e940d 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -672,7 +672,7 @@ impl Client { if !read_from_keychain && IMPERSONATE_LOGIN.is_none() { write_credentials_to_keychain(&credentials, cx).log_err(); } - self.set_connection(conn, cx).await; + self.set_connection(conn, cx); Ok(()) } Err(EstablishConnectionError::Unauthorized) => { @@ -703,13 +703,12 @@ impl Client { } } - async fn set_connection(self: &Arc, conn: Connection, cx: &AsyncAppContext) { + fn set_connection(self: &Arc, conn: Connection, cx: &AsyncAppContext) { let executor = cx.background(); log::info!("add connection to peer"); let (connection_id, handle_io, mut incoming) = self .peer - .add_connection(conn, move |duration| executor.timer(duration)) - .await; + .add_connection(conn, move |duration| executor.timer(duration)); log::info!("set status to connected {}", connection_id); self.set_status(Status::Connected { connection_id }, cx); cx.foreground() diff --git a/crates/client/src/test.rs b/crates/client/src/test.rs index d7b7883b17..ade21f02f4 100644 --- a/crates/client/src/test.rs +++ b/crates/client/src/test.rs @@ -82,7 +82,7 @@ impl FakeServer { let (client_conn, server_conn, _) = Connection::in_memory(cx.background()); let (connection_id, io, incoming) = - peer.add_test_connection(server_conn, cx.background()).await; + peer.add_test_connection(server_conn, cx.background()); cx.background().spawn(io).detach(); let mut state = state.lock(); state.connection_id = Some(connection_id); diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 84449e79d5..470692bf35 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -365,8 +365,7 @@ impl Server { timer.await; } } - }) - .await; + }); tracing::info!(%user_id, %login, %connection_id, %address, "connection opened"); diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 5b1ed6c2af..4dbade4fec 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -113,7 +113,7 @@ impl Peer { } #[instrument(skip_all)] - pub async fn add_connection( + pub fn add_connection( self: &Arc, connection: Connection, create_timer: F, @@ -326,7 +326,7 @@ impl Peer { } #[cfg(any(test, feature = "test-support"))] - pub async fn add_test_connection( + pub fn add_test_connection( self: &Arc, connection: Connection, executor: Arc, @@ -337,7 +337,6 @@ impl Peer { ) { let executor = executor.clone(); self.add_connection(connection, move |duration| executor.timer(duration)) - .await } pub fn disconnect(&self, connection_id: ConnectionId) { @@ -522,21 +521,17 @@ mod tests { let (client1_to_server_conn, server_to_client_1_conn, _kill) = Connection::in_memory(cx.background()); - let (client1_conn_id, io_task1, client1_incoming) = client1 - .add_test_connection(client1_to_server_conn, cx.background()) - .await; - let (_, io_task2, server_incoming1) = server - .add_test_connection(server_to_client_1_conn, cx.background()) - .await; + let (client1_conn_id, io_task1, client1_incoming) = + client1.add_test_connection(client1_to_server_conn, cx.background()); + let (_, io_task2, server_incoming1) = + server.add_test_connection(server_to_client_1_conn, cx.background()); let (client2_to_server_conn, server_to_client_2_conn, _kill) = Connection::in_memory(cx.background()); - let (client2_conn_id, io_task3, client2_incoming) = client2 - .add_test_connection(client2_to_server_conn, cx.background()) - .await; - let (_, io_task4, server_incoming2) = server - .add_test_connection(server_to_client_2_conn, cx.background()) - .await; + let (client2_conn_id, io_task3, client2_incoming) = + client2.add_test_connection(client2_to_server_conn, cx.background()); + let (_, io_task4, server_incoming2) = + server.add_test_connection(server_to_client_2_conn, cx.background()); executor.spawn(io_task1).detach(); executor.spawn(io_task2).detach(); @@ -619,12 +614,10 @@ mod tests { let (client_to_server_conn, server_to_client_conn, _kill) = Connection::in_memory(cx.background()); - let (client_to_server_conn_id, io_task1, mut client_incoming) = client - .add_test_connection(client_to_server_conn, cx.background()) - .await; - let (server_to_client_conn_id, io_task2, mut server_incoming) = server - .add_test_connection(server_to_client_conn, cx.background()) - .await; + let (client_to_server_conn_id, io_task1, mut client_incoming) = + client.add_test_connection(client_to_server_conn, cx.background()); + let (server_to_client_conn_id, io_task2, mut server_incoming) = + server.add_test_connection(server_to_client_conn, cx.background()); executor.spawn(io_task1).detach(); executor.spawn(io_task2).detach(); @@ -719,12 +712,10 @@ mod tests { let (client_to_server_conn, server_to_client_conn, _kill) = Connection::in_memory(cx.background()); - let (client_to_server_conn_id, io_task1, mut client_incoming) = client - .add_test_connection(client_to_server_conn, cx.background()) - .await; - let (server_to_client_conn_id, io_task2, mut server_incoming) = server - .add_test_connection(server_to_client_conn, cx.background()) - .await; + let (client_to_server_conn_id, io_task1, mut client_incoming) = + client.add_test_connection(client_to_server_conn, cx.background()); + let (server_to_client_conn_id, io_task2, mut server_incoming) = + server.add_test_connection(server_to_client_conn, cx.background()); executor.spawn(io_task1).detach(); executor.spawn(io_task2).detach(); @@ -832,9 +823,8 @@ mod tests { let (client_conn, mut server_conn, _kill) = Connection::in_memory(cx.background()); let client = Peer::new(); - let (connection_id, io_handler, mut incoming) = client - .add_test_connection(client_conn, cx.background()) - .await; + let (connection_id, io_handler, mut incoming) = + client.add_test_connection(client_conn, cx.background()); let (io_ended_tx, io_ended_rx) = oneshot::channel(); executor @@ -868,9 +858,8 @@ mod tests { let (client_conn, mut server_conn, _kill) = Connection::in_memory(cx.background()); let client = Peer::new(); - let (connection_id, io_handler, mut incoming) = client - .add_test_connection(client_conn, cx.background()) - .await; + let (connection_id, io_handler, mut incoming) = + client.add_test_connection(client_conn, cx.background()); executor.spawn(io_handler).detach(); executor .spawn(async move { incoming.next().await }) From c4dde0f4e2211d466c955ca30558ec1b4b290352 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 Oct 2022 15:35:28 +0200 Subject: [PATCH 14/22] :lipstick: --- crates/client/src/client.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 32db5e940d..cc6bdf6279 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -663,7 +663,6 @@ impl Client { self.set_status(Status::Reconnecting, cx); } - let mut timeout = cx.background().timer(CONNECTION_TIMEOUT).fuse(); futures::select_biased! { connection = self.establish_connection(&credentials, cx).fuse() => { match connection { @@ -696,7 +695,7 @@ impl Client { } } } - _ = timeout => { + _ = cx.background().timer(CONNECTION_TIMEOUT).fuse() => { self.set_status(Status::ConnectionError, cx); Err(anyhow!("timed out trying to establish connection")) } From 1179f8f7be0d78cbc87d653460bf4095d99f832d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 09:19:29 -0700 Subject: [PATCH 15/22] Fix rounding error in computing editor's row range during layout Co-authored-by: Nathan Sobo Co-authored-by: Antonio Scandurra --- crates/editor/src/element.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 2842928914..f29eb52804 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1582,11 +1582,14 @@ impl Element for EditorElement { // The scroll position is a fractional point, the whole number of which represents // the top of the window in terms of display rows. let start_row = scroll_position.y() as u32; - let visible_row_count = (size.y() / line_height).ceil() as u32; + let height_in_lines = size.y() / line_height; let max_row = snapshot.max_point().row(); // Add 1 to ensure selections bleed off screen - let end_row = 1 + cmp::min(start_row + visible_row_count, max_row); + let end_row = 1 + cmp::min( + (scroll_position.y() + height_in_lines).ceil() as u32, + max_row, + ); let start_anchor = if start_row == 0 { Anchor::min() @@ -1680,8 +1683,7 @@ impl Element for EditorElement { .git_diff_hunks_in_range(start_row..end_row) .collect(); - let scrollbar_row_range = - scroll_position.y()..(scroll_position.y() + visible_row_count as f32); + let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); let mut max_visible_line_width = 0.0; let line_layouts = self.layout_lines(start_row..end_row, &snapshot, cx); From 47332f97c72b330d2b39d15832e5f0d7f11a127f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 12 Oct 2022 09:28:55 -0700 Subject: [PATCH 16/22] Stops the dock anchor from being written to settings --- crates/workspace/src/dock.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 30607afdff..7e626c60a9 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -35,23 +35,16 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Dock::move_dock); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockRight, cx: &mut ViewContext| { - settings::settings_file::write_setting("default_dock_anchor", "right".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Right), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockBottom, cx: &mut ViewContext| { - settings::settings_file::write_setting("default_dock_anchor", "bottom".to_string(), cx); Dock::move_dock(workspace, &MoveDock(DockAnchor::Bottom), cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &ExpandDock, cx: &mut ViewContext| { - settings::settings_file::write_setting( - "default_dock_anchor", - "expanded".to_string(), - cx, - ); Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx) }, ); From aac24938f5751e04e8359fd28321f73a46fa5221 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 12 Oct 2022 09:34:17 -0700 Subject: [PATCH 17/22] Fix a bug in how I parse alacritty's styles --- crates/terminal/src/terminal_element.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/terminal/src/terminal_element.rs b/crates/terminal/src/terminal_element.rs index edf445a1d3..df745dae46 100644 --- a/crates/terminal/src/terminal_element.rs +++ b/crates/terminal/src/terminal_element.rs @@ -330,13 +330,10 @@ impl TerminalElement { } let mut properties = Properties::new(); - if indexed - .flags - .intersects(Flags::BOLD | Flags::BOLD_ITALIC | Flags::DIM_BOLD) - { + if indexed.flags.intersects(Flags::BOLD | Flags::DIM_BOLD) { properties = *properties.weight(Weight::BOLD); } - if indexed.flags.intersects(Flags::ITALIC | Flags::BOLD_ITALIC) { + if indexed.flags.intersects(Flags::ITALIC) { properties = *properties.style(Italic); } From 3c3671a193beb25ea468abf0a059c2605ed2b870 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 11:33:19 -0700 Subject: [PATCH 18/22] Avoid sending stale diagnostics after sharing a worktree Co-authored-by: Antonio Scandurra --- crates/collab/src/integration_tests.rs | 34 +++++++++++++++++++------- crates/project/src/worktree.rs | 21 +++++++++------- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 0809a7c61e..65d17e2da3 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2159,10 +2159,6 @@ async fn test_collaborating_with_diagnostics( ) .await; let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; - let project_id = active_call_a - .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) - .await - .unwrap(); // Cause the language server to start. let _buffer = cx_a @@ -2179,14 +2175,31 @@ async fn test_collaborating_with_diagnostics( .await .unwrap(); - // Join the worktree as client B. - let project_b = client_b.build_remote_project(project_id, cx_b).await; - // Simulate a language server reporting errors for a file. let mut fake_language_server = fake_language_servers.next().await.unwrap(); fake_language_server .receive_notification::() .await; + fake_language_server.notify::( + lsp::PublishDiagnosticsParams { + uri: lsp::Url::from_file_path("/a/a.rs").unwrap(), + version: None, + diagnostics: vec![lsp::Diagnostic { + severity: Some(lsp::DiagnosticSeverity::WARNING), + range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 7)), + message: "message 0".to_string(), + ..Default::default() + }], + }, + ); + + // Client A shares the project and, simultaneously, the language server + // publishes a diagnostic. This is done to ensure that the server always + // observes the latest diagnostics for a worktree. + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); fake_language_server.notify::( lsp::PublishDiagnosticsParams { uri: lsp::Url::from_file_path("/a/a.rs").unwrap(), @@ -2200,6 +2213,9 @@ async fn test_collaborating_with_diagnostics( }, ); + // Join the worktree as client B. + let project_b = client_b.build_remote_project(project_id, cx_b).await; + // Wait for server to see the diagnostics update. deterministic.run_until_parked(); { @@ -2321,7 +2337,7 @@ async fn test_collaborating_with_diagnostics( DiagnosticEntry { range: Point::new(0, 4)..Point::new(0, 7), diagnostic: Diagnostic { - group_id: 1, + group_id: 2, message: "message 1".to_string(), severity: lsp::DiagnosticSeverity::ERROR, is_primary: true, @@ -2331,7 +2347,7 @@ async fn test_collaborating_with_diagnostics( DiagnosticEntry { range: Point::new(0, 10)..Point::new(0, 13), diagnostic: Diagnostic { - group_id: 2, + group_id: 3, severity: lsp::DiagnosticSeverity::WARNING, message: "message 2".to_string(), is_primary: true, diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 3073392a52..0de647029d 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -959,9 +959,20 @@ impl LocalWorktree { let (snapshots_tx, mut snapshots_rx) = watch::channel_with(self.snapshot()); let rpc = self.client.clone(); let worktree_id = cx.model_id() as u64; + + for (path, summary) in self.diagnostic_summaries.iter() { + if let Err(e) = rpc.send(proto::UpdateDiagnosticSummary { + project_id, + worktree_id, + summary: Some(summary.to_proto(&path.0)), + }) { + return Task::ready(Err(e)); + } + } + let maintain_remote_snapshot = cx.background().spawn({ let rpc = rpc; - let diagnostic_summaries = self.diagnostic_summaries.clone(); + async move { let mut prev_snapshot = match snapshots_rx.recv().await { Some(snapshot) => { @@ -994,14 +1005,6 @@ impl LocalWorktree { } }; - for (path, summary) in diagnostic_summaries.iter() { - rpc.send(proto::UpdateDiagnosticSummary { - project_id, - worktree_id, - summary: Some(summary.to_proto(&path.0)), - })?; - } - while let Some(snapshot) = snapshots_rx.recv().await { send_worktree_update( &rpc, From 69dcfbb42367553c5bdeacca3f06084b937403a7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 13:49:09 -0700 Subject: [PATCH 19/22] Send guests DiskBasedDiagnosticsFinished messages when they join a project Co-authored-by: Antonio Scandurra --- crates/collab/src/integration_tests.rs | 50 +++++++++++++++----------- crates/collab/src/rpc.rs | 15 ++++++++ crates/project/src/project.rs | 14 ++++---- 3 files changed, 52 insertions(+), 27 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 65d17e2da3..bac5cc040e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -2161,9 +2161,8 @@ async fn test_collaborating_with_diagnostics( let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await; // Cause the language server to start. - let _buffer = cx_a - .background() - .spawn(project_a.update(cx_a, |project, cx| { + let _buffer = project_a + .update(cx_a, |project, cx| { project.open_buffer( ProjectPath { worktree_id, @@ -2171,7 +2170,7 @@ async fn test_collaborating_with_diagnostics( }, cx, ) - })) + }) .await .unwrap(); @@ -2245,24 +2244,35 @@ async fn test_collaborating_with_diagnostics( // Join project as client C and observe the diagnostics. let project_c = client_c.build_remote_project(project_id, cx_c).await; - deterministic.run_until_parked(); - project_c.read_with(cx_c, |project, cx| { - assert_eq!( - project.diagnostic_summaries(cx).collect::>(), - &[( - ProjectPath { - worktree_id, - path: Arc::from(Path::new("a.rs")), - }, - DiagnosticSummary { - error_count: 1, - warning_count: 0, - ..Default::default() - }, - )] - ) + let project_c_diagnostic_summaries = Rc::new(RefCell::new(Vec::new())); + project_c.update(cx_c, |_, cx| { + let summaries = project_c_diagnostic_summaries.clone(); + cx.subscribe(&project_c, { + move |p, _, event, cx| { + if let project::Event::DiskBasedDiagnosticsFinished { .. } = event { + *summaries.borrow_mut() = p.diagnostic_summaries(cx).collect(); + } + } + }) + .detach(); }); + deterministic.run_until_parked(); + assert_eq!( + project_c_diagnostic_summaries.borrow().as_slice(), + &[( + ProjectPath { + worktree_id, + path: Arc::from(Path::new("a.rs")), + }, + DiagnosticSummary { + error_count: 1, + warning_count: 0, + ..Default::default() + }, + )] + ); + // Simulate a language server reporting more errors for a file. fake_language_server.notify::( lsp::PublishDiagnosticsParams { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 470692bf35..564e173fec 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1012,6 +1012,21 @@ impl Server { } } + for language_server in &project.language_servers { + self.peer.send( + request.sender_id, + proto::UpdateLanguageServer { + project_id: project_id.to_proto(), + language_server_id: language_server.id, + variant: Some( + proto::update_language_server::Variant::DiskBasedDiagnosticsUpdated( + proto::LspDiskBasedDiagnosticsUpdated {}, + ), + ), + }, + )?; + } + Ok(()) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 6db9ce8aca..de4d76ebc3 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1084,13 +1084,6 @@ impl Project { } } - for worktree in self.worktrees(cx).collect::>() { - worktree.update(cx, |worktree, cx| { - let worktree = worktree.as_local_mut().unwrap(); - worktree_share_tasks.push(worktree.share(project_id, cx)); - }); - } - for (server_id, status) in &self.language_server_statuses { self.client .send(proto::StartLanguageServer { @@ -1103,6 +1096,13 @@ impl Project { .log_err(); } + for worktree in self.worktrees(cx).collect::>() { + worktree.update(cx, |worktree, cx| { + let worktree = worktree.as_local_mut().unwrap(); + worktree_share_tasks.push(worktree.share(project_id, cx)); + }); + } + self.client_subscriptions .push(self.client.add_model_for_remote_entity(project_id, cx)); self.metadata_changed(cx); From 89f05ada0b8bd3e00814feb311adddccae2a39b0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 14:14:03 -0700 Subject: [PATCH 20/22] Allow toggling collaboration menu from the keyboard --- assets/keymaps/default.json | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 17 +++++++++-------- crates/collab_ui/src/collab_ui.rs | 2 +- crates/workspace/src/workspace.rs | 4 ++++ crates/zed/src/zed.rs | 18 +++++++++++++++++- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index e2adfc0f81..841d9ab7c8 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -376,6 +376,7 @@ { "bindings": { "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator", + "cmd-shift-c": "collab::ToggleCollaborationMenu", "cmd-alt-i": "zed::DebugElements" } }, diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 9faea76a10..928cf3273b 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -17,10 +17,7 @@ use std::ops::Range; use theme::Theme; use workspace::{FollowNextCollaborator, JoinProject, ToggleFollow, Workspace}; -actions!( - contacts_titlebar_item, - [ToggleContactsPopover, ShareProject] -); +actions!(collab, [ToggleCollaborationMenu, ShareProject]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(CollabTitlebarItem::toggle_contacts_popover); @@ -143,7 +140,11 @@ impl CollabTitlebarItem { } } - fn toggle_contacts_popover(&mut self, _: &ToggleContactsPopover, cx: &mut ViewContext) { + pub fn toggle_contacts_popover( + &mut self, + _: &ToggleCollaborationMenu, + cx: &mut ViewContext, + ) { match self.contacts_popover.take() { Some(_) => {} None => { @@ -197,7 +198,7 @@ impl CollabTitlebarItem { }; Stack::new() .with_child( - MouseEventHandler::::new(0, cx, |state, _| { + MouseEventHandler::::new(0, cx, |state, _| { let style = titlebar .toggle_contacts_button .style_for(state, self.contacts_popover.is_some()); @@ -214,8 +215,8 @@ impl CollabTitlebarItem { .boxed() }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(ToggleContactsPopover); + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleCollaborationMenu); }) .aligned() .boxed(), diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 4b7e3dae01..f5f508ce5b 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -8,7 +8,7 @@ mod notifications; mod project_shared_notification; use call::ActiveCall; -pub use collab_titlebar_item::CollabTitlebarItem; +pub use collab_titlebar_item::{CollabTitlebarItem, ToggleCollaborationMenu}; use gpui::MutableAppContext; use project::Project; use std::sync::Arc; diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 9a6bc9e6ac..be2b09baa9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1173,6 +1173,10 @@ impl Workspace { cx.notify(); } + pub fn titlebar_item(&self) -> Option { + self.titlebar_item.clone() + } + /// Call the given callback with a workspace whose project is local. /// /// If the given workspace has a local project, then it will be passed diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 9012bc89e2..ef0c84909a 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -9,7 +9,7 @@ use anyhow::{anyhow, Context, Result}; use assets::Assets; use breadcrumbs::Breadcrumbs; pub use client; -use collab_ui::CollabTitlebarItem; +use collab_ui::{CollabTitlebarItem, ToggleCollaborationMenu}; use collections::VecDeque; pub use editor; use editor::{Editor, MultiBuffer}; @@ -94,6 +94,22 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.toggle_full_screen(); }, ); + cx.add_action( + |workspace: &mut Workspace, + _: &ToggleCollaborationMenu, + cx: &mut ViewContext| { + if let Some(item) = workspace + .titlebar_item() + .and_then(|item| item.downcast::()) + { + cx.as_mut().defer(move |cx| { + item.update(cx, |item, cx| { + item.toggle_contacts_popover(&Default::default(), cx); + }); + }); + } + }, + ); cx.add_global_action(quit); cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url)); cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| { From 490a60866371d5970e788d5d85ea1c5a914cc60b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 14:18:40 -0700 Subject: [PATCH 21/22] v0.60.0 --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fedb6258f0..0b875441ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7386,7 +7386,7 @@ dependencies = [ [[package]] name = "zed" -version = "0.59.0" +version = "0.60.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 6c92dd9bef..5bfeb681a6 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.59.0" +version = "0.60.0" [lib] name = "zed" From fedec68d395fca6546c352099c614e7b74c6038e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 12 Oct 2022 14:25:40 -0700 Subject: [PATCH 22/22] Update Dockerfiles to use Rust 1.64 --- Dockerfile | 2 +- Dockerfile.migrator | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6bfd49be55..122600bf94 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.62-bullseye as builder +FROM rust:1.64-bullseye as builder WORKDIR app COPY . . diff --git a/Dockerfile.migrator b/Dockerfile.migrator index b6393ed1b5..482228a2eb 100644 --- a/Dockerfile.migrator +++ b/Dockerfile.migrator @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.2 -FROM rust:1.62-bullseye as builder +FROM rust:1.64-bullseye as builder WORKDIR app RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=./target \