diff --git a/.github/actions/run_tests_windows/action.yml b/.github/actions/run_tests_windows/action.yml new file mode 100644 index 0000000000..c4be7f6d6d --- /dev/null +++ b/.github/actions/run_tests_windows/action.yml @@ -0,0 +1,26 @@ +name: "Run tests on Windows" +description: "Runs the tests on Windows" + +inputs: + working-directory: + description: "The working directory" + required: true + default: "." + +runs: + using: "composite" + steps: + - name: Install Rust + shell: pwsh + working-directory: ${{ inputs.working-directory }} + run: cargo install cargo-nextest --locked + + - name: Install Node + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 + with: + node-version: "18" + + - name: Run tests + shell: pwsh + working-directory: ${{ inputs.working-directory }} + run: cargo nextest run --workspace --no-fail-fast diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c22474d45..f20495baeb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -228,7 +228,6 @@ jobs: if: always() run: rm -rf ./../.cargo - # todo(windows): Actually run the tests windows_tests: timeout-minutes: 60 name: (Windows) Run Clippy and tests @@ -269,10 +268,19 @@ jobs: # Windows can't run shell scripts, so we need to use `cargo xtask`. run: cargo xtask clippy + - name: Run tests + uses: ./.github/actions/run_tests_windows + with: + working-directory: ${{ env.ZED_WORKSPACE }} + - name: Build Zed working-directory: ${{ env.ZED_WORKSPACE }} run: cargo build + - name: Check dev drive space + working-directory: ${{ env.ZED_WORKSPACE }} + run: ./script/exit-ci-if-dev-drive-is-full.ps1 55 + # Since the Windows runners are stateful, so we need to remove the config file to prevent potential bug. - name: Clean CI config file if: always() diff --git a/Cargo.lock b/Cargo.lock index 90373a0bcf..5353b9bc44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14404,6 +14404,15 @@ dependencies = [ "tempfile", "tendril", "unicase", + "util_macros", +] + +[[package]] +name = "util_macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn 1.0.109", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9723d9beed..d70d7b2faf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,6 +144,7 @@ members = [ "crates/ui_input", "crates/ui_macros", "crates/util", + "crates/util_macros", "crates/vcs_menu", "crates/vim", "crates/vim_mode_setting", @@ -339,6 +340,7 @@ ui = { path = "crates/ui" } ui_input = { path = "crates/ui_input" } ui_macros = { path = "crates/ui_macros" } util = { path = "crates/util" } +util_macros = { path = "crates/util_macros" } vcs_menu = { path = "crates/vcs_menu" } vim = { path = "crates/vim" } vim_mode_setting = { path = "crates/vim_mode_setting" } @@ -359,7 +361,7 @@ alacritty_terminal = { git = "https://github.com/alacritty/alacritty.git", rev = any_vec = "0.14" anyhow = "1.0.86" arrayvec = { version = "0.7.4", features = ["serde"] } -ashpd = { version = "0.10", default-features = false, features = ["async-std"]} +ashpd = { version = "0.10", default-features = false, features = ["async-std"] } async-compat = "0.2.1" async-compression = { version = "0.4", features = ["gzip", "futures-io"] } async-dispatcher = "0.1" @@ -421,7 +423,11 @@ jupyter-websocket-client = { version = "0.9.0" } libc = "0.2" libsqlite3-sys = { version = "0.30.1", features = ["bundled"] } linkify = "0.10.0" -livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev="811ceae29fabee455f110c56cd66b3f49a7e5003", features = ["dispatcher", "services-dispatcher", "rustls-tls-native-roots"], default-features = false } +livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "811ceae29fabee455f110c56cd66b3f49a7e5003", features = [ + "dispatcher", + "services-dispatcher", + "rustls-tls-native-roots", +], default-features = false } log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] } markup5ever_rcdom = "0.3.0" nanoid = "0.4" @@ -441,11 +447,13 @@ pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git" pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "1abe5cec5ebfbe97ca71746a4cfc7fe89bddf8e0" } postage = { version = "0.5", features = ["futures-traits"] } pretty_assertions = { version = "1.3.0", features = ["unstable"] } +proc-macro2 = "1.0.93" profiling = "1" prost = "0.9" prost-build = "0.9" prost-types = "0.9" pulldown-cmark = { version = "0.12.0", default-features = false } +quote = "1.0.9" rand = "0.8.5" rayon = "1.8" regex = "1.5" @@ -489,6 +497,7 @@ sqlformat = "0.2" strsim = "0.11" strum = { version = "0.26.0", features = ["derive"] } subtle = "2.5.0" +syn = { version = "1.0.72", features = ["full", "extra-traits"] } sys-locale = "0.3.1" sysinfo = "0.31.0" take-until = "0.2.0" diff --git a/crates/assistant_slash_commands/src/file_command.rs b/crates/assistant_slash_commands/src/file_command.rs index d898d82bc3..71a7376845 100644 --- a/crates/assistant_slash_commands/src/file_command.rs +++ b/crates/assistant_slash_commands/src/file_command.rs @@ -323,7 +323,14 @@ fn collect_files( )))?; directory_stack.push(entry.path.clone()); } else { - let entry_name = format!("{}/{}", prefix_paths, &filename); + // todo(windows) + // Potential bug: this assumes that the path separator is always `\` on Windows + let entry_name = format!( + "{}{}{}", + prefix_paths, + std::path::MAIN_SEPARATOR_STR, + &filename + ); events_tx.unbounded_send(Ok(SlashCommandEvent::StartSection { icon: IconName::Folder, label: entry_name.clone().into(), @@ -455,6 +462,7 @@ mod custom_path_matcher { use std::{fmt::Debug as _, path::Path}; use globset::{Glob, GlobSet, GlobSetBuilder}; + use util::paths::SanitizedPath; #[derive(Clone, Debug, Default)] pub struct PathMatcher { @@ -481,7 +489,7 @@ mod custom_path_matcher { pub fn new(globs: &[String]) -> Result { let globs = globs .into_iter() - .map(|glob| Glob::new(&glob)) + .map(|glob| Glob::new(&SanitizedPath::from(glob).to_glob_string())) .collect::, _>>()?; let sources = globs.iter().map(|glob| glob.glob().to_owned()).collect(); let sources_with_trailing_slash = globs @@ -507,7 +515,9 @@ mod custom_path_matcher { .zip(self.sources_with_trailing_slash.iter()) .any(|(source, with_slash)| { let as_bytes = other_path.as_os_str().as_encoded_bytes(); - let with_slash = if source.ends_with("/") { + // todo(windows) + // Potential bug: this assumes that the path separator is always `\` on Windows + let with_slash = if source.ends_with(std::path::MAIN_SEPARATOR_STR) { source.as_bytes() } else { with_slash.as_bytes() @@ -569,6 +579,7 @@ mod test { use serde_json::json; use settings::SettingsStore; use smol::stream::StreamExt; + use util::{path, separator}; use super::collect_files; @@ -592,7 +603,7 @@ mod test { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/root", + path!("/root"), json!({ "dir": { "subdir": { @@ -607,7 +618,7 @@ mod test { ) .await; - let project = Project::test(fs, ["/root".as_ref()], cx).await; + let project = Project::test(fs, [path!("/root").as_ref()], cx).await; let result_1 = cx.update(|cx| collect_files(project.clone(), &["root/dir".to_string()], cx)); @@ -615,7 +626,7 @@ mod test { .await .unwrap(); - assert!(result_1.text.starts_with("root/dir")); + assert!(result_1.text.starts_with(separator!("root/dir"))); // 4 files + 2 directories assert_eq!(result_1.sections.len(), 6); @@ -631,7 +642,7 @@ mod test { cx.update(|cx| collect_files(project.clone(), &["root/dir*".to_string()], cx).boxed()); let result = SlashCommandOutput::from_event_stream(result).await.unwrap(); - assert!(result.text.starts_with("root/dir")); + assert!(result.text.starts_with(separator!("root/dir"))); // 5 files + 2 directories assert_eq!(result.sections.len(), 7); @@ -645,7 +656,7 @@ mod test { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/zed", + path!("/zed"), json!({ "assets": { "dir1": { @@ -670,7 +681,7 @@ mod test { ) .await; - let project = Project::test(fs, ["/zed".as_ref()], cx).await; + let project = Project::test(fs, [path!("/zed").as_ref()], cx).await; let result = cx.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx)); @@ -679,27 +690,36 @@ mod test { .unwrap(); // Sanity check - assert!(result.text.starts_with("zed/assets/themes\n")); + assert!(result.text.starts_with(separator!("zed/assets/themes\n"))); assert_eq!(result.sections.len(), 7); // Ensure that full file paths are included in the real output - assert!(result.text.contains("zed/assets/themes/andromeda/LICENSE")); - assert!(result.text.contains("zed/assets/themes/ayu/LICENSE")); - assert!(result.text.contains("zed/assets/themes/summercamp/LICENSE")); + assert!(result + .text + .contains(separator!("zed/assets/themes/andromeda/LICENSE"))); + assert!(result + .text + .contains(separator!("zed/assets/themes/ayu/LICENSE"))); + assert!(result + .text + .contains(separator!("zed/assets/themes/summercamp/LICENSE"))); assert_eq!(result.sections[5].label, "summercamp"); // Ensure that things are in descending order, with properly relativized paths assert_eq!( result.sections[0].label, - "zed/assets/themes/andromeda/LICENSE" + separator!("zed/assets/themes/andromeda/LICENSE") ); assert_eq!(result.sections[1].label, "andromeda"); - assert_eq!(result.sections[2].label, "zed/assets/themes/ayu/LICENSE"); + assert_eq!( + result.sections[2].label, + separator!("zed/assets/themes/ayu/LICENSE") + ); assert_eq!(result.sections[3].label, "ayu"); assert_eq!( result.sections[4].label, - "zed/assets/themes/summercamp/LICENSE" + separator!("zed/assets/themes/summercamp/LICENSE") ); // Ensure that the project lasts until after the last await @@ -712,7 +732,7 @@ mod test { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/zed", + path!("/zed"), json!({ "assets": { "themes": { @@ -732,7 +752,7 @@ mod test { ) .await; - let project = Project::test(fs, ["/zed".as_ref()], cx).await; + let project = Project::test(fs, [path!("/zed").as_ref()], cx).await; let result = cx.update(|cx| collect_files(project.clone(), &["zed/assets/themes".to_string()], cx)); @@ -740,26 +760,29 @@ mod test { .await .unwrap(); - assert!(result.text.starts_with("zed/assets/themes\n")); - assert_eq!(result.sections[0].label, "zed/assets/themes/LICENSE"); + assert!(result.text.starts_with(separator!("zed/assets/themes\n"))); + assert_eq!( + result.sections[0].label, + separator!("zed/assets/themes/LICENSE") + ); assert_eq!( result.sections[1].label, - "zed/assets/themes/summercamp/LICENSE" + separator!("zed/assets/themes/summercamp/LICENSE") ); assert_eq!( result.sections[2].label, - "zed/assets/themes/summercamp/subdir/LICENSE" + separator!("zed/assets/themes/summercamp/subdir/LICENSE") ); assert_eq!( result.sections[3].label, - "zed/assets/themes/summercamp/subdir/subsubdir/LICENSE" + separator!("zed/assets/themes/summercamp/subdir/subsubdir/LICENSE") ); assert_eq!(result.sections[4].label, "subsubdir"); assert_eq!(result.sections[5].label, "subdir"); assert_eq!(result.sections[6].label, "summercamp"); - assert_eq!(result.sections[7].label, "zed/assets/themes"); + assert_eq!(result.sections[7].label, separator!("zed/assets/themes")); - assert_eq!(result.text, "zed/assets/themes\n```zed/assets/themes/LICENSE\n1\n```\n\nsummercamp\n```zed/assets/themes/summercamp/LICENSE\n1\n```\n\nsubdir\n```zed/assets/themes/summercamp/subdir/LICENSE\n1\n```\n\nsubsubdir\n```zed/assets/themes/summercamp/subdir/subsubdir/LICENSE\n3\n```\n\n"); + assert_eq!(result.text, separator!("zed/assets/themes\n```zed/assets/themes/LICENSE\n1\n```\n\nsummercamp\n```zed/assets/themes/summercamp/LICENSE\n1\n```\n\nsubdir\n```zed/assets/themes/summercamp/subdir/LICENSE\n1\n```\n\nsubsubdir\n```zed/assets/themes/summercamp/subdir/subsubdir/LICENSE\n3\n```\n\n")); // Ensure that the project lasts until after the last await drop(project); diff --git a/crates/copilot/src/copilot.rs b/crates/copilot/src/copilot.rs index a95b2efeb0..6b65b8057c 100644 --- a/crates/copilot/src/copilot.rs +++ b/crates/copilot/src/copilot.rs @@ -1061,6 +1061,7 @@ async fn get_copilot_lsp(http: Arc) -> anyhow::Result { mod tests { use super::*; use gpui::TestAppContext; + use util::path; #[gpui::test(iterations = 10)] async fn test_buffer_management(cx: &mut TestAppContext) { @@ -1123,7 +1124,7 @@ mod tests { buffer_1.update(cx, |buffer, cx| { buffer.file_updated( Arc::new(File { - abs_path: "/root/child/buffer-1".into(), + abs_path: path!("/root/child/buffer-1").into(), path: Path::new("child/buffer-1").into(), }), cx, @@ -1136,7 +1137,7 @@ mod tests { text_document: lsp::TextDocumentIdentifier::new(buffer_1_uri), } ); - let buffer_1_uri = lsp::Url::from_file_path("/root/child/buffer-1").unwrap(); + let buffer_1_uri = lsp::Url::from_file_path(path!("/root/child/buffer-1")).unwrap(); assert_eq!( lsp.receive_notification::() .await, diff --git a/crates/copilot/src/copilot_completion_provider.rs b/crates/copilot/src/copilot_completion_provider.rs index dd1bf335ca..9c25e295aa 100644 --- a/crates/copilot/src/copilot_completion_provider.rs +++ b/crates/copilot/src/copilot_completion_provider.rs @@ -290,7 +290,10 @@ mod tests { use serde_json::json; use settings::SettingsStore; use std::future::Future; - use util::test::{marked_text_ranges_by, TextRangeMarker}; + use util::{ + path, + test::{marked_text_ranges_by, TextRangeMarker}, + }; #[gpui::test(iterations = 10)] async fn test_copilot(executor: BackgroundExecutor, cx: &mut TestAppContext) { @@ -949,24 +952,24 @@ mod tests { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/test", + path!("/test"), json!({ ".env": "SECRET=something\n", "README.md": "hello\nworld\nhow\nare\nyou\ntoday" }), ) .await; - let project = Project::test(fs, ["/test".as_ref()], cx).await; + let project = Project::test(fs, [path!("/test").as_ref()], cx).await; let private_buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/test/.env", cx) + project.open_local_buffer(path!("/test/.env"), cx) }) .await .unwrap(); let public_buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/test/README.md", cx) + project.open_local_buffer(path!("/test/README.md"), cx) }) .await .unwrap(); diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 4e3a7e6460..c933b04cf5 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -1433,7 +1433,10 @@ impl ToDisplayPoint for Anchor { #[cfg(test)] pub mod tests { use super::*; - use crate::{movement, test::marked_display_snapshot}; + use crate::{ + movement, + test::{marked_display_snapshot, test_font}, + }; use block_map::BlockPlacement; use gpui::{ div, font, observe, px, App, AppContext as _, BorrowAppContext, Element, Hsla, Rgba, @@ -1492,10 +1495,11 @@ pub mod tests { } }); + let font = test_font(); let map = cx.new(|cx| { DisplayMap::new( buffer.clone(), - font("Helvetica"), + font, font_size, wrap_width, true, diff --git a/crates/editor/src/display_map/block_map.rs b/crates/editor/src/display_map/block_map.rs index 7715471a1f..b4bf81846e 100644 --- a/crates/editor/src/display_map/block_map.rs +++ b/crates/editor/src/display_map/block_map.rs @@ -1992,8 +1992,9 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) { #[cfg(test)] mod tests { use super::*; - use crate::display_map::{ - fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap, + use crate::{ + display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap}, + test::test_font, }; use gpui::{div, font, px, App, AppContext as _, Element}; use itertools::Itertools; @@ -2227,7 +2228,7 @@ mod tests { multi_buffer }); - let font = font("Helvetica"); + let font = test_font(); let font_size = px(14.); let font_id = cx.text_system().resolve_font(&font); let mut wrap_width = px(0.); @@ -3069,8 +3070,9 @@ mod tests { let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone()); let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot); let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap()); - let (wrap_map, wraps_snapshot) = cx - .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx)); + let font = test_font(); + let (wrap_map, wraps_snapshot) = + cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx)); let mut block_map = BlockMap::new( wraps_snapshot, true, diff --git a/crates/editor/src/display_map/wrap_map.rs b/crates/editor/src/display_map/wrap_map.rs index 6b00ab7db0..77eab5881c 100644 --- a/crates/editor/src/display_map/wrap_map.rs +++ b/crates/editor/src/display_map/wrap_map.rs @@ -1169,9 +1169,10 @@ mod tests { use super::*; use crate::{ display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap}, + test::test_font, MultiBuffer, }; - use gpui::{font, px, test::observe}; + use gpui::{px, test::observe}; use rand::prelude::*; use settings::SettingsStore; use smol::stream::StreamExt; @@ -1196,7 +1197,8 @@ mod tests { Some(px(rng.gen_range(0.0..=1000.0))) }; let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap(); - let font = font("Helvetica"); + + let font = test_font(); let _font_id = text_system.font_id(&font); let font_size = px(14.0); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index fae15adf46..491510ed32 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -40,8 +40,9 @@ use std::{ use test::{build_editor_with_project, editor_lsp_test_context::rust_lang}; use unindent::Unindent; use util::{ - assert_set_eq, + assert_set_eq, path, test::{marked_text_ranges, marked_text_ranges_by, sample_text, TextRangeMarker}, + uri, }; use workspace::{ item::{FollowEvent, FollowableItem, Item, ItemHandle}, @@ -7074,9 +7075,9 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); let fs = FakeFs::new(cx.executor()); - fs.insert_file("/file.rs", Default::default()).await; + fs.insert_file(path!("/file.rs"), Default::default()).await; - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs, [path!("/file.rs").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -7092,7 +7093,9 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { ); let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/file.rs"), cx) + }) .await .unwrap(); @@ -7117,7 +7120,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { .handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); assert_eq!(params.options.tab_size, 4); Ok(Some(vec![lsp::TextEdit::new( @@ -7145,7 +7148,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { fake_server.handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); futures::future::pending::<()>().await; unreachable!() @@ -7202,7 +7205,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) { .handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); assert_eq!(params.options.tab_size, 8); Ok(Some(vec![])) @@ -7237,7 +7240,7 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": sample_text_1, "other.rs": sample_text_2, @@ -7246,7 +7249,7 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx); @@ -7421,20 +7424,20 @@ async fn test_multibuffer_format_during_save(cx: &mut gpui::TestAppContext) { assert!(cx.read(|cx| !multi_buffer_editor.is_dirty(cx))); assert_eq!( multi_buffer_editor.update(cx, |editor, cx| editor.text(cx)), - "a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}", + uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\n\nffff\ngggg\n\njjjj\n\nlll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|\nr\n\nuuuu\n\nvvvv\nwwww\nxxxx\n\n{{{{\n||||\n\n\u{7f}\u{7f}\u{7f}\u{7f}"), ); buffer_1.update(cx, |buffer, _| { assert!(!buffer.is_dirty()); assert_eq!( buffer.text(), - "a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n", + uri!("a|o[file:///a/main.rs formatted]bbbb\ncccc\ndddd\neeee\nffff\ngggg\nhhhh\niiii\njjjj\n"), ) }); buffer_2.update(cx, |buffer, _| { assert!(!buffer.is_dirty()); assert_eq!( buffer.text(), - "lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n", + uri!("lll[file:///a/other.rs formatted]mmmm\nnnnn|four|five|six|oooo\npppp\nr\nssss\ntttt\nuuuu\n"), ) }); buffer_3.update(cx, |buffer, _| { @@ -7448,9 +7451,9 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); let fs = FakeFs::new(cx.executor()); - fs.insert_file("/file.rs", Default::default()).await; + fs.insert_file(path!("/file.rs"), Default::default()).await; - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs, [path!("/").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -7466,7 +7469,9 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { ); let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/file.rs"), cx) + }) .await .unwrap(); @@ -7491,7 +7496,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { .handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); assert_eq!(params.options.tab_size, 4); Ok(Some(vec![lsp::TextEdit::new( @@ -7519,7 +7524,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); futures::future::pending::<()>().await; unreachable!() @@ -7577,7 +7582,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) { .handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); assert_eq!(params.options.tab_size, 8); Ok(Some(vec![])) @@ -7597,9 +7602,9 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { }); let fs = FakeFs::new(cx.executor()); - fs.insert_file("/file.rs", Default::default()).await; + fs.insert_file(path!("/file.rs"), Default::default()).await; - let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs, [path!("/").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(Arc::new(Language::new( @@ -7633,7 +7638,9 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { ); let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/file.rs"), cx) + }) .await .unwrap(); @@ -7663,7 +7670,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { .handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); assert_eq!(params.options.tab_size, 4); Ok(Some(vec![lsp::TextEdit::new( @@ -7687,7 +7694,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) { fake_server.handle_request::(move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/file.rs").unwrap() + lsp::Url::from_file_path(path!("/file.rs")).unwrap() ); futures::future::pending::<()>().await; unreachable!() @@ -8727,14 +8734,14 @@ async fn test_multiline_completion(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.ts": "a", }), ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let typescript_language = Arc::new(Language::new( LanguageConfig { @@ -8794,7 +8801,7 @@ async fn test_multiline_completion(cx: &mut gpui::TestAppContext) { .unwrap(); let _buffer = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/a/main.ts", cx) + project.open_local_buffer_with_lsp(path!("/a/main.ts"), cx) }) .await .unwrap(); @@ -10570,7 +10577,7 @@ async fn go_to_prev_overlapping_diagnostic( .update_diagnostics( LanguageServerId(0), lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/root/file").unwrap(), + uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(), version: None, diagnostics: vec![ lsp::Diagnostic { @@ -10663,7 +10670,7 @@ async fn test_diagnostics_with_links(cx: &mut TestAppContext) { lsp_store.update_diagnostics( LanguageServerId(0), lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/root/file").unwrap(), + uri: lsp::Url::from_file_path(path!("/root/file")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 12)), @@ -10923,14 +10930,14 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": "fn main() { let a = 5; }", "other.rs": "// Test file", }), ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(Arc::new(Language::new( @@ -10982,7 +10989,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) { let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/main.rs", cx) + project.open_local_buffer(path!("/a/main.rs"), cx) }) .await .unwrap(); @@ -11002,7 +11009,7 @@ async fn test_on_type_formatting_not_triggered(cx: &mut gpui::TestAppContext) { fake_server.handle_request::(|params, _| async move { assert_eq!( params.text_document_position.text_document.uri, - lsp::Url::from_file_path("/a/main.rs").unwrap(), + lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(), ); assert_eq!( params.text_document_position.position, @@ -11040,7 +11047,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": "fn main() { let a = 5; }", "other.rs": "// Test file", @@ -11048,7 +11055,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let server_restarts = Arc::new(AtomicUsize::new(0)); let closure_restarts = Arc::clone(&server_restarts); @@ -11088,7 +11095,7 @@ async fn test_language_server_restart_due_to_settings_change(cx: &mut gpui::Test let _window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let _buffer = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/a/main.rs", cx) + project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx) }) .await .unwrap(); @@ -11861,9 +11868,9 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { }); let fs = FakeFs::new(cx.executor()); - fs.insert_file("/file.ts", Default::default()).await; + fs.insert_file(path!("/file.ts"), Default::default()).await; - let project = Project::test(fs, ["/file.ts".as_ref()], cx).await; + let project = Project::test(fs, [path!("/file.ts").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(Arc::new(Language::new( @@ -11895,7 +11902,9 @@ async fn test_document_format_with_prettier(cx: &mut gpui::TestAppContext) { let prettier_format_suffix = project::TEST_PRETTIER_FORMAT_SUFFIX; let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/file.ts", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/file.ts"), cx) + }) .await .unwrap(); diff --git a/crates/editor/src/git/blame.rs b/crates/editor/src/git/blame.rs index d9c4926d33..767c1eabb9 100644 --- a/crates/editor/src/git/blame.rs +++ b/crates/editor/src/git/blame.rs @@ -560,7 +560,7 @@ mod tests { use settings::SettingsStore; use std::{cmp, env, ops::Range, path::Path}; use unindent::Unindent as _; - use util::RandomCharIter; + use util::{path, RandomCharIter}; // macro_rules! assert_blame_rows { // ($blame:expr, $rows:expr, $expected:expr, $cx:expr) => { @@ -793,7 +793,7 @@ mod tests { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/my-repo", + path!("/my-repo"), json!({ ".git": {}, "file.txt": r#" @@ -807,7 +807,7 @@ mod tests { .await; fs.set_blame_for_repo( - Path::new("/my-repo/.git"), + Path::new(path!("/my-repo/.git")), vec![( "file.txt".into(), Blame { @@ -817,10 +817,10 @@ mod tests { )], ); - let project = Project::test(fs, ["/my-repo".as_ref()], cx).await; + let project = Project::test(fs, [path!("/my-repo").as_ref()], cx).await; let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/my-repo/file.txt", cx) + project.open_local_buffer(path!("/my-repo/file.txt"), cx) }) .await .unwrap(); @@ -945,7 +945,7 @@ mod tests { log::info!("initial buffer text: {:?}", buffer_initial_text); fs.insert_tree( - "/my-repo", + path!("/my-repo"), json!({ ".git": {}, "file.txt": buffer_initial_text.to_string() @@ -956,7 +956,7 @@ mod tests { let blame_entries = gen_blame_entries(buffer_initial_text.max_point().row, &mut rng); log::info!("initial blame entries: {:?}", blame_entries); fs.set_blame_for_repo( - Path::new("/my-repo/.git"), + Path::new(path!("/my-repo/.git")), vec![( "file.txt".into(), Blame { @@ -966,10 +966,10 @@ mod tests { )], ); - let project = Project::test(fs.clone(), ["/my-repo".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/my-repo").as_ref()], cx).await; let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/my-repo/file.txt", cx) + project.open_local_buffer(path!("/my-repo/file.txt"), cx) }) .await .unwrap(); @@ -998,7 +998,7 @@ mod tests { log::info!("regenerating blame entries: {:?}", blame_entries); fs.set_blame_for_repo( - Path::new("/my-repo/.git"), + Path::new(path!("/my-repo/.git")), vec![( "file.txt".into(), Blame { diff --git a/crates/editor/src/hover_links.rs b/crates/editor/src/hover_links.rs index 0442669e5e..b0e4abcb32 100644 --- a/crates/editor/src/hover_links.rs +++ b/crates/editor/src/hover_links.rs @@ -921,7 +921,7 @@ mod tests { use indoc::indoc; use language::language_settings::InlayHintSettings; use lsp::request::{GotoDefinition, GotoTypeDefinition}; - use util::assert_set_eq; + use util::{assert_set_eq, path}; use workspace::item::Item; #[gpui::test] @@ -1574,18 +1574,31 @@ mod tests { // Insert a new file let fs = cx.update_workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone()); fs.as_fake() - .insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec()) + .insert_file( + path!("/root/dir/file2.rs"), + "This is file2.rs".as_bytes().to_vec(), + ) .await; + #[cfg(not(target_os = "windows"))] cx.set_state(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. Or go to ../dir/file2.rs if you want. Or go to /root/dir/file2.rs if project is local. Or go to /root/dir/file2 if this is a Rust file.ˇ + "}); + #[cfg(target_os = "windows")] + cx.set_state(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file.ˇ "}); // File does not exist + #[cfg(not(target_os = "windows"))] let screen_coord = cx.pixel_position(indoc! {" You can't go to a file that dˇoes_not_exist.txt. Go to file2.rs if you want. @@ -1593,6 +1606,14 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + let screen_coord = cx.pixel_position(indoc! {" + You can't go to a file that dˇoes_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key()); // No highlight cx.update_editor(|editor, window, cx| { @@ -1605,6 +1626,7 @@ mod tests { }); // Moving the mouse over a file that does exist should highlight it. + #[cfg(not(target_os = "windows"))] let screen_coord = cx.pixel_position(indoc! {" You can't go to a file that does_not_exist.txt. Go to fˇile2.rs if you want. @@ -1612,8 +1634,17 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + let screen_coord = cx.pixel_position(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to fˇile2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key()); + #[cfg(not(target_os = "windows"))] cx.assert_editor_text_highlights::(indoc! {" You can't go to a file that does_not_exist.txt. Go to «file2.rsˇ» if you want. @@ -1621,8 +1652,17 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + cx.assert_editor_text_highlights::(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to «file2.rsˇ» if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); // Moving the mouse over a relative path that does exist should highlight it + #[cfg(not(target_os = "windows"))] let screen_coord = cx.pixel_position(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. @@ -1630,8 +1670,17 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + let screen_coord = cx.pixel_position(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/fˇile2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key()); + #[cfg(not(target_os = "windows"))] cx.assert_editor_text_highlights::(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. @@ -1639,8 +1688,17 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + cx.assert_editor_text_highlights::(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to «../dir/file2.rsˇ» if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); // Moving the mouse over an absolute path that does exist should highlight it + #[cfg(not(target_os = "windows"))] let screen_coord = cx.pixel_position(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. @@ -1649,7 +1707,17 @@ mod tests { Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + let screen_coord = cx.pixel_position(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/diˇr/file2.rs if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); + cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key()); + #[cfg(not(target_os = "windows"))] cx.assert_editor_text_highlights::(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. @@ -1657,8 +1725,17 @@ mod tests { Or go to «/root/dir/file2.rsˇ» if project is local. Or go to /root/dir/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + cx.assert_editor_text_highlights::(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to «C:/root/dir/file2.rsˇ» if project is local. + Or go to C:/root/dir/file2 if this is a Rust file. + "}); // Moving the mouse over a path that exists, if we add the language-specific suffix, it should highlight it + #[cfg(not(target_os = "windows"))] let screen_coord = cx.pixel_position(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. @@ -1666,8 +1743,17 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to /root/diˇr/file2 if this is a Rust file. "}); + #[cfg(target_os = "windows")] + let screen_coord = cx.pixel_position(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to C:/root/diˇr/file2 if this is a Rust file. + "}); cx.simulate_mouse_move(screen_coord, None, Modifiers::secondary_key()); + #[cfg(not(target_os = "windows"))] cx.assert_editor_text_highlights::(indoc! {" You can't go to a file that does_not_exist.txt. Go to file2.rs if you want. @@ -1675,6 +1761,14 @@ mod tests { Or go to /root/dir/file2.rs if project is local. Or go to «/root/dir/file2ˇ» if this is a Rust file. "}); + #[cfg(target_os = "windows")] + cx.assert_editor_text_highlights::(indoc! {" + You can't go to a file that does_not_exist.txt. + Go to file2.rs if you want. + Or go to ../dir/file2.rs if you want. + Or go to C:/root/dir/file2.rs if project is local. + Or go to «C:/root/dir/file2ˇ» if this is a Rust file. + "}); cx.simulate_click(screen_coord, Modifiers::secondary_key()); @@ -1692,7 +1786,10 @@ mod tests { let file = buffer.read(cx).file().unwrap(); let file_path = file.as_local().unwrap().abs_path(cx); - assert_eq!(file_path.to_str().unwrap(), "/root/dir/file2.rs"); + assert_eq!( + file_path, + std::path::PathBuf::from(path!("/root/dir/file2.rs")) + ); }); } diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 3b325a5802..e6789c3a3a 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -1274,6 +1274,7 @@ pub mod tests { use settings::SettingsStore; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; use text::Point; + use util::path; use super::*; @@ -1499,7 +1500,7 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out", "other.md": "Test md file with some text", @@ -1507,7 +1508,7 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let mut rs_fake_servers = None; @@ -1542,14 +1543,16 @@ pub mod tests { "Rust" => { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/a/main.rs").unwrap(), + lsp::Url::from_file_path(path!("/a/main.rs")) + .unwrap(), ); rs_lsp_request_count.fetch_add(1, Ordering::Release) + 1 } "Markdown" => { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/a/other.md").unwrap(), + lsp::Url::from_file_path(path!("/a/other.md")) + .unwrap(), ); md_lsp_request_count.fetch_add(1, Ordering::Release) + 1 } @@ -1585,7 +1588,7 @@ pub mod tests { let rs_buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/main.rs", cx) + project.open_local_buffer(path!("/a/main.rs"), cx) }) .await .unwrap(); @@ -1611,7 +1614,7 @@ pub mod tests { cx.executor().run_until_parked(); let md_buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/other.md", cx) + project.open_local_buffer(path!("/a/other.md"), cx) }) .await .unwrap(); @@ -2173,7 +2176,7 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)), "other.rs": "// Test file", @@ -2181,7 +2184,7 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -2209,7 +2212,7 @@ pub mod tests { async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/a/main.rs").unwrap(), + lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(), ); task_lsp_request_ranges.lock().push(params.range); @@ -2237,7 +2240,7 @@ pub mod tests { let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/main.rs", cx) + project.open_local_buffer(path!("/a/main.rs"), cx) }) .await .unwrap(); @@ -2471,7 +2474,7 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::>().join("")), "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::>().join("")), @@ -2479,7 +2482,7 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let language = rust_lang(); @@ -2497,13 +2500,13 @@ pub mod tests { let (buffer_1, _handle1) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/a/main.rs", cx) + project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx) }) .await .unwrap(); let (buffer_2, _handle2) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/a/other.rs", cx) + project.open_local_buffer_with_lsp(path!("/a/other.rs"), cx) }) .await .unwrap(); @@ -2585,11 +2588,11 @@ pub mod tests { let task_editor_edited = Arc::clone(&closure_editor_edited); async move { let hint_text = if params.text_document.uri - == lsp::Url::from_file_path("/a/main.rs").unwrap() + == lsp::Url::from_file_path(path!("/a/main.rs")).unwrap() { "main hint" } else if params.text_document.uri - == lsp::Url::from_file_path("/a/other.rs").unwrap() + == lsp::Url::from_file_path(path!("/a/other.rs")).unwrap() { "other hint" } else { @@ -2815,7 +2818,7 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::>().join("")), "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::>().join("")), @@ -2823,7 +2826,7 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -2840,13 +2843,13 @@ pub mod tests { let (buffer_1, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/a/main.rs", cx) + project.open_local_buffer_with_lsp(path!("/a/main.rs"), cx) }) .await .unwrap(); let (buffer_2, _handle2) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/a/other.rs", cx) + project.open_local_buffer_with_lsp(path!("/a/other.rs"), cx) }) .await .unwrap(); @@ -2886,11 +2889,11 @@ pub mod tests { let task_editor_edited = Arc::clone(&closure_editor_edited); async move { let hint_text = if params.text_document.uri - == lsp::Url::from_file_path("/a/main.rs").unwrap() + == lsp::Url::from_file_path(path!("/a/main.rs")).unwrap() { "main hint" } else if params.text_document.uri - == lsp::Url::from_file_path("/a/other.rs").unwrap() + == lsp::Url::from_file_path(path!("/a/other.rs")).unwrap() { "other hint" } else { @@ -3027,7 +3030,7 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)), "other.rs": "// Test file", @@ -3035,7 +3038,7 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -3054,7 +3057,7 @@ pub mod tests { async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/a/main.rs").unwrap(), + lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(), ); let query_start = params.range.start; Ok(Some(vec![lsp::InlayHint { @@ -3077,7 +3080,7 @@ pub mod tests { let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/main.rs", cx) + project.open_local_buffer(path!("/a/main.rs"), cx) }) .await .unwrap(); @@ -3250,7 +3253,7 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": "fn main() { let x = 42; @@ -3265,7 +3268,7 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -3281,7 +3284,7 @@ pub mod tests { move |params, _| async move { assert_eq!( params.text_document.uri, - lsp::Url::from_file_path("/a/main.rs").unwrap(), + lsp::Url::from_file_path(path!("/a/main.rs")).unwrap(), ); Ok(Some( serde_json::from_value(json!([ @@ -3351,7 +3354,7 @@ pub mod tests { let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/main.rs", cx) + project.open_local_buffer(path!("/a/main.rs"), cx) }) .await .unwrap(); @@ -3408,7 +3411,7 @@ pub mod tests { ) -> (&'static str, WindowHandle, FakeLanguageServer) { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/a", + path!("/a"), json!({ "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out", "other.rs": "// Test file", @@ -3416,8 +3419,8 @@ pub mod tests { ) .await; - let project = Project::test(fs, ["/a".as_ref()], cx).await; - let file_path = "/a/main.rs"; + let project = Project::test(fs, [path!("/a").as_ref()], cx).await; + let file_path = path!("/a/main.rs"); let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -3435,7 +3438,7 @@ pub mod tests { let buffer = project .update(cx, |project, cx| { - project.open_local_buffer("/a/main.rs", cx) + project.open_local_buffer(path!("/a/main.rs"), cx) }) .await .unwrap(); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 2679df6a7c..d53b95b007 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -1717,6 +1717,7 @@ mod tests { use language::{LanguageMatcher, TestFile}; use project::FakeFs; use std::path::{Path, PathBuf}; + use util::path; #[gpui::test] fn test_path_for_file(cx: &mut App) { @@ -1771,24 +1772,24 @@ mod tests { init_test(cx, |_| {}); let fs = FakeFs::new(cx.executor()); - fs.insert_file("/file.rs", Default::default()).await; + fs.insert_file(path!("/file.rs"), Default::default()).await; // Test case 1: Deserialize with path and contents { - let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/file.rs").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); let workspace_id = workspace::WORKSPACE_DB.next_id().await.unwrap(); let item_id = 1234 as ItemId; let mtime = fs - .metadata(Path::new("/file.rs")) + .metadata(Path::new(path!("/file.rs"))) .await .unwrap() .unwrap() .mtime; let serialized_editor = SerializedEditor { - abs_path: Some(PathBuf::from("/file.rs")), + abs_path: Some(PathBuf::from(path!("/file.rs"))), contents: Some("fn main() {}".to_string()), language: Some("Rust".to_string()), mtime: Some(mtime), @@ -1812,7 +1813,7 @@ mod tests { // Test case 2: Deserialize with only path { - let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/file.rs").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -1820,7 +1821,7 @@ mod tests { let item_id = 5678 as ItemId; let serialized_editor = SerializedEditor { - abs_path: Some(PathBuf::from("/file.rs")), + abs_path: Some(PathBuf::from(path!("/file.rs"))), contents: None, language: None, mtime: None, @@ -1845,7 +1846,7 @@ mod tests { // Test case 3: Deserialize with no path (untitled buffer, with content and language) { - let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/file.rs").as_ref()], cx).await; // Add Rust to the language, so that we can restore the language of the buffer project.update(cx, |project, _| project.languages().add(rust_language())); @@ -1884,7 +1885,7 @@ mod tests { // Test case 4: Deserialize with path, content, and old mtime { - let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/file.rs").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -1893,7 +1894,7 @@ mod tests { let item_id = 9345 as ItemId; let old_mtime = MTime::from_seconds_and_nanos(0, 50); let serialized_editor = SerializedEditor { - abs_path: Some(PathBuf::from("/file.rs")), + abs_path: Some(PathBuf::from(path!("/file.rs"))), contents: Some("fn main() {}".to_string()), language: Some("Rust".to_string()), mtime: Some(old_mtime), diff --git a/crates/editor/src/test.rs b/crates/editor/src/test.rs index 6b8451cba1..35fb1b4c91 100644 --- a/crates/editor/src/test.rs +++ b/crates/editor/src/test.rs @@ -1,12 +1,15 @@ pub mod editor_lsp_test_context; pub mod editor_test_context; +use std::sync::LazyLock; + use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, DisplayPoint, Editor, EditorMode, FoldPlaceholder, MultiBuffer, }; use gpui::{ - AppContext as _, Context, Entity, Font, FontFeatures, FontStyle, FontWeight, Pixels, Window, + font, AppContext as _, Context, Entity, Font, FontFeatures, FontStyle, FontWeight, Pixels, + Window, }; use project::Project; use util::test::{marked_text_offsets, marked_text_ranges}; @@ -19,6 +22,22 @@ fn init_logger() { } } +pub fn test_font() -> Font { + static TEST_FONT: LazyLock = LazyLock::new(|| { + #[cfg(not(target_os = "windows"))] + { + font("Helvetica") + } + + #[cfg(target_os = "windows")] + { + font("Courier New") + } + }); + + TEST_FONT.clone() +} + // Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one. pub fn marked_display_snapshot( text: &str, diff --git a/crates/extension_host/src/extension_store_test.rs b/crates/extension_host/src/extension_store_test.rs index e65678bcc8..137e3f80d9 100644 --- a/crates/extension_host/src/extension_store_test.rs +++ b/crates/extension_host/src/extension_store_test.rs @@ -455,7 +455,12 @@ async fn test_extension_store(cx: &mut TestAppContext) { }); } +// todo(windows) +// Disable this test on Windows for now. Because this test hangs at +// `let fake_server = fake_servers.next().await.unwrap();`. +// Reenable this test when we figure out why. #[gpui::test] +#[cfg_attr(target_os = "windows", ignore)] async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { init_test(cx); cx.executor().allow_parking(); @@ -634,6 +639,8 @@ async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { .await .unwrap(); + // todo(windows) + // This test hangs here on Windows. let fake_server = fake_servers.next().await.unwrap(); let expected_server_path = extensions_dir.join(format!("work/{test_extension_id}/gleam-v1.2.3/gleam")); diff --git a/crates/file_finder/src/file_finder_tests.rs b/crates/file_finder/src/file_finder_tests.rs index b25ed8b9c1..8555da775e 100644 --- a/crates/file_finder/src/file_finder_tests.rs +++ b/crates/file_finder/src/file_finder_tests.rs @@ -6,6 +6,7 @@ use gpui::{Entity, TestAppContext, VisualTestContext}; use menu::{Confirm, SelectNext, SelectPrev}; use project::{RemoveOptions, FS_WATCH_LATENCY}; use serde_json::json; +use util::path; use workspace::{AppState, ToggleFileFinder, Workspace}; #[ctor::ctor] @@ -90,7 +91,7 @@ async fn test_absolute_paths(cx: &mut TestAppContext) { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "a": { "file1.txt": "", @@ -102,16 +103,16 @@ async fn test_absolute_paths(cx: &mut TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (picker, workspace, cx) = build_find_picker(project, cx); - let matching_abs_path = "/root/a/b/file2.txt"; + let matching_abs_path = path!("/root/a/b/file2.txt").to_string(); picker .update_in(cx, |picker, window, cx| { picker .delegate - .update_matches(matching_abs_path.to_string(), window, cx) + .update_matches(matching_abs_path, window, cx) }) .await; picker.update(cx, |picker, _| { @@ -128,12 +129,12 @@ async fn test_absolute_paths(cx: &mut TestAppContext) { assert_eq!(active_editor.read(cx).title(cx), "file2.txt"); }); - let mismatching_abs_path = "/root/a/b/file1.txt"; + let mismatching_abs_path = path!("/root/a/b/file1.txt").to_string(); picker .update_in(cx, |picker, window, cx| { picker .delegate - .update_matches(mismatching_abs_path.to_string(), window, cx) + .update_matches(mismatching_abs_path, window, cx) }) .await; picker.update(cx, |picker, _| { @@ -518,7 +519,7 @@ async fn test_path_distance_ordering(cx: &mut TestAppContext) { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "dir1": { "a.txt": "" }, "dir2": { @@ -529,7 +530,7 @@ async fn test_path_distance_ordering(cx: &mut TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); let worktree_id = cx.read(|cx| { @@ -606,7 +607,7 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -617,7 +618,7 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); @@ -648,7 +649,7 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/first.rs")), }, - Some(PathBuf::from("/src/test/first.rs")) + Some(PathBuf::from(path!("/src/test/first.rs"))) )], "Should show 1st opened item in the history when opening the 2nd item" ); @@ -663,14 +664,14 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/second.rs")), }, - Some(PathBuf::from("/src/test/second.rs")) + Some(PathBuf::from(path!("/src/test/second.rs"))) ), FoundPath::new( ProjectPath { worktree_id, path: Arc::from(Path::new("test/first.rs")), }, - Some(PathBuf::from("/src/test/first.rs")) + Some(PathBuf::from(path!("/src/test/first.rs"))) ), ], "Should show 1st and 2nd opened items in the history when opening the 3rd item. \ @@ -687,21 +688,21 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/third.rs")), }, - Some(PathBuf::from("/src/test/third.rs")) + Some(PathBuf::from(path!("/src/test/third.rs"))) ), FoundPath::new( ProjectPath { worktree_id, path: Arc::from(Path::new("test/second.rs")), }, - Some(PathBuf::from("/src/test/second.rs")) + Some(PathBuf::from(path!("/src/test/second.rs"))) ), FoundPath::new( ProjectPath { worktree_id, path: Arc::from(Path::new("test/first.rs")), }, - Some(PathBuf::from("/src/test/first.rs")) + Some(PathBuf::from(path!("/src/test/first.rs"))) ), ], "Should show 1st, 2nd and 3rd opened items in the history when opening the 2nd item again. \ @@ -718,21 +719,21 @@ async fn test_query_history(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/second.rs")), }, - Some(PathBuf::from("/src/test/second.rs")) + Some(PathBuf::from(path!("/src/test/second.rs"))) ), FoundPath::new( ProjectPath { worktree_id, path: Arc::from(Path::new("test/third.rs")), }, - Some(PathBuf::from("/src/test/third.rs")) + Some(PathBuf::from(path!("/src/test/third.rs"))) ), FoundPath::new( ProjectPath { worktree_id, path: Arc::from(Path::new("test/first.rs")), }, - Some(PathBuf::from("/src/test/first.rs")) + Some(PathBuf::from(path!("/src/test/first.rs"))) ), ], "Should show 1st, 2nd and 3rd opened items in the history when opening the 3rd item again. \ @@ -748,7 +749,7 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -762,7 +763,7 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/external-src", + path!("/external-src"), json!({ "test": { "third.rs": "// Third Rust file", @@ -772,10 +773,10 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; cx.update(|cx| { project.update(cx, |project, cx| { - project.find_or_create_worktree("/external-src", false, cx) + project.find_or_create_worktree(path!("/external-src"), false, cx) }) }) .detach(); @@ -791,7 +792,7 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) { workspace .update_in(cx, |workspace, window, cx| { workspace.open_abs_path( - PathBuf::from("/external-src/test/third.rs"), + PathBuf::from(path!("/external-src/test/third.rs")), false, window, cx, @@ -827,7 +828,7 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) { worktree_id: external_worktree_id, path: Arc::from(Path::new("")), }, - Some(PathBuf::from("/external-src/test/third.rs")) + Some(PathBuf::from(path!("/external-src/test/third.rs"))) )], "Should show external file with its full path in the history after it was open" ); @@ -842,14 +843,14 @@ async fn test_external_files_history(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/second.rs")), }, - Some(PathBuf::from("/src/test/second.rs")) + Some(PathBuf::from(path!("/src/test/second.rs"))) ), FoundPath::new( ProjectPath { worktree_id: external_worktree_id, path: Arc::from(Path::new("")), }, - Some(PathBuf::from("/external-src/test/third.rs")) + Some(PathBuf::from(path!("/external-src/test/third.rs"))) ), ], "Should keep external file with history updates", @@ -864,7 +865,7 @@ async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -875,7 +876,7 @@ async fn test_toggle_panel_new_selections(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); // generate some history to select from @@ -919,7 +920,7 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -931,7 +932,7 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); let worktree_id = cx.read(|cx| { let worktrees = workspace.read(cx).worktrees(cx).collect::>(); @@ -964,7 +965,7 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/first.rs")), }, - Some(PathBuf::from("/src/test/first.rs")) + Some(PathBuf::from(path!("/src/test/first.rs"))) )); assert_eq!(matches.search.len(), 1, "Only one non-history item contains {first_query}, it should be present"); assert_eq!(matches.search.first().unwrap(), Path::new("test/fourth.rs")); @@ -1007,7 +1008,7 @@ async fn test_search_preserves_history_items(cx: &mut gpui::TestAppContext) { worktree_id, path: Arc::from(Path::new("test/first.rs")), }, - Some(PathBuf::from("/src/test/first.rs")) + Some(PathBuf::from(path!("/src/test/first.rs"))) )); assert_eq!(matches.search.len(), 1, "Only one non-history item contains {first_query_again}, it should be present, even after non-matching query"); assert_eq!(matches.search.first().unwrap(), Path::new("test/fourth.rs")); @@ -1022,7 +1023,7 @@ async fn test_search_sorts_history_items(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "test": { "1_qw": "// First file that matches the query", @@ -1037,7 +1038,7 @@ async fn test_search_sorts_history_items(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); // generate some history to select from open_close_queried_buffer("1", 1, "1_qw", &workspace, cx).await; @@ -1079,7 +1080,7 @@ async fn test_select_current_open_file_when_no_history(cx: &mut gpui::TestAppCon .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "test": { "1_qw": "", @@ -1088,7 +1089,7 @@ async fn test_select_current_open_file_when_no_history(cx: &mut gpui::TestAppCon ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); // Open new buffer open_queried_buffer("1", 1, "1_qw", &workspace, cx).await; @@ -1109,7 +1110,7 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one( .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "bar.rs": "// Bar file", @@ -1122,7 +1123,7 @@ async fn test_keep_opened_file_on_top_of_search_results_and_select_next_one( ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_close_queried_buffer("bar", 1, "bar.rs", &workspace, cx).await; @@ -1202,7 +1203,7 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) { .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "bar.rs": "// Bar file", @@ -1215,7 +1216,7 @@ async fn test_non_separate_history_items(cx: &mut TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_close_queried_buffer("bar", 1, "bar.rs", &workspace, cx).await; @@ -1296,7 +1297,7 @@ async fn test_history_items_shown_in_order_of_open(cx: &mut TestAppContext) { .fs .as_fake() .insert_tree( - "/test", + path!("/test"), json!({ "test": { "1.txt": "// One", @@ -1307,7 +1308,7 @@ async fn test_history_items_shown_in_order_of_open(cx: &mut TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/test").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; @@ -1354,7 +1355,7 @@ async fn test_selected_history_item_stays_selected_on_worktree_updated(cx: &mut .fs .as_fake() .insert_tree( - "/test", + path!("/test"), json!({ "test": { "1.txt": "// One", @@ -1365,7 +1366,7 @@ async fn test_selected_history_item_stays_selected_on_worktree_updated(cx: &mut ) .await; - let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/test").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_close_queried_buffer("1", 1, "1.txt", &workspace, cx).await; @@ -1384,7 +1385,11 @@ async fn test_selected_history_item_stays_selected_on_worktree_updated(cx: &mut // Add more files to the worktree to trigger update matches for i in 0..5 { - let filename = format!("/test/{}.txt", 4 + i); + let filename = if cfg!(windows) { + format!("C:/test/{}.txt", 4 + i) + } else { + format!("/test/{}.txt", 4 + i) + }; app_state .fs .create_file(Path::new(&filename), Default::default()) @@ -1410,7 +1415,7 @@ async fn test_history_items_vs_very_good_external_match(cx: &mut gpui::TestAppCo .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "collab_ui": { "first.rs": "// First Rust file", @@ -1422,7 +1427,7 @@ async fn test_history_items_vs_very_good_external_match(cx: &mut gpui::TestAppCo ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); // generate some history to select from open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; @@ -1456,7 +1461,7 @@ async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext) .fs .as_fake() .insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -1467,7 +1472,7 @@ async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext) ) .await; - let project = Project::test(app_state.fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/src").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); // generate some history to select from open_close_queried_buffer("fir", 1, "first.rs", &workspace, cx).await; open_close_queried_buffer("non", 1, "nonexistent.rs", &workspace, cx).await; @@ -1476,7 +1481,7 @@ async fn test_nonexistent_history_items_not_shown(cx: &mut gpui::TestAppContext) app_state .fs .remove_file( - Path::new("/src/test/nonexistent.rs"), + Path::new(path!("/src/test/nonexistent.rs")), RemoveOptions::default(), ) .await @@ -1742,14 +1747,14 @@ async fn test_keeps_file_finder_open_after_modifier_keys_release(cx: &mut gpui:: .fs .as_fake() .insert_tree( - "/test", + path!("/test"), json!({ "1.txt": "// One", }), ) .await; - let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/test").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; @@ -1809,7 +1814,7 @@ async fn test_switches_between_release_norelease_modes_on_forward_nav( .fs .as_fake() .insert_tree( - "/test", + path!("/test"), json!({ "1.txt": "// One", "2.txt": "// Two", @@ -1817,7 +1822,7 @@ async fn test_switches_between_release_norelease_modes_on_forward_nav( ) .await; - let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/test").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; @@ -1864,7 +1869,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav( .fs .as_fake() .insert_tree( - "/test", + path!("/test"), json!({ "1.txt": "// One", "2.txt": "// Two", @@ -1873,7 +1878,7 @@ async fn test_switches_between_release_norelease_modes_on_backward_nav( ) .await; - let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/test").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; @@ -1921,14 +1926,14 @@ async fn test_extending_modifiers_does_not_confirm_selection(cx: &mut gpui::Test .fs .as_fake() .insert_tree( - "/test", + path!("/test"), json!({ "1.txt": "// One", }), ) .await; - let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/test").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); open_queried_buffer("1", 1, "1.txt", &workspace, cx).await; diff --git a/crates/fuzzy/src/matcher.rs b/crates/fuzzy/src/matcher.rs index 1b039c16f5..66a480d87a 100644 --- a/crates/fuzzy/src/matcher.rs +++ b/crates/fuzzy/src/matcher.rs @@ -9,6 +9,8 @@ const BASE_DISTANCE_PENALTY: f64 = 0.6; const ADDITIONAL_DISTANCE_PENALTY: f64 = 0.05; const MIN_DISTANCE_PENALTY: f64 = 0.2; +// TODO: +// Use `Path` instead of `&str` for paths. pub struct Matcher<'a> { query: &'a [char], lowercase_query: &'a [char], @@ -173,6 +175,8 @@ impl<'a> Matcher<'a> { path_idx: usize, cur_score: f64, ) -> f64 { + use std::path::MAIN_SEPARATOR; + if query_idx == self.query.len() { return 1.0; } @@ -196,13 +200,19 @@ impl<'a> Matcher<'a> { } else { path_cased[j - prefix.len()] }; - let is_path_sep = path_char == '/' || path_char == '\\'; + let is_path_sep = path_char == MAIN_SEPARATOR; if query_idx == 0 && is_path_sep { last_slash = j; } - if query_char == path_char || (is_path_sep && query_char == '_' || query_char == '\\') { + #[cfg(not(target_os = "windows"))] + let need_to_score = + query_char == path_char || (is_path_sep && query_char == '_' || query_char == '\\'); + // `query_char == '\\'` breaks `test_match_path_entries` on Windows, `\` is only used as a path separator on Windows. + #[cfg(target_os = "windows")] + let need_to_score = query_char == path_char || (is_path_sep && query_char == '_'); + if need_to_score { let curr = if j < prefix.len() { prefix[j] } else { @@ -217,7 +227,7 @@ impl<'a> Matcher<'a> { path[j - 1 - prefix.len()] }; - if last == '/' { + if last == MAIN_SEPARATOR { char_score = 0.9; } else if (last == '-' || last == '_' || last == ' ' || last.is_numeric()) || (last.is_lowercase() && curr.is_uppercase()) @@ -238,7 +248,7 @@ impl<'a> Matcher<'a> { // Apply a severe penalty if the case doesn't match. // This will make the exact matches have higher score than the case-insensitive and the // path insensitive matches. - if (self.smart_case || curr == '/') && self.query[query_idx] != curr { + if (self.smart_case || curr == MAIN_SEPARATOR) && self.query[query_idx] != curr { char_score *= 0.001; } @@ -322,6 +332,7 @@ mod tests { assert_eq!(matcher.last_positions, vec![0, 3, 4, 8]); } + #[cfg(not(target_os = "windows"))] #[test] fn test_match_path_entries() { let paths = vec![ @@ -363,6 +374,54 @@ mod tests { ); } + /// todo(windows) + /// Now, on Windows, users can only use the backslash as a path separator. + /// I do want to support both the backslash and the forward slash as path separators on Windows. + #[cfg(target_os = "windows")] + #[test] + fn test_match_path_entries() { + let paths = vec![ + "", + "a", + "ab", + "abC", + "abcd", + "alphabravocharlie", + "AlphaBravoCharlie", + "thisisatestdir", + "\\\\\\\\\\ThisIsATestDir", + "\\this\\is\\a\\test\\dir", + "\\test\\tiatd", + ]; + + assert_eq!( + match_single_path_query("abc", false, &paths), + vec![ + ("abC", vec![0, 1, 2]), + ("abcd", vec![0, 1, 2]), + ("AlphaBravoCharlie", vec![0, 5, 10]), + ("alphabravocharlie", vec![4, 5, 10]), + ] + ); + assert_eq!( + match_single_path_query("t\\i\\a\\t\\d", false, &paths), + vec![( + "\\this\\is\\a\\test\\dir", + vec![1, 5, 6, 8, 9, 10, 11, 15, 16] + ),] + ); + + assert_eq!( + match_single_path_query("tiatd", false, &paths), + vec![ + ("\\test\\tiatd", vec![6, 7, 8, 9, 10]), + ("\\this\\is\\a\\test\\dir", vec![1, 6, 9, 11, 16]), + ("\\\\\\\\\\ThisIsATestDir", vec![5, 9, 11, 12, 16]), + ("thisisatestdir", vec![0, 2, 6, 7, 11]), + ] + ); + } + #[test] fn test_lowercase_longer_than_uppercase() { // This character has more chars in lower-case than in upper-case. diff --git a/crates/git/src/blame.rs b/crates/git/src/blame.rs index 0a7a4b9211..e4947e5bbd 100644 --- a/crates/git/src/blame.rs +++ b/crates/git/src/blame.rs @@ -353,7 +353,7 @@ mod tests { let want_json = std::fs::read_to_string(&path).unwrap_or_else(|_| { panic!("could not read golden test data file at {:?}. Did you run the test with UPDATE_GOLDEN=true before?", path); - }); + }).replace("\r\n", "\n"); pretty_assertions::assert_eq!(have_json, want_json, "wrong blame entries"); } diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index fb53a833d6..eef52b2014 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -428,17 +428,24 @@ impl DirectWriteState { target_font.fallbacks.as_ref(), ) .unwrap_or_else(|| { - let family = self.system_ui_font_name.clone(); - log::error!("{} not found, use {} instead.", target_font.family, family); - self.get_font_id_from_font_collection( - family.as_ref(), - target_font.weight, - target_font.style, - &target_font.features, - target_font.fallbacks.as_ref(), - true, - ) - .unwrap() + #[cfg(any(test, feature = "test-support"))] + { + panic!("ERROR: {} font not found!", target_font.family); + } + #[cfg(not(any(test, feature = "test-support")))] + { + let family = self.system_ui_font_name.clone(); + log::error!("{} not found, use {} instead.", target_font.family, family); + self.get_font_id_from_font_collection( + family.as_ref(), + target_font.weight, + target_font.style, + &target_font.features, + target_font.fallbacks.as_ref(), + true, + ) + .unwrap() + } }) } } diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index ef4fd4a778..5423dfcbc7 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -756,21 +756,20 @@ fn should_auto_hide_scrollbars() -> Result { #[cfg(test)] mod tests { - use crate::{ClipboardItem, Platform, WindowsPlatform}; + use crate::{read_from_clipboard, write_to_clipboard, ClipboardItem}; #[test] fn test_clipboard() { - let platform = WindowsPlatform::new(); - let item = ClipboardItem::new_string("你好".to_string()); - platform.write_to_clipboard(item.clone()); - assert_eq!(platform.read_from_clipboard(), Some(item)); + let item = ClipboardItem::new_string("你好,我是张小白".to_string()); + write_to_clipboard(item.clone()); + assert_eq!(read_from_clipboard(), Some(item)); let item = ClipboardItem::new_string("12345".to_string()); - platform.write_to_clipboard(item.clone()); - assert_eq!(platform.read_from_clipboard(), Some(item)); + write_to_clipboard(item.clone()); + assert_eq!(read_from_clipboard(), Some(item)); let item = ClipboardItem::new_string_with_json_metadata("abcdef".to_string(), vec![3, 4]); - platform.write_to_clipboard(item.clone()); - assert_eq!(platform.read_from_clipboard(), Some(item)); + write_to_clipboard(item.clone()); + assert_eq!(read_from_clipboard(), Some(item)); } } diff --git a/crates/gpui_macros/Cargo.toml b/crates/gpui_macros/Cargo.toml index c8236245e6..997b167f89 100644 --- a/crates/gpui_macros/Cargo.toml +++ b/crates/gpui_macros/Cargo.toml @@ -14,9 +14,9 @@ proc-macro = true doctest = true [dependencies] -proc-macro2 = "1.0.66" -quote = "1.0.9" -syn = { version = "1.0.72", features = ["full", "extra-traits"] } +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true [dev-dependencies] gpui.workspace = true diff --git a/crates/language_tools/src/lsp_log_tests.rs b/crates/language_tools/src/lsp_log_tests.rs index 204625a05f..5d318d0afa 100644 --- a/crates/language_tools/src/lsp_log_tests.rs +++ b/crates/language_tools/src/lsp_log_tests.rs @@ -11,6 +11,7 @@ use lsp_log::LogKind; use project::{FakeFs, Project}; use serde_json::json; use settings::SettingsStore; +use util::path; #[gpui::test] async fn test_lsp_logs(cx: &mut TestAppContext) { @@ -22,7 +23,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/the-root", + path!("/the-root"), json!({ "test.rs": "", "package.json": "", @@ -30,7 +31,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { ) .await; - let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/the-root").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(Arc::new(Language::new( @@ -57,7 +58,7 @@ async fn test_lsp_logs(cx: &mut TestAppContext) { let _rust_buffer = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/the-root/test.rs", cx) + project.open_local_buffer_with_lsp(path!("/the-root/test.rs"), cx) }) .await .unwrap(); diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index ba68cf2be1..61167620fc 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -818,11 +818,12 @@ mod tests { use lsp::CompletionItemLabelDetails; use settings::SettingsStore; use theme::SyntaxTheme; + use util::path; #[gpui::test] async fn test_process_rust_diagnostics() { let mut params = lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/a").unwrap(), + uri: lsp::Url::from_file_path(path!("/a")).unwrap(), version: None, diagnostics: vec![ // no newlines diff --git a/crates/prettier/src/prettier.rs b/crates/prettier/src/prettier.rs index 4411e43f1b..a9254ac157 100644 --- a/crates/prettier/src/prettier.rs +++ b/crates/prettier/src/prettier.rs @@ -946,7 +946,7 @@ mod tests { .await { Ok(path) => panic!("Expected to fail for prettier in package.json but not in node_modules found, but got path {path:?}"), Err(e) => { - let message = e.to_string(); + let message = e.to_string().replace("\\\\", "/"); assert!(message.contains("/root/work/full-stack-foundations/exercises/03.loading/01.problem.loader"), "Error message should mention which project had prettier defined"); assert!(message.contains("/root/work/full-stack-foundations"), "Error message should mention potential candidates without prettier node_modules contents"); }, diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 97dbc3bd24..d2f36ae960 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -25,10 +25,7 @@ use std::{mem, num::NonZeroU32, ops::Range, task::Poll}; use task::{ResolvedTask, TaskContext}; use unindent::Unindent as _; use util::{ - assert_set_eq, - paths::{replace_path_separator, PathMatcher}, - test::TempTree, - TryFutureExt as _, + assert_set_eq, path, paths::PathMatcher, separator, test::TempTree, uri, TryFutureExt as _, }; #[gpui::test] @@ -37,7 +34,10 @@ async fn test_block_via_channel(cx: &mut gpui::TestAppContext) { let (tx, mut rx) = futures::channel::mpsc::unbounded(); let _thread = std::thread::spawn(move || { + #[cfg(not(target_os = "windows"))] std::fs::metadata("/tmp").unwrap(); + #[cfg(target_os = "windows")] + std::fs::metadata("C:/Windows").unwrap(); std::thread::sleep(Duration::from_millis(1000)); tx.unbounded_send(1).unwrap(); }); @@ -199,7 +199,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/the-root", + path!("/dir"), json!({ ".zed": { "settings.json": r#"{ "tab_size": 8 }"#, @@ -227,7 +227,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) ) .await; - let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let worktree = project.update(cx, |project, cx| project.worktrees(cx).next().unwrap()); let task_context = TaskContext::default(); @@ -280,8 +280,12 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) ( TaskSourceKind::Worktree { id: worktree_id, - directory_in_worktree: PathBuf::from("b/.zed"), - id_base: "local worktree tasks from directory \"b/.zed\"".into(), + directory_in_worktree: PathBuf::from(separator!("b/.zed")), + id_base: if cfg!(windows) { + "local worktree tasks from directory \"b\\\\.zed\"".into() + } else { + "local worktree tasks from directory \"b/.zed\"".into() + }, }, "cargo check".to_string(), vec!["check".to_string()], @@ -359,8 +363,12 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) ( TaskSourceKind::Worktree { id: worktree_id, - directory_in_worktree: PathBuf::from("b/.zed"), - id_base: "local worktree tasks from directory \"b/.zed\"".into(), + directory_in_worktree: PathBuf::from(separator!("b/.zed")), + id_base: if cfg!(windows) { + "local worktree tasks from directory \"b\\\\.zed\"".into() + } else { + "local worktree tasks from directory \"b/.zed\"".into() + }, }, "cargo check".to_string(), vec!["check".to_string()], @@ -392,7 +400,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/the-root", + path!("/dir"), json!({ "test.rs": "const A: i32 = 1;", "test2.rs": "", @@ -402,7 +410,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let mut fake_rust_servers = language_registry.register_fake_lsp( @@ -449,7 +457,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { // Open a buffer without an associated language server. let (toml_buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/the-root/Cargo.toml", cx) + project.open_local_buffer_with_lsp(path!("/dir/Cargo.toml"), cx) }) .await .unwrap(); @@ -457,7 +465,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { // Open a buffer with an associated language server before the language for it has been loaded. let (rust_buffer, _handle2) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/the-root/test.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/test.rs"), cx) }) .await .unwrap(); @@ -482,7 +490,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/test.rs")).unwrap(), version: 0, text: "const A: i32 = 1;".to_string(), language_id: "rust".to_string(), @@ -512,7 +520,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::VersionedTextDocumentIdentifier::new( - lsp::Url::from_file_path("/the-root/test.rs").unwrap(), + lsp::Url::from_file_path(path!("/dir/test.rs")).unwrap(), 1 ) ); @@ -520,7 +528,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { // Open a third buffer with a different associated language server. let (json_buffer, _json_handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/the-root/package.json", cx) + project.open_local_buffer_with_lsp(path!("/dir/package.json"), cx) }) .await .unwrap(); @@ -533,7 +541,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/package.json")).unwrap(), version: 0, text: "{\"a\": 1}".to_string(), language_id: "json".to_string(), @@ -557,7 +565,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { // it is also configured based on the existing language server's capabilities. let (rust_buffer2, _handle4) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/the-root/test2.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/test2.rs"), cx) }) .await .unwrap(); @@ -583,7 +591,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::VersionedTextDocumentIdentifier::new( - lsp::Url::from_file_path("/the-root/test2.rs").unwrap(), + lsp::Url::from_file_path(path!("/dir/test2.rs")).unwrap(), 1 ) ); @@ -598,20 +606,24 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .receive_notification::() .await .text_document, - lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap()) + lsp::TextDocumentIdentifier::new( + lsp::Url::from_file_path(path!("/dir/Cargo.toml")).unwrap() + ) ); assert_eq!( fake_json_server .receive_notification::() .await .text_document, - lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/Cargo.toml").unwrap()) + lsp::TextDocumentIdentifier::new( + lsp::Url::from_file_path(path!("/dir/Cargo.toml")).unwrap() + ) ); // Renames are reported only to servers matching the buffer's language. fs.rename( - Path::new("/the-root/test2.rs"), - Path::new("/the-root/test3.rs"), + Path::new(path!("/dir/test2.rs")), + Path::new(path!("/dir/test3.rs")), Default::default(), ) .await @@ -621,7 +633,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .receive_notification::() .await .text_document, - lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/test2.rs").unwrap()), + lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path!("/dir/test2.rs")).unwrap()), ); assert_eq!( fake_rust_server @@ -629,7 +641,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/test3.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/test3.rs")).unwrap(), version: 0, text: rust_buffer2.update(cx, |buffer, _| buffer.text()), language_id: "rust".to_string(), @@ -660,8 +672,8 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { // When the rename changes the extension of the file, the buffer gets closed on the old // language server and gets opened on the new one. fs.rename( - Path::new("/the-root/test3.rs"), - Path::new("/the-root/test3.json"), + Path::new(path!("/dir/test3.rs")), + Path::new(path!("/dir/test3.json")), Default::default(), ) .await @@ -671,7 +683,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .receive_notification::() .await .text_document, - lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path("/the-root/test3.rs").unwrap(),), + lsp::TextDocumentIdentifier::new(lsp::Url::from_file_path(path!("/dir/test3.rs")).unwrap(),), ); assert_eq!( fake_json_server @@ -679,7 +691,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/test3.json")).unwrap(), version: 0, text: rust_buffer2.update(cx, |buffer, _| buffer.text()), language_id: "json".to_string(), @@ -705,7 +717,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::VersionedTextDocumentIdentifier::new( - lsp::Url::from_file_path("/the-root/test3.json").unwrap(), + lsp::Url::from_file_path(path!("/dir/test3.json")).unwrap(), 1 ) ); @@ -734,7 +746,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { .await .text_document, lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/test.rs")).unwrap(), version: 0, text: rust_buffer.update(cx, |buffer, _| buffer.text()), language_id: "rust".to_string(), @@ -755,13 +767,13 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { ], [ lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/package.json")).unwrap(), version: 0, text: json_buffer.update(cx, |buffer, _| buffer.text()), language_id: "json".to_string(), }, lsp::TextDocumentItem { - uri: lsp::Url::from_file_path("/the-root/test3.json").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/test3.json")).unwrap(), version: 0, text: rust_buffer2.update(cx, |buffer, _| buffer.text()), language_id: "json".to_string(), @@ -773,7 +785,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { cx.update(|_| drop(_json_handle)); let close_message = lsp::DidCloseTextDocumentParams { text_document: lsp::TextDocumentIdentifier::new( - lsp::Url::from_file_path("/the-root/package.json").unwrap(), + lsp::Url::from_file_path(path!("/dir/package.json")).unwrap(), ), }; assert_eq!( @@ -786,19 +798,11 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) { #[gpui::test] async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) { - fn add_root_for_windows(path: &str) -> String { - if cfg!(windows) { - format!("C:{}", path) - } else { - path.to_string() - } - } - init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( - add_root_for_windows("/the-root"), + path!("/the-root"), json!({ ".gitignore": "target\n", "src": { @@ -826,7 +830,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon ) .await; - let project = Project::test(fs.clone(), [add_root_for_windows("/the-root").as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/the-root").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); let mut fake_servers = language_registry.register_fake_lsp( @@ -842,7 +846,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon // Start the language server by opening a buffer with a compatible file extension. let _ = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp(add_root_for_windows("/the-root/src/a.rs"), cx) + project.open_local_buffer_with_lsp(path!("/the-root/src/a.rs"), cx) }) .await .unwrap(); @@ -882,21 +886,21 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon lsp::DidChangeWatchedFilesRegistrationOptions { watchers: vec![ lsp::FileSystemWatcher { - glob_pattern: lsp::GlobPattern::String(add_root_for_windows( - "/the-root/Cargo.toml", - )), + glob_pattern: lsp::GlobPattern::String( + path!("/the-root/Cargo.toml").to_string(), + ), kind: None, }, lsp::FileSystemWatcher { - glob_pattern: lsp::GlobPattern::String(add_root_for_windows( - "/the-root/src/*.{rs,c}", - )), + glob_pattern: lsp::GlobPattern::String( + path!("/the-root/src/*.{rs,c}").to_string(), + ), kind: None, }, lsp::FileSystemWatcher { - glob_pattern: lsp::GlobPattern::String(add_root_for_windows( - "/the-root/target/y/**/*.rs", - )), + glob_pattern: lsp::GlobPattern::String( + path!("/the-root/target/y/**/*.rs").to_string(), + ), kind: None, }, ], @@ -949,32 +953,23 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon // Perform some file system mutations, two of which match the watched patterns, // and one of which does not. + fs.create_file(path!("/the-root/src/c.rs").as_ref(), Default::default()) + .await + .unwrap(); + fs.create_file(path!("/the-root/src/d.txt").as_ref(), Default::default()) + .await + .unwrap(); + fs.remove_file(path!("/the-root/src/b.rs").as_ref(), Default::default()) + .await + .unwrap(); fs.create_file( - add_root_for_windows("/the-root/src/c.rs").as_ref(), + path!("/the-root/target/x/out/x2.rs").as_ref(), Default::default(), ) .await .unwrap(); fs.create_file( - add_root_for_windows("/the-root/src/d.txt").as_ref(), - Default::default(), - ) - .await - .unwrap(); - fs.remove_file( - add_root_for_windows("/the-root/src/b.rs").as_ref(), - Default::default(), - ) - .await - .unwrap(); - fs.create_file( - add_root_for_windows("/the-root/target/x/out/x2.rs").as_ref(), - Default::default(), - ) - .await - .unwrap(); - fs.create_file( - add_root_for_windows("/the-root/target/y/out/y2.rs").as_ref(), + path!("/the-root/target/y/out/y2.rs").as_ref(), Default::default(), ) .await @@ -986,16 +981,15 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon &*file_changes.lock(), &[ lsp::FileEvent { - uri: lsp::Url::from_file_path(add_root_for_windows("/the-root/src/b.rs")).unwrap(), + uri: lsp::Url::from_file_path(path!("/the-root/src/b.rs")).unwrap(), typ: lsp::FileChangeType::DELETED, }, lsp::FileEvent { - uri: lsp::Url::from_file_path(add_root_for_windows("/the-root/src/c.rs")).unwrap(), + uri: lsp::Url::from_file_path(path!("/the-root/src/c.rs")).unwrap(), typ: lsp::FileChangeType::CREATED, }, lsp::FileEvent { - uri: lsp::Url::from_file_path(add_root_for_windows("/the-root/target/y/out/y2.rs")) - .unwrap(), + uri: lsp::Url::from_file_path(path!("/the-root/target/y/out/y2.rs")).unwrap(), typ: lsp::FileChangeType::CREATED, }, ] @@ -1008,7 +1002,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.rs": "let a = 1;", "b.rs": "let b = 2;" @@ -1016,15 +1010,24 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs, ["/dir/a.rs".as_ref(), "/dir/b.rs".as_ref()], cx).await; + let project = Project::test( + fs, + [path!("/dir/a.rs").as_ref(), path!("/dir/b.rs").as_ref()], + cx, + ) + .await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let buffer_a = project - .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/dir/a.rs"), cx) + }) .await .unwrap(); let buffer_b = project - .update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/dir/b.rs"), cx) + }) .await .unwrap(); @@ -1033,7 +1036,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { .update_diagnostics( LanguageServerId(0), lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/dir/a.rs").unwrap(), + uri: Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 5)), @@ -1050,7 +1053,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) { .update_diagnostics( LanguageServerId(0), lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/dir/b.rs").unwrap(), + uri: Url::from_file_path(path!("/dir/b.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 5)), @@ -1101,7 +1104,7 @@ async fn test_omitted_diagnostics(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/root", + path!("/root"), json!({ "dir": { ".git": { @@ -1116,11 +1119,11 @@ async fn test_omitted_diagnostics(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs, ["/root/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/root/dir").as_ref()], cx).await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let (worktree, _) = project .update(cx, |project, cx| { - project.find_or_create_worktree("/root/dir", true, cx) + project.find_or_create_worktree(path!("/root/dir"), true, cx) }) .await .unwrap(); @@ -1128,7 +1131,7 @@ async fn test_omitted_diagnostics(cx: &mut gpui::TestAppContext) { let (worktree, _) = project .update(cx, |project, cx| { - project.find_or_create_worktree("/root/other.rs", false, cx) + project.find_or_create_worktree(path!("/root/other.rs"), false, cx) }) .await .unwrap(); @@ -1140,7 +1143,7 @@ async fn test_omitted_diagnostics(cx: &mut gpui::TestAppContext) { .update_diagnostics( server_id, lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/root/dir/b.rs").unwrap(), + uri: Url::from_file_path(path!("/root/dir/b.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 4), lsp::Position::new(0, 5)), @@ -1157,7 +1160,7 @@ async fn test_omitted_diagnostics(cx: &mut gpui::TestAppContext) { .update_diagnostics( server_id, lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/root/other.rs").unwrap(), + uri: Url::from_file_path(path!("/root/other.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 8), lsp::Position::new(0, 9)), @@ -1244,7 +1247,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.rs": "fn a() { A }", "b.rs": "const y: i32 = 1", @@ -1252,7 +1255,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -1270,7 +1273,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { // Cause worktree to start the fake language server let _ = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/b.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/b.rs"), cx) }) .await .unwrap(); @@ -1299,7 +1302,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { ); fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/dir/a.rs").unwrap(), + uri: Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)), @@ -1325,7 +1328,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { ); let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/dir/a.rs", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/a.rs"), cx)) .await .unwrap(); @@ -1351,7 +1354,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { // Ensure publishing empty diagnostics twice only results in one update event. fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/dir/a.rs").unwrap(), + uri: Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: Default::default(), }); @@ -1364,7 +1367,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { ); fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/dir/a.rs").unwrap(), + uri: Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: Default::default(), }); @@ -1379,9 +1382,9 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC let progress_token = "the-progress-token"; let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "a.rs": "" })).await; + fs.insert_tree(path!("/dir"), json!({ "a.rs": "" })).await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -1399,7 +1402,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC let (buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/a.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/a.rs"), cx) }) .await .unwrap(); @@ -1465,9 +1468,9 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp init_test(cx); let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "a.rs": "x" })).await; + fs.insert_tree(path!("/dir"), json!({ "a.rs": "x" })).await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -1475,7 +1478,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp let (buffer, _) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/a.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/a.rs"), cx) }) .await .unwrap(); @@ -1483,7 +1486,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp // Publish diagnostics let fake_server = fake_servers.next().await.unwrap(); fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: Url::from_file_path("/dir/a.rs").unwrap(), + uri: Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: None, diagnostics: vec![lsp::Diagnostic { range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0)), @@ -1546,9 +1549,9 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T init_test(cx); let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "a.rs": "" })).await; + fs.insert_tree(path!("/dir"), json!({ "a.rs": "" })).await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -1556,7 +1559,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T let (buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/a.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/a.rs"), cx) }) .await .unwrap(); @@ -1564,7 +1567,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T // Before restarting the server, report diagnostics with an unknown buffer version. let fake_server = fake_servers.next().await.unwrap(); fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(10000), diagnostics: Vec::new(), }); @@ -1588,9 +1591,9 @@ async fn test_cancel_language_server_work(cx: &mut gpui::TestAppContext) { let progress_token = "the-progress-token"; let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "a.rs": "" })).await; + fs.insert_tree(path!("/dir"), json!({ "a.rs": "" })).await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -1606,7 +1609,7 @@ async fn test_cancel_language_server_work(cx: &mut gpui::TestAppContext) { let (buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/a.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/a.rs"), cx) }) .await .unwrap(); @@ -1651,10 +1654,10 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" })) + fs.insert_tree(path!("/dir"), json!({ "a.rs": "", "b.js": "" })) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); let mut fake_rust_servers = language_registry.register_fake_lsp( @@ -1676,13 +1679,13 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { let _rs_buffer = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/a.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/a.rs"), cx) }) .await .unwrap(); let _js_buffer = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/b.js", cx) + project.open_local_buffer_with_lsp(path!("/dir/b.js"), cx) }) .await .unwrap(); @@ -1695,7 +1698,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { .text_document .uri .as_str(), - "file:///dir/a.rs" + uri!("file:///dir/a.rs") ); let mut fake_js_server = fake_js_servers.next().await.unwrap(); @@ -1706,7 +1709,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { .text_document .uri .as_str(), - "file:///dir/b.js" + uri!("file:///dir/b.js") ); // Disable Rust language server, ensuring only that server gets stopped. @@ -1757,7 +1760,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) { .text_document .uri .as_str(), - "file:///dir/a.rs" + uri!("file:///dir/a.rs") ); fake_js_server .receive_notification::() @@ -1776,9 +1779,9 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { .unindent(); let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "a.rs": text })).await; + fs.insert_tree(path!("/dir"), json!({ "a.rs": text })).await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let language_registry = project.read_with(cx, |project, _| project.languages().clone()); @@ -1792,7 +1795,9 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { ); let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/dir/a.rs"), cx) + }) .await .unwrap(); @@ -1814,7 +1819,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { // Report some diagnostics for the initial version of the buffer fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(open_notification.text_document.version), diagnostics: vec![ lsp::Diagnostic { @@ -1900,7 +1905,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { // Ensure overlapping diagnostics are highlighted correctly. fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(open_notification.text_document.version), diagnostics: vec![ lsp::Diagnostic { @@ -1992,7 +1997,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) { // Handle out-of-order diagnostics fake_server.notify::(&lsp::PublishDiagnosticsParams { - uri: lsp::Url::from_file_path("/dir/a.rs").unwrap(), + uri: lsp::Url::from_file_path(path!("/dir/a.rs")).unwrap(), version: Some(change_notification_2.text_document.version), diagnostics: vec![ lsp::Diagnostic { @@ -2198,14 +2203,14 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.rs": text.clone(), }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let language_registry = project.read_with(cx, |project, _| project.languages().clone()); @@ -2214,7 +2219,7 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) { let (buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/a.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/a.rs"), cx) }) .await .unwrap(); @@ -2351,17 +2356,19 @@ async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui::TestAp let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.rs": text.clone(), }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/dir/a.rs"), cx) + }) .await .unwrap(); @@ -2460,17 +2467,19 @@ async fn test_invalid_edits_from_lsp2(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.rs": text.clone(), }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let buffer = project - .update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx)) + .update(cx, |project, cx| { + project.open_local_buffer(path!("/dir/a.rs"), cx) + }) .await .unwrap(); @@ -2571,7 +2580,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.rs": "const fn a() { A }", "b.rs": "const y: i32 = crate::a()", @@ -2579,7 +2588,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs, ["/dir/b.rs".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir/b.rs").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -2587,7 +2596,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { let (buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/b.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/b.rs"), cx) }) .await .unwrap(); @@ -2597,13 +2606,13 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { let params = params.text_document_position_params; assert_eq!( params.text_document.uri.to_file_path().unwrap(), - Path::new("/dir/b.rs"), + Path::new(path!("/dir/b.rs")), ); assert_eq!(params.position, lsp::Position::new(0, 22)); Ok(Some(lsp::GotoDefinitionResponse::Scalar( lsp::Location::new( - lsp::Url::from_file_path("/dir/a.rs").unwrap(), + lsp::Url::from_file_path(path!("/dir/a.rs")).unwrap(), lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)), ), ))) @@ -2629,18 +2638,24 @@ async fn test_definition(cx: &mut gpui::TestAppContext) { .as_local() .unwrap() .abs_path(cx), - Path::new("/dir/a.rs"), + Path::new(path!("/dir/a.rs")), ); assert_eq!(definition.target.range.to_offset(target_buffer), 9..10); assert_eq!( list_worktrees(&project, cx), - [("/dir/a.rs".as_ref(), false), ("/dir/b.rs".as_ref(), true)], + [ + (path!("/dir/a.rs").as_ref(), false), + (path!("/dir/b.rs").as_ref(), true) + ], ); drop(definition); }); cx.update(|cx| { - assert_eq!(list_worktrees(&project, cx), [("/dir/b.rs".as_ref(), true)]); + assert_eq!( + list_worktrees(&project, cx), + [(path!("/dir/b.rs").as_ref(), true)] + ); }); fn list_worktrees<'a>(project: &'a Entity, cx: &'a App) -> Vec<(&'a Path, bool)> { @@ -2664,14 +2679,14 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.ts": "", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); @@ -2690,7 +2705,9 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) { ); let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx) + }) .await .unwrap(); @@ -2756,14 +2773,14 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.ts": "", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); @@ -2782,7 +2799,9 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) { ); let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx) + }) .await .unwrap(); @@ -2817,14 +2836,14 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.ts": "a", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); @@ -2845,7 +2864,9 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { ); let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx) + }) .await .unwrap(); @@ -2910,7 +2931,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) { edit: lsp::WorkspaceEdit { changes: Some( [( - lsp::Url::from_file_path("/dir/a.ts").unwrap(), + lsp::Url::from_file_path(path!("/dir/a.ts")).unwrap(), vec![lsp::TextEdit { range: lsp::Range::new( lsp::Position::new(0, 0), @@ -2952,16 +2973,16 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "file1": "the old contents", }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file1"), cx)) .await .unwrap(); buffer.update(cx, |buffer, cx| { @@ -2974,7 +2995,11 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) { .await .unwrap(); - let new_text = fs.load(Path::new("/dir/file1")).await.unwrap(); + let new_text = fs + .load(Path::new(path!("/dir/file1"))) + .await + .unwrap() + .replace("\r\n", "\n"); assert_eq!(new_text, buffer.update(cx, |buffer, _| buffer.text())); } @@ -2984,17 +3009,17 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "file1": "the original contents", }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let worktree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap()); let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file1"), cx)) .await .unwrap(); @@ -3005,7 +3030,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) // Change the buffer's file on disk, and then wait for the file change // to be detected by the worktree, so that the buffer starts reloading. fs.save( - "/dir/file1".as_ref(), + path!("/dir/file1").as_ref(), &"the first contents".into(), Default::default(), ) @@ -3016,7 +3041,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) // Change the buffer's file again. Depending on the random seed, the // previous file change may still be in progress. fs.save( - "/dir/file1".as_ref(), + path!("/dir/file1").as_ref(), &"the second contents".into(), Default::default(), ) @@ -3025,7 +3050,7 @@ async fn test_file_changes_multiple_times_on_disk(cx: &mut gpui::TestAppContext) worktree.next_event(cx).await; cx.executor().run_until_parked(); - let on_disk_text = fs.load(Path::new("/dir/file1")).await.unwrap(); + let on_disk_text = fs.load(Path::new(path!("/dir/file1"))).await.unwrap(); buffer.read_with(cx, |buffer, _| { assert_eq!(buffer.text(), on_disk_text); assert!(!buffer.is_dirty(), "buffer should not be dirty"); @@ -3039,17 +3064,17 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "file1": "the original contents", }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let worktree = project.read_with(cx, |project, cx| project.worktrees(cx).next().unwrap()); let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file1"), cx)) .await .unwrap(); @@ -3060,7 +3085,7 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) { // Change the buffer's file on disk, and then wait for the file change // to be detected by the worktree, so that the buffer starts reloading. fs.save( - "/dir/file1".as_ref(), + path!("/dir/file1").as_ref(), &"the first contents".into(), Default::default(), ) @@ -3079,7 +3104,7 @@ async fn test_edit_buffer_while_it_reloads(cx: &mut gpui::TestAppContext) { }); cx.executor().run_until_parked(); - let on_disk_text = fs.load(Path::new("/dir/file1")).await.unwrap(); + let on_disk_text = fs.load(Path::new(path!("/dir/file1"))).await.unwrap(); buffer.read_with(cx, |buffer, _| { let buffer_text = buffer.text(); if buffer_text == on_disk_text { @@ -3103,16 +3128,16 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "file1": "the old contents", }), ) .await; - let project = Project::test(fs.clone(), ["/dir/file1".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir/file1").as_ref()], cx).await; let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file1"), cx)) .await .unwrap(); buffer.update(cx, |buffer, cx| { @@ -3124,7 +3149,11 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) { .await .unwrap(); - let new_text = fs.load(Path::new("/dir/file1")).await.unwrap(); + let new_text = fs + .load(Path::new(path!("/dir/file1"))) + .await + .unwrap() + .replace("\r\n", "\n"); assert_eq!(new_text, buffer.update(cx, |buffer, _| buffer.text())); } @@ -3259,26 +3288,21 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) { std::fs::rename(dir.path().join("a/file2"), dir.path().join("a/file2.new")).unwrap(); tree.flush_fs_events(cx).await; - let expected_paths = vec![ - "a", - "a/file1", - "a/file2.new", - "b", - "d", - "d/file3", - "d/file4", - ] - .into_iter() - .map(replace_path_separator) - .collect::>(); - cx.update(|app| { assert_eq!( tree.read(app) .paths() .map(|p| p.to_str().unwrap()) .collect::>(), - expected_paths + vec![ + "a", + separator!("a/file1"), + separator!("a/file2.new"), + "b", + "d", + separator!("d/file3"), + separator!("d/file4"), + ] ); }); @@ -3338,7 +3362,15 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) { .paths() .map(|p| p.to_str().unwrap()) .collect::>(), - expected_paths + vec![ + "a", + separator!("a/file1"), + separator!("a/file2.new"), + "b", + "d", + separator!("d/file3"), + separator!("d/file4"), + ] ); }); } @@ -3447,7 +3479,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "file1": "abc", "file2": "def", @@ -3456,10 +3488,10 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let buffer1 = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file1"), cx)) .await .unwrap(); let events = Arc::new(Mutex::new(Vec::new())); @@ -3542,7 +3574,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { // When a file is deleted, the buffer is considered dirty. let events = Arc::new(Mutex::new(Vec::new())); let buffer2 = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file2", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file2"), cx)) .await .unwrap(); buffer2.update(cx, |_, cx| { @@ -3553,7 +3585,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { .detach(); }); - fs.remove_file("/dir/file2".as_ref(), Default::default()) + fs.remove_file(path!("/dir/file2").as_ref(), Default::default()) .await .unwrap(); cx.executor().run_until_parked(); @@ -3569,7 +3601,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { // When a file is already dirty when deleted, we don't emit a Dirtied event. let events = Arc::new(Mutex::new(Vec::new())); let buffer3 = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file3", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file3"), cx)) .await .unwrap(); buffer3.update(cx, |_, cx| { @@ -3584,7 +3616,7 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) { buffer.edit([(0..0, "x")], None, cx); }); events.lock().clear(); - fs.remove_file("/dir/file3".as_ref(), Default::default()) + fs.remove_file(path!("/dir/file3").as_ref(), Default::default()) .await .unwrap(); cx.executor().run_until_parked(); @@ -3599,15 +3631,15 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { let initial_contents = "aaa\nbbbbb\nc\n"; let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "the-file": initial_contents, }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/dir/the-file", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/the-file"), cx)) .await .unwrap(); @@ -3623,7 +3655,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { }); let new_contents = "AAAA\naaa\nBB\nbbbbb\n"; fs.save( - "/dir/the-file".as_ref(), + path!("/dir/the-file").as_ref(), &new_contents.into(), LineEnding::Unix, ) @@ -3658,7 +3690,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) { // Change the file on disk again, adding blank lines to the beginning. fs.save( - "/dir/the-file".as_ref(), + path!("/dir/the-file").as_ref(), &"\n\n\nAAAA\naaa\nBB\nbbbbb\n".into(), LineEnding::Unix, ) @@ -3679,7 +3711,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "file1": "a\nb\nc\n", "file2": "one\r\ntwo\r\nthree\r\n", @@ -3687,13 +3719,13 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let buffer1 = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file1", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file1"), cx)) .await .unwrap(); let buffer2 = project - .update(cx, |p, cx| p.open_local_buffer("/dir/file2", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/file2"), cx)) .await .unwrap(); @@ -3709,7 +3741,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { // Change a file's line endings on disk from unix to windows. The buffer's // state updates correctly. fs.save( - "/dir/file1".as_ref(), + path!("/dir/file1").as_ref(), &"aaa\nb\nc\n".into(), LineEnding::Windows, ) @@ -3730,7 +3762,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) { .await .unwrap(); assert_eq!( - fs.load("/dir/file2".as_ref()).await.unwrap(), + fs.load(path!("/dir/file2").as_ref()).await.unwrap(), "one\r\ntwo\r\nthree\r\nfour\r\n", ); } @@ -3741,7 +3773,7 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/the-dir", + path!("/dir"), json!({ "a.rs": " fn foo(mut v: Vec) { @@ -3755,14 +3787,14 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs.clone(), ["/the-dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let lsp_store = project.read_with(cx, |project, _| project.lsp_store()); let buffer = project - .update(cx, |p, cx| p.open_local_buffer("/the-dir/a.rs", cx)) + .update(cx, |p, cx| p.open_local_buffer(path!("/dir/a.rs"), cx)) .await .unwrap(); - let buffer_uri = Url::from_file_path("/the-dir/a.rs").unwrap(); + let buffer_uri = Url::from_file_path(path!("/dir/a.rs")).unwrap(); let message = lsp::PublishDiagnosticsParams { uri: buffer_uri.clone(), diagnostics: vec![ @@ -3984,7 +4016,7 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": "const ONE: usize = 1;", "two": { @@ -3994,7 +4026,7 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) { }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -4038,7 +4070,7 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) { let _ = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/one.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/one.rs"), cx) }) .await .unwrap(); @@ -4067,7 +4099,7 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) { new_text: "This is not a drill".to_owned(), })], text_document: lsp::OptionalVersionedTextDocumentIdentifier { - uri: Url::from_str("file:///dir/two/two.rs").unwrap(), + uri: Url::from_str(uri!("file:///dir/two/two.rs")).unwrap(), version: Some(1337), }, }] @@ -4084,8 +4116,8 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) { let expected_edit = expected_edit.clone(); async move { assert_eq!(params.files.len(), 1); - assert_eq!(params.files[0].old_uri, "file:///dir/one.rs"); - assert_eq!(params.files[0].new_uri, "file:///dir/three.rs"); + assert_eq!(params.files[0].old_uri, uri!("file:///dir/one.rs")); + assert_eq!(params.files[0].new_uri, uri!("file:///dir/three.rs")); resolved_workspace_edit.set(expected_edit.clone()).unwrap(); Ok(Some(expected_edit)) } @@ -4098,8 +4130,8 @@ async fn test_lsp_rename_notifications(cx: &mut gpui::TestAppContext) { fake_server .handle_notification::(|params, _| { assert_eq!(params.files.len(), 1); - assert_eq!(params.files[0].old_uri, "file:///dir/one.rs"); - assert_eq!(params.files[0].new_uri, "file:///dir/three.rs"); + assert_eq!(params.files[0].old_uri, uri!("file:///dir/one.rs")); + assert_eq!(params.files[0].new_uri, uri!("file:///dir/three.rs")); }) .next() .await @@ -4114,7 +4146,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": "const ONE: usize = 1;", "two.rs": "const TWO: usize = one::ONE + one::ONE;" @@ -4122,7 +4154,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(rust_lang()); @@ -4142,7 +4174,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { let (buffer, _handle) = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/one.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/one.rs"), cx) }) .await .unwrap(); @@ -4154,7 +4186,10 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { }); fake_server .handle_request::(|params, _| async move { - assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs"); + assert_eq!( + params.text_document.uri.as_str(), + uri!("file:///dir/one.rs") + ); assert_eq!(params.position, lsp::Position::new(0, 7)); Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new( lsp::Position::new(0, 6), @@ -4178,7 +4213,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { .handle_request::(|params, _| async move { assert_eq!( params.text_document_position.text_document.uri.as_str(), - "file:///dir/one.rs" + uri!("file:///dir/one.rs") ); assert_eq!( params.text_document_position.position, @@ -4189,14 +4224,14 @@ async fn test_rename(cx: &mut gpui::TestAppContext) { changes: Some( [ ( - lsp::Url::from_file_path("/dir/one.rs").unwrap(), + lsp::Url::from_file_path(path!("/dir/one.rs")).unwrap(), vec![lsp::TextEdit::new( lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), "THREE".to_string(), )], ), ( - lsp::Url::from_file_path("/dir/two.rs").unwrap(), + lsp::Url::from_file_path(path!("/dir/two.rs")).unwrap(), vec![ lsp::TextEdit::new( lsp::Range::new( @@ -4250,7 +4285,7 @@ async fn test_search(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": "const ONE: usize = 1;", "two.rs": "const TWO: usize = one::ONE + one::ONE;", @@ -4259,7 +4294,7 @@ async fn test_search(cx: &mut gpui::TestAppContext) { }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; assert_eq!( search( &project, @@ -4278,14 +4313,14 @@ async fn test_search(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/two.rs".to_string(), vec![6..9]), - ("dir/three.rs".to_string(), vec![37..40]) + (separator!("dir/two.rs").to_string(), vec![6..9]), + (separator!("dir/three.rs").to_string(), vec![37..40]) ]) ); let buffer_4 = project .update(cx, |project, cx| { - project.open_local_buffer("/dir/four.rs", cx) + project.open_local_buffer(path!("/dir/four.rs"), cx) }) .await .unwrap(); @@ -4312,9 +4347,9 @@ async fn test_search(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/two.rs".to_string(), vec![6..9]), - ("dir/three.rs".to_string(), vec![37..40]), - ("dir/four.rs".to_string(), vec![25..28, 36..39]) + (separator!("dir/two.rs").to_string(), vec![6..9]), + (separator!("dir/three.rs").to_string(), vec![37..40]), + (separator!("dir/four.rs").to_string(), vec![25..28, 36..39]) ]) ); } @@ -4327,7 +4362,7 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": r#"// Rust file one"#, "one.ts": r#"// TypeScript file one"#, @@ -4336,7 +4371,7 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; assert!( search( @@ -4377,8 +4412,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/one.rs".to_string(), vec![8..12]), - ("dir/two.rs".to_string(), vec![8..12]), + (separator!("dir/one.rs").to_string(), vec![8..12]), + (separator!("dir/two.rs").to_string(), vec![8..12]), ]), "Rust only search should give only Rust files" ); @@ -4402,8 +4437,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/one.ts".to_string(), vec![14..18]), - ("dir/two.ts".to_string(), vec![14..18]), + (separator!("dir/one.ts").to_string(), vec![14..18]), + (separator!("dir/two.ts").to_string(), vec![14..18]), ]), "TypeScript only search should give only TypeScript files, even if other inclusions don't match anything" ); @@ -4427,10 +4462,10 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/two.ts".to_string(), vec![14..18]), - ("dir/one.rs".to_string(), vec![8..12]), - ("dir/one.ts".to_string(), vec![14..18]), - ("dir/two.rs".to_string(), vec![8..12]), + (separator!("dir/two.ts").to_string(), vec![14..18]), + (separator!("dir/one.rs").to_string(), vec![8..12]), + (separator!("dir/one.ts").to_string(), vec![14..18]), + (separator!("dir/two.rs").to_string(), vec![8..12]), ]), "Rust and typescript search should give both Rust and TypeScript files, even if other inclusions don't match anything" ); @@ -4444,7 +4479,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": r#"// Rust file one"#, "one.ts": r#"// TypeScript file one"#, @@ -4453,7 +4488,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; assert_eq!( search( @@ -4473,10 +4508,10 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/one.rs".to_string(), vec![8..12]), - ("dir/one.ts".to_string(), vec![14..18]), - ("dir/two.rs".to_string(), vec![8..12]), - ("dir/two.ts".to_string(), vec![14..18]), + (separator!("dir/one.rs").to_string(), vec![8..12]), + (separator!("dir/one.ts").to_string(), vec![14..18]), + (separator!("dir/two.rs").to_string(), vec![8..12]), + (separator!("dir/two.ts").to_string(), vec![14..18]), ]), "If no exclusions match, all files should be returned" ); @@ -4499,8 +4534,8 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/one.ts".to_string(), vec![14..18]), - ("dir/two.ts".to_string(), vec![14..18]), + (separator!("dir/one.ts").to_string(), vec![14..18]), + (separator!("dir/two.ts").to_string(), vec![14..18]), ]), "Rust exclusion search should give only TypeScript files" ); @@ -4522,8 +4557,8 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/one.rs".to_string(), vec![8..12]), - ("dir/two.rs".to_string(), vec![8..12]), + (separator!("dir/one.rs").to_string(), vec![8..12]), + (separator!("dir/two.rs").to_string(), vec![8..12]), ]), "TypeScript exclusion search should give only Rust files, even if other exclusions don't match anything" ); @@ -4558,7 +4593,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": r#"// Rust file one"#, "one.ts": r#"// TypeScript file one"#, @@ -4567,7 +4602,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; assert!( search( @@ -4649,8 +4684,8 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex .await .unwrap(), HashMap::from_iter([ - ("dir/one.ts".to_string(), vec![14..18]), - ("dir/two.ts".to_string(), vec![14..18]), + (separator!("dir/one.ts").to_string(), vec![14..18]), + (separator!("dir/two.ts").to_string(), vec![14..18]), ]), "Non-intersecting TypeScript inclusions and Rust exclusions should return TypeScript files" ); @@ -4662,7 +4697,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/worktree-a", + path!("/worktree-a"), json!({ "haystack.rs": r#"// NEEDLE"#, "haystack.ts": r#"// NEEDLE"#, @@ -4670,7 +4705,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo ) .await; fs.insert_tree( - "/worktree-b", + path!("/worktree-b"), json!({ "haystack.rs": r#"// NEEDLE"#, "haystack.ts": r#"// NEEDLE"#, @@ -4680,7 +4715,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo let project = Project::test( fs.clone(), - ["/worktree-a".as_ref(), "/worktree-b".as_ref()], + [path!("/worktree-a").as_ref(), path!("/worktree-b").as_ref()], cx, ) .await; @@ -4702,7 +4737,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo ) .await .unwrap(), - HashMap::from_iter([("worktree-a/haystack.rs".to_string(), vec![3..9])]), + HashMap::from_iter([(separator!("worktree-a/haystack.rs").to_string(), vec![3..9])]), "should only return results from included worktree" ); assert_eq!( @@ -4722,7 +4757,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo ) .await .unwrap(), - HashMap::from_iter([("worktree-b/haystack.rs".to_string(), vec![3..9])]), + HashMap::from_iter([(separator!("worktree-b/haystack.rs").to_string(), vec![3..9])]), "should only return results from included worktree" ); @@ -4744,8 +4779,8 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo .await .unwrap(), HashMap::from_iter([ - ("worktree-a/haystack.ts".to_string(), vec![3..9]), - ("worktree-b/haystack.ts".to_string(), vec![3..9]) + (separator!("worktree-a/haystack.ts").to_string(), vec![3..9]), + (separator!("worktree-b/haystack.ts").to_string(), vec![3..9]) ]), "should return results from both worktrees" ); @@ -4757,7 +4792,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ ".git": {}, ".gitignore": "**/target\n/node_modules\n", @@ -4778,7 +4813,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) { }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let query = "key"; assert_eq!( @@ -4798,11 +4833,11 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) { ) .await .unwrap(), - HashMap::from_iter([("dir/package.json".to_string(), vec![8..11])]), + HashMap::from_iter([(separator!("dir/package.json").to_string(), vec![8..11])]), "Only one non-ignored file should have the query" ); - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; assert_eq!( search( &project, @@ -4821,19 +4856,22 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([ - ("dir/package.json".to_string(), vec![8..11]), - ("dir/target/index.txt".to_string(), vec![6..9]), + (separator!("dir/package.json").to_string(), vec![8..11]), + (separator!("dir/target/index.txt").to_string(), vec![6..9]), ( - "dir/node_modules/prettier/package.json".to_string(), + separator!("dir/node_modules/prettier/package.json").to_string(), vec![9..12] ), ( - "dir/node_modules/prettier/index.ts".to_string(), + separator!("dir/node_modules/prettier/index.ts").to_string(), vec![15..18] ), - ("dir/node_modules/eslint/index.ts".to_string(), vec![13..16]), ( - "dir/node_modules/eslint/package.json".to_string(), + separator!("dir/node_modules/eslint/index.ts").to_string(), + vec![13..16] + ), + ( + separator!("dir/node_modules/eslint/package.json").to_string(), vec![8..11] ), ]), @@ -4842,7 +4880,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) { let files_to_include = PathMatcher::new(&["node_modules/prettier/**".to_owned()]).unwrap(); let files_to_exclude = PathMatcher::new(&["*.ts".to_owned()]).unwrap(); - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; assert_eq!( search( &project, @@ -4861,7 +4899,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) { .await .unwrap(), HashMap::from_iter([( - "dir/node_modules/prettier/package.json".to_string(), + separator!("dir/node_modules/prettier/package.json").to_string(), vec![9..12] )]), "With search including ignored prettier directory and excluding TS files, only one file should be found" @@ -4944,14 +4982,14 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.tsx": "a", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(tsx_lang()); @@ -5009,7 +5047,9 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) { ]; let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.tsx", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.tsx"), cx) + }) .await .unwrap(); cx.executor().run_until_parked(); @@ -5095,14 +5135,14 @@ async fn test_hovers_with_empty_parts(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.ts": "a", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); @@ -5118,7 +5158,9 @@ async fn test_hovers_with_empty_parts(cx: &mut gpui::TestAppContext) { ); let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx) + }) .await .unwrap(); cx.executor().run_until_parked(); @@ -5165,14 +5207,14 @@ async fn test_code_actions_only_kinds(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.ts": "a", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(typescript_lang()); @@ -5188,7 +5230,9 @@ async fn test_code_actions_only_kinds(cx: &mut gpui::TestAppContext) { ); let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.ts", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.ts"), cx) + }) .await .unwrap(); cx.executor().run_until_parked(); @@ -5243,14 +5287,14 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) { let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a.tsx": "a", }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(tsx_lang()); @@ -5309,7 +5353,9 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) { ]; let (buffer, _handle) = project - .update(cx, |p, cx| p.open_local_buffer_with_lsp("/dir/a.tsx", cx)) + .update(cx, |p, cx| { + p.open_local_buffer_with_lsp(path!("/dir/a.tsx"), cx) + }) .await .unwrap(); cx.executor().run_until_parked(); diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index 3e7c9dd644..b727d1365b 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -18,7 +18,7 @@ use task::{ ResolvedTask, TaskContext, TaskId, TaskTemplate, TaskTemplates, TaskVariables, VariableName, }; use text::{Point, ToPoint}; -use util::{post_inc, NumericPrefixWithSuffix, ResultExt as _}; +use util::{paths::PathExt as _, post_inc, NumericPrefixWithSuffix, ResultExt as _}; use worktree::WorktreeId; use crate::worktree_store::WorktreeStore; @@ -470,7 +470,7 @@ impl ContextProvider for BasicContextProvider { let current_file = buffer .file() .and_then(|file| file.as_local()) - .map(|file| file.abs_path(cx).to_string_lossy().to_string()); + .map(|file| file.abs_path(cx).to_sanitized_string()); let Point { row, column } = location.range.start.to_point(&buffer_snapshot); let row = row + 1; let column = column + 1; @@ -502,14 +502,14 @@ impl ContextProvider for BasicContextProvider { if let Some(Some(worktree_path)) = worktree_root_dir { task_variables.insert( VariableName::WorktreeRoot, - worktree_path.to_string_lossy().to_string(), + worktree_path.to_sanitized_string(), ); if let Some(full_path) = current_file.as_ref() { let relative_path = pathdiff::diff_paths(full_path, worktree_path); if let Some(relative_path) = relative_path { task_variables.insert( VariableName::RelativeFile, - relative_path.to_string_lossy().into_owned(), + relative_path.to_sanitized_string(), ); } } diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 7baee66f69..b2c0ab8923 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1106,8 +1106,13 @@ impl ProjectPanel { let worktree_id = edit_state.worktree_id; let is_new_entry = edit_state.is_new_entry(); let filename = self.filename_editor.read(cx).text(cx); - edit_state.is_dir = edit_state.is_dir - || (edit_state.is_new_entry() && filename.ends_with(std::path::MAIN_SEPARATOR)); + #[cfg(not(target_os = "windows"))] + let filename_indicates_dir = filename.ends_with("/"); + // On Windows, path separator could be either `/` or `\`. + #[cfg(target_os = "windows")] + let filename_indicates_dir = filename.ends_with("/") || filename.ends_with("\\"); + edit_state.is_dir = + edit_state.is_dir || (edit_state.is_new_entry() && filename_indicates_dir); let is_dir = edit_state.is_dir; let worktree = self.project.read(cx).worktree_for_id(worktree_id, cx)?; let entry = worktree.read(cx).entry_for_id(edit_state.entry_id)?.clone(); @@ -4793,6 +4798,7 @@ mod tests { use serde_json::json; use settings::SettingsStore; use std::path::{Path, PathBuf}; + use util::{path, separator}; use workspace::{ item::{Item, ProjectItem}, register_project_item, AppState, @@ -4894,7 +4900,7 @@ mod tests { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -4905,7 +4911,7 @@ mod tests { ) .await; - let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/src").as_ref()], cx).await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace, cx); @@ -5066,7 +5072,7 @@ mod tests { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/root1", + path!("/root1"), json!({ "dir_1": { "nested_dir_1": { @@ -5088,7 +5094,7 @@ mod tests { ) .await; fs.insert_tree( - "/root2", + path!("/root2"), json!({ "dir_2": { "file_1.java": "// File contents", @@ -5097,7 +5103,12 @@ mod tests { ) .await; - let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; + let project = Project::test( + fs.clone(), + [path!("/root1").as_ref(), path!("/root2").as_ref()], + cx, + ) + .await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace, cx); @@ -5115,10 +5126,10 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..10, cx), &[ - "v root1", - " > dir_1/nested_dir_1/nested_dir_2/nested_dir_3", - "v root2", - " > dir_2", + separator!("v root1"), + separator!(" > dir_1/nested_dir_1/nested_dir_2/nested_dir_3"), + separator!("v root2"), + separator!(" > dir_2"), ] ); @@ -5130,14 +5141,14 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..10, cx), &[ - "v root1", - " v dir_1/nested_dir_1/nested_dir_2/nested_dir_3 <== selected", - " > nested_dir_4/nested_dir_5", - " file_a.java", - " file_b.java", - " file_c.java", - "v root2", - " > dir_2", + separator!("v root1"), + separator!(" v dir_1/nested_dir_1/nested_dir_2/nested_dir_3 <== selected"), + separator!(" > nested_dir_4/nested_dir_5"), + separator!(" file_a.java"), + separator!(" file_b.java"), + separator!(" file_c.java"), + separator!("v root2"), + separator!(" > dir_2"), ] ); @@ -5149,31 +5160,31 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..10, cx), &[ - "v root1", - " v dir_1/nested_dir_1/nested_dir_2/nested_dir_3", - " v nested_dir_4/nested_dir_5 <== selected", - " file_d.java", - " file_a.java", - " file_b.java", - " file_c.java", - "v root2", - " > dir_2", + separator!("v root1"), + separator!(" v dir_1/nested_dir_1/nested_dir_2/nested_dir_3"), + separator!(" v nested_dir_4/nested_dir_5 <== selected"), + separator!(" file_d.java"), + separator!(" file_a.java"), + separator!(" file_b.java"), + separator!(" file_c.java"), + separator!("v root2"), + separator!(" > dir_2"), ] ); toggle_expand_dir(&panel, "root2/dir_2", cx); assert_eq!( visible_entries_as_strings(&panel, 0..10, cx), &[ - "v root1", - " v dir_1/nested_dir_1/nested_dir_2/nested_dir_3", - " v nested_dir_4/nested_dir_5", - " file_d.java", - " file_a.java", - " file_b.java", - " file_c.java", - "v root2", - " v dir_2 <== selected", - " file_1.java", + separator!("v root1"), + separator!(" v dir_1/nested_dir_1/nested_dir_2/nested_dir_3"), + separator!(" v nested_dir_4/nested_dir_5"), + separator!(" file_d.java"), + separator!(" file_a.java"), + separator!(" file_b.java"), + separator!(" file_c.java"), + separator!("v root2"), + separator!(" v dir_2 <== selected"), + separator!(" file_1.java"), ] ); } @@ -5682,7 +5693,7 @@ mod tests { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/root1", + path!("/root1"), json!({ ".dockerignore": "", ".git": { @@ -5692,7 +5703,7 @@ mod tests { ) .await; - let project = Project::test(fs.clone(), ["/root1".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/root1").as_ref()], cx).await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace, cx); @@ -5727,9 +5738,10 @@ mod tests { ); let confirm = panel.update_in(cx, |panel, window, cx| { + // If we want to create a subdirectory, there should be no prefix slash. panel .filename_editor - .update(cx, |editor, cx| editor.set_text("/new_dir/", window, cx)); + .update(cx, |editor, cx| editor.set_text("new_dir/", window, cx)); panel.confirm_edit(window, cx).unwrap() }); @@ -5738,14 +5750,14 @@ mod tests { &[ "v root1", " > .git", - " [PROCESSING: '/new_dir/'] <== selected", + " [PROCESSING: 'new_dir/'] <== selected", " .dockerignore", ] ); confirm.await.unwrap(); assert_eq!( - visible_entries_as_strings(&panel, 0..13, cx), + visible_entries_as_strings(&panel, 0..10, cx), &[ "v root1", " > .git", @@ -5753,6 +5765,54 @@ mod tests { " .dockerignore", ] ); + + // Test filename with whitespace + select_path(&panel, "root1", cx); + panel.update_in(cx, |panel, window, cx| panel.new_file(&NewFile, window, cx)); + let confirm = panel.update_in(cx, |panel, window, cx| { + // If we want to create a subdirectory, there should be no prefix slash. + panel + .filename_editor + .update(cx, |editor, cx| editor.set_text("new dir 2/", window, cx)); + panel.confirm_edit(window, cx).unwrap() + }); + confirm.await.unwrap(); + assert_eq!( + visible_entries_as_strings(&panel, 0..10, cx), + &[ + "v root1", + " > .git", + " v new dir 2 <== selected", + " v new_dir", + " .dockerignore", + ] + ); + + // Test filename ends with "\" + #[cfg(target_os = "windows")] + { + select_path(&panel, "root1", cx); + panel.update_in(cx, |panel, window, cx| panel.new_file(&NewFile, window, cx)); + let confirm = panel.update_in(cx, |panel, window, cx| { + // If we want to create a subdirectory, there should be no prefix slash. + panel + .filename_editor + .update(cx, |editor, cx| editor.set_text("new_dir_3\\", window, cx)); + panel.confirm_edit(window, cx).unwrap() + }); + confirm.await.unwrap(); + assert_eq!( + visible_entries_as_strings(&panel, 0..10, cx), + &[ + "v root1", + " > .git", + " v new dir 2", + " v new_dir", + " v new_dir_3 <== selected", + " .dockerignore", + ] + ); + } } #[gpui::test] @@ -6409,7 +6469,7 @@ mod tests { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/src", + path!("/src"), json!({ "test": { "first.rs": "// First Rust file", @@ -6420,7 +6480,7 @@ mod tests { ) .await; - let project = Project::test(fs.clone(), ["/src".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/src").as_ref()], cx).await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace, cx); @@ -8545,7 +8605,7 @@ mod tests { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/root", + path!("/root"), json!({ ".gitignore": "**/ignored_dir\n**/ignored_nested", "dir1": { @@ -8573,7 +8633,7 @@ mod tests { ) .await; - let project = Project::test(fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace, cx); @@ -8602,12 +8662,12 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " > empty1/empty2/empty3", - " > ignored_dir", - " > subdir1", - " .gitignore", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" > empty1/empty2/empty3"), + separator!(" > ignored_dir"), + separator!(" > subdir1"), + separator!(" .gitignore"), ], "Should show first level with auto-folded dirs and ignored dir visible" ); @@ -8624,18 +8684,18 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " v empty1", - " v empty2", - " v empty3", - " file.txt", - " > ignored_dir", - " v subdir1", - " > ignored_nested", - " file1.txt", - " file2.txt", - " .gitignore", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" v empty1"), + separator!(" v empty2"), + separator!(" v empty3"), + separator!(" file.txt"), + separator!(" > ignored_dir"), + separator!(" v subdir1"), + separator!(" > ignored_nested"), + separator!(" file1.txt"), + separator!(" file2.txt"), + separator!(" .gitignore"), ], "After expand_all with auto-fold: should not expand ignored_dir, should expand folded dirs, and should not expand ignored_nested" ); @@ -8660,12 +8720,12 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " > empty1", - " > ignored_dir", - " > subdir1", - " .gitignore", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" > empty1"), + separator!(" > ignored_dir"), + separator!(" > subdir1"), + separator!(" .gitignore"), ], "With auto-fold disabled: should show all directories separately" ); @@ -8682,18 +8742,18 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " v empty1", - " v empty2", - " v empty3", - " file.txt", - " > ignored_dir", - " v subdir1", - " > ignored_nested", - " file1.txt", - " file2.txt", - " .gitignore", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" v empty1"), + separator!(" v empty2"), + separator!(" v empty3"), + separator!(" file.txt"), + separator!(" > ignored_dir"), + separator!(" v subdir1"), + separator!(" > ignored_nested"), + separator!(" file1.txt"), + separator!(" file2.txt"), + separator!(" .gitignore"), ], "After expand_all without auto-fold: should expand all dirs normally, \ expand ignored_dir itself but not its subdirs, and not expand ignored_nested" @@ -8712,20 +8772,20 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " v empty1", - " v empty2", - " v empty3", - " file.txt", - " v ignored_dir", - " v subdir", - " deep_file.txt", - " v subdir1", - " > ignored_nested", - " file1.txt", - " file2.txt", - " .gitignore", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" v empty1"), + separator!(" v empty2"), + separator!(" v empty3"), + separator!(" file.txt"), + separator!(" v ignored_dir"), + separator!(" v subdir"), + separator!(" deep_file.txt"), + separator!(" v subdir1"), + separator!(" > ignored_nested"), + separator!(" file1.txt"), + separator!(" file2.txt"), + separator!(" .gitignore"), ], "After expand_all on ignored_dir: should expand all contents of the ignored directory" ); @@ -8737,7 +8797,7 @@ mod tests { let fs = FakeFs::new(cx.executor().clone()); fs.insert_tree( - "/root", + path!("/root"), json!({ "dir1": { "subdir1": { @@ -8759,7 +8819,7 @@ mod tests { ) .await; - let project = Project::test(fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx)); let cx = &mut VisualTestContext::from_window(*workspace, cx); @@ -8776,15 +8836,15 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1", - " v subdir1", - " v nested1", - " file1.txt", - " file2.txt", - " v subdir2 <== selected", - " file4.txt", - " > dir2", + separator!("v root"), + separator!(" v dir1"), + separator!(" v subdir1"), + separator!(" v nested1"), + separator!(" file1.txt"), + separator!(" file2.txt"), + separator!(" v subdir2 <== selected"), + separator!(" file4.txt"), + separator!(" > dir2"), ], "Initial state with everything expanded" ); @@ -8826,13 +8886,13 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1", - " v subdir1/nested1 <== selected", - " file1.txt", - " file2.txt", - " > subdir2", - " > dir2/single_file", + separator!("v root"), + separator!(" v dir1"), + separator!(" v subdir1/nested1 <== selected"), + separator!(" file1.txt"), + separator!(" file2.txt"), + separator!(" > subdir2"), + separator!(" > dir2/single_file"), ], "Initial state with some dirs expanded" ); @@ -8849,11 +8909,11 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " > subdir1/nested1", - " > subdir2", - " > dir2/single_file", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" > subdir1/nested1"), + separator!(" > subdir2"), + separator!(" > dir2/single_file"), ], "Subdirs should be collapsed and folded with auto-fold enabled" ); @@ -8881,14 +8941,14 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1", - " v subdir1", - " v nested1 <== selected", - " file1.txt", - " file2.txt", - " > subdir2", - " > dir2", + separator!("v root"), + separator!(" v dir1"), + separator!(" v subdir1"), + separator!(" v nested1 <== selected"), + separator!(" file1.txt"), + separator!(" file2.txt"), + separator!(" > subdir2"), + separator!(" > dir2"), ], "Initial state with some dirs expanded and auto-fold disabled" ); @@ -8905,11 +8965,11 @@ mod tests { assert_eq!( visible_entries_as_strings(&panel, 0..20, cx), &[ - "v root", - " v dir1 <== selected", - " > subdir1", - " > subdir2", - " > dir2", + separator!("v root"), + separator!(" v dir1 <== selected"), + separator!(" > subdir1"), + separator!(" > subdir2"), + separator!(" > dir2"), ], "Subdirs should be collapsed but not folded with auto-fold disabled" ); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 2b2462ef25..7ae87aeff2 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -272,15 +272,17 @@ mod tests { use serde_json::json; use settings::SettingsStore; use std::{path::Path, sync::Arc}; + use util::path; #[gpui::test] async fn test_project_symbols(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); - fs.insert_tree("/dir", json!({ "test.rs": "" })).await; + fs.insert_tree(path!("/dir"), json!({ "test.rs": "" })) + .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let language_registry = project.read_with(cx, |project, _| project.languages().clone()); language_registry.add(Arc::new(Language::new( @@ -299,7 +301,7 @@ mod tests { let _buffer = project .update(cx, |project, cx| { - project.open_local_buffer_with_lsp("/dir/test.rs", cx) + project.open_local_buffer_with_lsp(path!("/dir/test.rs"), cx) }) .await .unwrap(); @@ -307,9 +309,9 @@ mod tests { // Set up fake language server to return fuzzy matches against // a fixed set of symbol names. let fake_symbols = [ - symbol("one", "/external"), - symbol("ton", "/dir/test.rs"), - symbol("uno", "/dir/test.rs"), + symbol("one", path!("/external")), + symbol("ton", path!("/dir/test.rs")), + symbol("uno", path!("/dir/test.rs")), ]; let fake_server = fake_servers.next().await.unwrap(); fake_server.handle_request::( diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 1d21d5860e..6dc394b2f2 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -595,6 +595,7 @@ mod tests { use project::{project_settings::ProjectSettings, Project}; use serde_json::json; use settings::SettingsStore; + use util::path; use workspace::{open_paths, AppState}; use super::*; @@ -615,7 +616,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/dir", + path!("/dir"), json!({ "main.ts": "a" }), @@ -623,7 +624,7 @@ mod tests { .await; cx.update(|cx| { open_paths( - &[PathBuf::from("/dir/main.ts")], + &[PathBuf::from(path!("/dir/main.ts"))], app_state, workspace::OpenOptions::default(), cx, diff --git a/crates/refineable/derive_refineable/Cargo.toml b/crates/refineable/derive_refineable/Cargo.toml index 62669c610c..8ec8de31fd 100644 --- a/crates/refineable/derive_refineable/Cargo.toml +++ b/crates/refineable/derive_refineable/Cargo.toml @@ -14,6 +14,6 @@ proc-macro = true doctest = false [dependencies] -syn = "1.0.72" -quote = "1.0.9" -proc-macro2 = "1.0.66" +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index be18bad293..b39df8edce 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -1,3 +1,6 @@ +/// todo(windows) +/// The tests in this file assume that server_cx is running on Windows too. +/// We neead to find a way to test Windows-Non-Windows interactions. use crate::headless_project::HeadlessProject; use client::{Client, UserStore}; use clock::FakeSystemClock; @@ -24,12 +27,13 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; +use util::{path, separator}; #[gpui::test] async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -45,14 +49,14 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test ) .await; fs.set_index_for_repo( - Path::new("/code/project1/.git"), + Path::new(path!("/code/project1/.git")), &[("src/lib.rs".into(), "fn one() -> usize { 0 }".into())], ); let (project, _headless) = init_test(&fs, cx, server_cx).await; let (worktree, _) = project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap(); @@ -113,7 +117,7 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test // A new file is created in the remote filesystem. The user // sees the new file. fs.save( - "/code/project1/src/main.rs".as_ref(), + path!("/code/project1/src/main.rs").as_ref(), &"fn main() {}".into(), Default::default(), ) @@ -134,8 +138,8 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test // A file that is currently open in a buffer is renamed. fs.rename( - "/code/project1/src/lib.rs".as_ref(), - "/code/project1/src/lib2.rs".as_ref(), + path!("/code/project1/src/lib.rs").as_ref(), + path!("/code/project1/src/lib2.rs").as_ref(), Default::default(), ) .await @@ -146,7 +150,7 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test }); fs.set_index_for_repo( - Path::new("/code/project1/.git"), + Path::new(path!("/code/project1/.git")), &[("src/lib2.rs".into(), "fn one() -> usize { 100 }".into())], ); cx.executor().run_until_parked(); @@ -162,7 +166,7 @@ async fn test_basic_remote_editing(cx: &mut TestAppContext, server_cx: &mut Test async fn test_remote_project_search(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -179,7 +183,7 @@ async fn test_remote_project_search(cx: &mut TestAppContext, server_cx: &mut Tes project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap(); @@ -210,7 +214,7 @@ async fn test_remote_project_search(cx: &mut TestAppContext, server_cx: &mut Tes buffer.update(&mut cx, |buffer, cx| { assert_eq!( buffer.file().unwrap().full_path(cx).to_string_lossy(), - "project1/README.md" + separator!("project1/README.md") ) }); @@ -368,7 +372,7 @@ async fn test_remote_settings(cx: &mut TestAppContext, server_cx: &mut TestAppCo async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -384,7 +388,7 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext let (project, headless) = init_test(&fs, cx, server_cx).await; fs.insert_tree( - "/code/project1/.zed", + path!("/code/project1/.zed"), json!({ "settings.json": r#" { @@ -431,7 +435,7 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext let worktree_id = project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap() @@ -512,7 +516,7 @@ async fn test_remote_lsp(cx: &mut TestAppContext, server_cx: &mut TestAppContext Ok(Some(lsp::WorkspaceEdit { changes: Some( [( - lsp::Url::from_file_path("/code/project1/src/lib.rs").unwrap(), + lsp::Url::from_file_path(path!("/code/project1/src/lib.rs")).unwrap(), vec![lsp::TextEdit::new( lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(0, 6)), "two".to_string(), @@ -545,7 +549,7 @@ async fn test_remote_cancel_language_server_work( ) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -561,7 +565,7 @@ async fn test_remote_cancel_language_server_work( let (project, headless) = init_test(&fs, cx, server_cx).await; fs.insert_tree( - "/code/project1/.zed", + path!("/code/project1/.zed"), json!({ "settings.json": r#" { @@ -608,7 +612,7 @@ async fn test_remote_cancel_language_server_work( let worktree_id = project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap() @@ -708,7 +712,7 @@ async fn test_remote_cancel_language_server_work( async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -724,7 +728,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont let (project, _headless) = init_test(&fs, cx, server_cx).await; let (worktree, _) = project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap(); @@ -739,7 +743,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont .unwrap(); fs.save( - &PathBuf::from("/code/project1/src/lib.rs"), + &PathBuf::from(path!("/code/project1/src/lib.rs")), &("bangles".to_string().into()), LineEnding::Unix, ) @@ -754,7 +758,7 @@ async fn test_remote_reload(cx: &mut TestAppContext, server_cx: &mut TestAppCont }); fs.save( - &PathBuf::from("/code/project1/src/lib.rs"), + &PathBuf::from(path!("/code/project1/src/lib.rs")), &("bloop".to_string().into()), LineEnding::Unix, ) @@ -786,7 +790,7 @@ async fn test_remote_resolve_path_in_buffer( ) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -802,7 +806,7 @@ async fn test_remote_resolve_path_in_buffer( let (project, _headless) = init_test(&fs, cx, server_cx).await; let (worktree, _) = project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap(); @@ -818,14 +822,14 @@ async fn test_remote_resolve_path_in_buffer( let path = project .update(cx, |project, cx| { - project.resolve_path_in_buffer("/code/project1/README.md", &buffer, cx) + project.resolve_path_in_buffer(path!("/code/project1/README.md"), &buffer, cx) }) .await .unwrap(); assert!(path.is_file()); assert_eq!( path.abs_path().unwrap().to_string_lossy(), - "/code/project1/README.md" + path!("/code/project1/README.md") ); let path = project @@ -1013,7 +1017,7 @@ async fn test_adding_then_removing_then_adding_worktrees( async fn test_open_server_settings(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -1035,7 +1039,9 @@ async fn test_open_server_settings(cx: &mut TestAppContext, server_cx: &mut Test cx.update(|cx| { assert_eq!( buffer.read(cx).text(), - initial_server_settings_content().to_string() + initial_server_settings_content() + .to_string() + .replace("\r\n", "\n") ) }) } @@ -1044,7 +1050,7 @@ async fn test_open_server_settings(cx: &mut TestAppContext, server_cx: &mut Test async fn test_reconnect(cx: &mut TestAppContext, server_cx: &mut TestAppContext) { let fs = FakeFs::new(server_cx.executor()); fs.insert_tree( - "/code", + path!("/code"), json!({ "project1": { ".git": {}, @@ -1061,7 +1067,7 @@ async fn test_reconnect(cx: &mut TestAppContext, server_cx: &mut TestAppContext) let (worktree, _) = project .update(cx, |project, cx| { - project.find_or_create_worktree("/code/project1", true, cx) + project.find_or_create_worktree(path!("/code/project1"), true, cx) }) .await .unwrap(); @@ -1091,7 +1097,9 @@ async fn test_reconnect(cx: &mut TestAppContext, server_cx: &mut TestAppContext) .unwrap(); assert_eq!( - fs.load("/code/project1/src/lib.rs".as_ref()).await.unwrap(), + fs.load(path!("/code/project1/src/lib.rs").as_ref()) + .await + .unwrap(), "fn one() -> usize { 100 }" ); } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 3c40eecb2f..3fe85f13f2 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -2188,6 +2188,7 @@ pub mod tests { use project::FakeFs; use serde_json::json; use settings::SettingsStore; + use util::path; use workspace::DeploySearch; #[gpui::test] @@ -3313,13 +3314,13 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": "const ONE: usize = 1;", }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let worktree_id = project.update(cx, |this, cx| { this.worktrees(cx).next().unwrap().read(cx).id() }); @@ -3537,13 +3538,13 @@ pub mod tests { // Setup 2 panes, both with a file open and one with a project search. let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": "const ONE: usize = 1;", }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let worktree_id = project.update(cx, |this, cx| { this.worktrees(cx).next().unwrap().read(cx).id() }); @@ -3771,13 +3772,13 @@ pub mod tests { let fs = FakeFs::new(cx.background_executor.clone()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "one.rs": "const ONE: usize = 1;", }), ) .await; - let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; + let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await; let worktree_id = project.update(cx, |this, cx| { this.worktrees(cx).next().unwrap().read(cx).id() }); diff --git a/crates/semantic_index/Cargo.toml b/crates/semantic_index/Cargo.toml index 221e854195..1f3c40507b 100644 --- a/crates/semantic_index/Cargo.toml +++ b/crates/semantic_index/Cargo.toml @@ -44,9 +44,9 @@ sha2.workspace = true smol.workspace = true theme.workspace = true tree-sitter.workspace = true -ui. workspace = true +ui.workspace = true unindent.workspace = true -util. workspace = true +util.workspace = true workspace.workspace = true worktree.workspace = true diff --git a/crates/semantic_index/src/semantic_index.rs b/crates/semantic_index/src/semantic_index.rs index 0daf4a985a..9345965ccd 100644 --- a/crates/semantic_index/src/semantic_index.rs +++ b/crates/semantic_index/src/semantic_index.rs @@ -279,6 +279,7 @@ mod tests { use settings::SettingsStore; use smol::channel; use std::{future, path::Path, sync::Arc}; + use util::separator; fn init_test(cx: &mut TestAppContext) { env_logger::try_init().ok(); @@ -421,7 +422,10 @@ mod tests { // Find result that is greater than 0.5 let search_result = results.iter().find(|result| result.score > 0.9).unwrap(); - assert_eq!(search_result.path.to_string_lossy(), "fixture/needle.md"); + assert_eq!( + search_result.path.to_string_lossy(), + separator!("fixture/needle.md") + ); let content = cx .update(|cx| { diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 101695508f..622c42d006 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -12,6 +12,7 @@ pub fn test_settings() -> String { crate::default_settings().as_ref(), ) .unwrap(); + #[cfg(not(target_os = "windows"))] util::merge_non_null_json_value_into( serde_json::json!({ "ui_font_family": "Courier", @@ -26,6 +27,21 @@ pub fn test_settings() -> String { }), &mut value, ); + #[cfg(target_os = "windows")] + util::merge_non_null_json_value_into( + serde_json::json!({ + "ui_font_family": "Courier New", + "ui_font_features": {}, + "ui_font_size": 14, + "ui_font_fallback": [], + "buffer_font_family": "Courier New", + "buffer_font_features": {}, + "buffer_font_size": 14, + "buffer_font_fallback": [], + "theme": EMPTY_THEME_NAME, + }), + &mut value, + ); value.as_object_mut().unwrap().remove("languages"); serde_json::to_string(&value).unwrap() } diff --git a/crates/sqlez_macros/Cargo.toml b/crates/sqlez_macros/Cargo.toml index 5959617f72..cff96d0b89 100644 --- a/crates/sqlez_macros/Cargo.toml +++ b/crates/sqlez_macros/Cargo.toml @@ -16,4 +16,4 @@ doctest = false [dependencies] sqlez.workspace = true sqlformat.workspace = true -syn = "1.0" +syn.workspace = true diff --git a/crates/tab_switcher/src/tab_switcher_tests.rs b/crates/tab_switcher/src/tab_switcher_tests.rs index 045879ef06..f1a5b64b10 100644 --- a/crates/tab_switcher/src/tab_switcher_tests.rs +++ b/crates/tab_switcher/src/tab_switcher_tests.rs @@ -5,6 +5,7 @@ use menu::SelectPrev; use project::{Project, ProjectPath}; use serde_json::json; use std::path::Path; +use util::path; use workspace::{AppState, Workspace}; #[ctor::ctor] @@ -24,7 +25,7 @@ async fn test_open_with_prev_tab_selected_and_cycle_on_toggle_action( .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "1.txt": "First file", "2.txt": "Second file", @@ -34,7 +35,7 @@ async fn test_open_with_prev_tab_selected_and_cycle_on_toggle_action( ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -81,7 +82,7 @@ async fn test_open_with_last_tab_selected(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "1.txt": "First file", "2.txt": "Second file", @@ -90,7 +91,7 @@ async fn test_open_with_last_tab_selected(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -172,10 +173,10 @@ async fn test_open_with_single_item(cx: &mut gpui::TestAppContext) { app_state .fs .as_fake() - .insert_tree("/root", json!({"1.txt": "Single file"})) + .insert_tree(path!("/root"), json!({"1.txt": "Single file"})) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -195,7 +196,7 @@ async fn test_close_selected_item(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "1.txt": "First file", "2.txt": "Second file", @@ -203,7 +204,7 @@ async fn test_close_selected_item(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -241,7 +242,7 @@ async fn test_close_preserves_selected_position(cx: &mut gpui::TestAppContext) { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "1.txt": "First file", "2.txt": "Second file", @@ -250,7 +251,7 @@ async fn test_close_preserves_selected_position(cx: &mut gpui::TestAppContext) { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index 4d65f772f2..f0d1c21f1e 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -603,6 +603,7 @@ mod tests { use project::{ContextProviderWithTasks, FakeFs, Project}; use serde_json::json; use task::TaskTemplates; + use util::path; use workspace::CloseInactiveTabsAndPanes; use crate::{modal::Spawn, tests::init_test}; @@ -614,7 +615,7 @@ mod tests { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ ".zed": { "tasks.json": r#"[ @@ -635,7 +636,7 @@ mod tests { ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project, window, cx)); @@ -654,7 +655,7 @@ mod tests { let _ = workspace .update_in(cx, |workspace, window, cx| { - workspace.open_abs_path(PathBuf::from("/dir/a.ts"), true, window, cx) + workspace.open_abs_path(PathBuf::from(path!("/dir/a.ts")), true, window, cx) }) .await .unwrap(); @@ -778,7 +779,7 @@ mod tests { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ ".zed": { "tasks.json": r#"[ @@ -800,7 +801,7 @@ mod tests { ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let (workspace, cx) = cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx)); @@ -819,7 +820,7 @@ mod tests { let _ = workspace .update_in(cx, |workspace, window, cx| { workspace.open_abs_path( - PathBuf::from("/dir/file_with.odd_extension"), + PathBuf::from(path!("/dir/file_with.odd_extension")), true, window, cx, @@ -832,8 +833,8 @@ mod tests { assert_eq!( task_names(&tasks_picker, cx), vec![ - "hello from /dir/file_with.odd_extension:1:1".to_string(), - "opened now: /dir".to_string() + concat!("hello from ", path!("/dir/file_with.odd_extension:1:1")).to_string(), + concat!("opened now: ", path!("/dir")).to_string(), ], "Second opened buffer should fill the context, labels should be trimmed if long enough" ); @@ -846,7 +847,7 @@ mod tests { let second_item = workspace .update_in(cx, |workspace, window, cx| { workspace.open_abs_path( - PathBuf::from("/dir/file_without_extension"), + PathBuf::from(path!("/dir/file_without_extension")), true, window, cx, @@ -868,8 +869,8 @@ mod tests { assert_eq!( task_names(&tasks_picker, cx), vec![ - "hello from /dir/file_without_extension:2:3".to_string(), - "opened now: /dir".to_string() + concat!("hello from ", path!("/dir/file_without_extension:2:3")).to_string(), + concat!("opened now: ", path!("/dir")).to_string(), ], "Opened buffer should fill the context, labels should be trimmed if long enough" ); @@ -885,7 +886,7 @@ mod tests { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ "a1.ts": "// a1", "a2.ts": "// a2", @@ -894,7 +895,7 @@ mod tests { ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; project.read_with(cx, |project, _| { let language_registry = project.languages(); language_registry.add(Arc::new( @@ -955,7 +956,7 @@ mod tests { let _ts_file_1 = workspace .update_in(cx, |workspace, window, cx| { - workspace.open_abs_path(PathBuf::from("/dir/a1.ts"), true, window, cx) + workspace.open_abs_path(PathBuf::from(path!("/dir/a1.ts")), true, window, cx) }) .await .unwrap(); @@ -963,23 +964,28 @@ mod tests { assert_eq!( task_names(&tasks_picker, cx), vec![ - "Another task from file /dir/a1.ts", - "TypeScript task from file /dir/a1.ts", + concat!("Another task from file ", path!("/dir/a1.ts")), + concat!("TypeScript task from file ", path!("/dir/a1.ts")), "Task without variables", ], "Should open spawn TypeScript tasks for the opened file, tasks with most template variables above, all groups sorted alphanumerically" ); + emulate_task_schedule( tasks_picker, &project, - "TypeScript task from file /dir/a1.ts", + concat!("TypeScript task from file ", path!("/dir/a1.ts")), cx, ); let tasks_picker = open_spawn_tasks(&workspace, cx); assert_eq!( task_names(&tasks_picker, cx), - vec!["TypeScript task from file /dir/a1.ts", "Another task from file /dir/a1.ts", "Task without variables"], + vec![ + concat!("TypeScript task from file ", path!("/dir/a1.ts")), + concat!("Another task from file ", path!("/dir/a1.ts")), + "Task without variables", + ], "After spawning the task and getting it into the history, it should be up in the sort as recently used. Tasks with the same labels and context are deduplicated." ); @@ -991,7 +997,7 @@ mod tests { let _ts_file_2 = workspace .update_in(cx, |workspace, window, cx| { - workspace.open_abs_path(PathBuf::from("/dir/a2.ts"), true, window, cx) + workspace.open_abs_path(PathBuf::from(path!("/dir/a2.ts")), true, window, cx) }) .await .unwrap(); @@ -999,10 +1005,10 @@ mod tests { assert_eq!( task_names(&tasks_picker, cx), vec![ - "TypeScript task from file /dir/a1.ts", - "Another task from file /dir/a2.ts", - "TypeScript task from file /dir/a2.ts", - "Task without variables" + concat!("TypeScript task from file ", path!("/dir/a1.ts")), + concat!("Another task from file ", path!("/dir/a2.ts")), + concat!("TypeScript task from file ", path!("/dir/a2.ts")), + "Task without variables", ], "Even when both TS files are open, should only show the history (on the top), and tasks, resolved for the current file" ); @@ -1029,7 +1035,7 @@ mod tests { emulate_task_schedule(tasks_picker, &project, "Rust task", cx); let _ts_file_2 = workspace .update_in(cx, |workspace, window, cx| { - workspace.open_abs_path(PathBuf::from("/dir/a2.ts"), true, window, cx) + workspace.open_abs_path(PathBuf::from(path!("/dir/a2.ts")), true, window, cx) }) .await .unwrap(); @@ -1037,10 +1043,10 @@ mod tests { assert_eq!( task_names(&tasks_picker, cx), vec![ - "TypeScript task from file /dir/a1.ts", - "Another task from file /dir/a2.ts", - "TypeScript task from file /dir/a2.ts", - "Task without variables" + concat!("TypeScript task from file ", path!("/dir/a1.ts")), + concat!("Another task from file ", path!("/dir/a2.ts")), + concat!("TypeScript task from file ", path!("/dir/a2.ts")), + "Task without variables", ], "After closing all but *.rs tabs, running a Rust task and switching back to TS tasks, \ same TS spawn history should be restored" diff --git a/crates/tasks_ui/src/tasks_ui.rs b/crates/tasks_ui/src/tasks_ui.rs index 0b0eb3654c..36cc140993 100644 --- a/crates/tasks_ui/src/tasks_ui.rs +++ b/crates/tasks_ui/src/tasks_ui.rs @@ -262,6 +262,7 @@ mod tests { use serde_json::json; use task::{TaskContext, TaskVariables, VariableName}; use ui::VisualContext; + use util::{path, separator}; use workspace::{AppState, Workspace}; use crate::task_context; @@ -271,7 +272,7 @@ mod tests { init_test(cx); let fs = FakeFs::new(cx.executor()); fs.insert_tree( - "/dir", + path!("/dir"), json!({ ".zed": { "tasks.json": r#"[ @@ -295,7 +296,7 @@ mod tests { }), ) .await; - let project = Project::test(fs, ["/dir".as_ref()], cx).await; + let project = Project::test(fs, [path!("/dir").as_ref()], cx).await; let worktree_store = project.update(cx, |project, _| project.worktree_store().clone()); let rust_language = Arc::new( Language::new( @@ -375,17 +376,18 @@ mod tests { task_context(workspace, window, cx) }) .await; + assert_eq!( first_context, TaskContext { - cwd: Some("/dir".into()), + cwd: Some(path!("/dir").into()), task_variables: TaskVariables::from_iter([ - (VariableName::File, "/dir/rust/b.rs".into()), + (VariableName::File, path!("/dir/rust/b.rs").into()), (VariableName::Filename, "b.rs".into()), - (VariableName::RelativeFile, "rust/b.rs".into()), - (VariableName::Dirname, "/dir/rust".into()), + (VariableName::RelativeFile, separator!("rust/b.rs").into()), + (VariableName::Dirname, path!("/dir/rust").into()), (VariableName::Stem, "b".into()), - (VariableName::WorktreeRoot, "/dir".into()), + (VariableName::WorktreeRoot, path!("/dir").into()), (VariableName::Row, "1".into()), (VariableName::Column, "1".into()), ]), @@ -407,14 +409,14 @@ mod tests { }) .await, TaskContext { - cwd: Some("/dir".into()), + cwd: Some(path!("/dir").into()), task_variables: TaskVariables::from_iter([ - (VariableName::File, "/dir/rust/b.rs".into()), + (VariableName::File, path!("/dir/rust/b.rs").into()), (VariableName::Filename, "b.rs".into()), - (VariableName::RelativeFile, "rust/b.rs".into()), - (VariableName::Dirname, "/dir/rust".into()), + (VariableName::RelativeFile, separator!("rust/b.rs").into()), + (VariableName::Dirname, path!("/dir/rust").into()), (VariableName::Stem, "b".into()), - (VariableName::WorktreeRoot, "/dir".into()), + (VariableName::WorktreeRoot, path!("/dir").into()), (VariableName::Row, "1".into()), (VariableName::Column, "15".into()), (VariableName::SelectedText, "is_i".into()), @@ -433,14 +435,14 @@ mod tests { }) .await, TaskContext { - cwd: Some("/dir".into()), + cwd: Some(path!("/dir").into()), task_variables: TaskVariables::from_iter([ - (VariableName::File, "/dir/a.ts".into()), + (VariableName::File, path!("/dir/a.ts").into()), (VariableName::Filename, "a.ts".into()), (VariableName::RelativeFile, "a.ts".into()), - (VariableName::Dirname, "/dir".into()), + (VariableName::Dirname, path!("/dir").into()), (VariableName::Stem, "a".into()), - (VariableName::WorktreeRoot, "/dir".into()), + (VariableName::WorktreeRoot, path!("/dir").into()), (VariableName::Row, "1".into()), (VariableName::Column, "1".into()), (VariableName::Symbol, "this_is_a_test".into()), diff --git a/crates/ui_macros/Cargo.toml b/crates/ui_macros/Cargo.toml index 7687c2b36b..773c07d238 100644 --- a/crates/ui_macros/Cargo.toml +++ b/crates/ui_macros/Cargo.toml @@ -13,7 +13,7 @@ path = "src/ui_macros.rs" proc-macro = true [dependencies] -proc-macro2 = "1.0.66" -quote = "1.0.9" -syn = { version = "1.0.72", features = ["full", "extra-traits"] } +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true convert_case.workspace = true diff --git a/crates/util/Cargo.toml b/crates/util/Cargo.toml index 213e2fc0d4..06c2c4d8ba 100644 --- a/crates/util/Cargo.toml +++ b/crates/util/Cargo.toml @@ -13,7 +13,7 @@ path = "src/util.rs" doctest = true [features] -test-support = ["tempfile", "git2", "rand"] +test-support = ["tempfile", "git2", "rand", "util_macros"] [dependencies] anyhow.workspace = true @@ -35,6 +35,7 @@ smol.workspace = true take-until.workspace = true tempfile = { workspace = true, optional = true } unicase.workspace = true +util_macros = { workspace = true, optional = true } [target.'cfg(unix)'.dependencies] libc.workspace = true @@ -47,3 +48,4 @@ dunce = "1.0" git2.workspace = true rand.workspace = true tempfile.workspace = true +util_macros.workspace = true diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index ba78d6d06d..275895d228 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -23,6 +23,7 @@ pub trait PathExt { fn compact(&self) -> PathBuf; fn icon_stem_or_suffix(&self) -> Option<&str>; fn extension_or_hidden_file_name(&self) -> Option<&str>; + fn to_sanitized_string(&self) -> String; fn try_from_bytes<'a>(bytes: &'a [u8]) -> anyhow::Result where Self: From<&'a Path>, @@ -94,6 +95,20 @@ impl> PathExt for T { self.as_ref().file_name()?.to_str()?.split('.').last() } + + /// Returns a sanitized string representation of the path. + /// Note, on Windows, this assumes that the path is a valid UTF-8 string and + /// is not a UNC path. + fn to_sanitized_string(&self) -> String { + #[cfg(target_os = "windows")] + { + self.as_ref().to_string_lossy().replace("/", "\\") + } + #[cfg(not(target_os = "windows"))] + { + self.as_ref().to_string_lossy().to_string() + } + } } /// Due to the issue of UNC paths on Windows, which can cause bugs in various parts of Zed, introducing this `SanitizedPath` @@ -115,6 +130,17 @@ impl SanitizedPath { self.0.to_string_lossy().to_string() } + pub fn to_glob_string(&self) -> String { + #[cfg(target_os = "windows")] + { + self.0.to_string_lossy().replace("/", "\\") + } + #[cfg(not(target_os = "windows"))] + { + self.0.to_string_lossy().to_string() + } + } + pub fn join(&self, path: &Self) -> Self { self.0.join(&path.0).into() } @@ -448,14 +474,6 @@ pub fn compare_paths( } } -#[cfg(any(test, feature = "test-support"))] -pub fn replace_path_separator(path: &str) -> String { - #[cfg(target_os = "windows")] - return path.replace("/", std::path::MAIN_SEPARATOR_STR); - #[cfg(not(target_os = "windows"))] - return path.to_string(); -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 77a788aef2..9fd802a09c 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -28,6 +28,8 @@ use unicase::UniCase; use anyhow::{anyhow, Context as _}; pub use take_until::*; +#[cfg(any(test, feature = "test-support"))] +pub use util_macros::{separator, uri}; #[macro_export] macro_rules! debug_panic { @@ -41,6 +43,50 @@ macro_rules! debug_panic { }; } +/// A macro to add "C:" to the beginning of a path literal on Windows, and replace all +/// the separator from `/` to `\`. +/// But on non-Windows platforms, it will return the path literal as is. +/// +/// # Examples +/// ```rust +/// use util::path; +/// +/// let path = path!("/Users/user/file.txt"); +/// #[cfg(target_os = "windows")] +/// assert_eq!(path, "C:\\Users\\user\\file.txt"); +/// #[cfg(not(target_os = "windows"))] +/// assert_eq!(path, "/Users/user/file.txt"); +/// ``` +#[cfg(all(any(test, feature = "test-support"), target_os = "windows"))] +#[macro_export] +macro_rules! path { + ($path:literal) => { + concat!("C:", util::separator!($path)) + }; +} + +/// A macro to add "C:" to the beginning of a path literal on Windows, and replace all +/// the separator from `/` to `\`. +/// But on non-Windows platforms, it will return the path literal as is. +/// +/// # Examples +/// ```rust +/// use util::path; +/// +/// let path = path!("/Users/user/file.txt"); +/// #[cfg(target_os = "windows")] +/// assert_eq!(path, "C:\\Users\\user\\file.txt"); +/// #[cfg(not(target_os = "windows"))] +/// assert_eq!(path, "/Users/user/file.txt"); +/// ``` +#[cfg(all(any(test, feature = "test-support"), not(target_os = "windows")))] +#[macro_export] +macro_rules! path { + ($path:literal) => { + $path + }; +} + pub fn truncate(s: &str, max_chars: usize) -> &str { match s.char_indices().nth(max_chars) { None => s, diff --git a/crates/util_macros/Cargo.toml b/crates/util_macros/Cargo.toml new file mode 100644 index 0000000000..59c8ee9699 --- /dev/null +++ b/crates/util_macros/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "util_macros" +version = "0.1.0" +edition.workspace = true +publish.workspace = true +license = "GPL-3.0-or-later" + +[lints] +workspace = true + +[lib] +path = "src/util_macros.rs" +proc-macro = true +doctest = false + +[dependencies] +quote.workspace = true +syn.workspace = true diff --git a/crates/util_macros/LICENSE-APACHE b/crates/util_macros/LICENSE-APACHE new file mode 120000 index 0000000000..1cd601d0a3 --- /dev/null +++ b/crates/util_macros/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/util_macros/src/util_macros.rs b/crates/util_macros/src/util_macros.rs new file mode 100644 index 0000000000..2baba2f473 --- /dev/null +++ b/crates/util_macros/src/util_macros.rs @@ -0,0 +1,56 @@ +#![cfg_attr(not(target_os = "windows"), allow(unused))] + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, LitStr}; + +/// This macro replaces the path separator `/` with `\` for Windows. +/// But if the target OS is not Windows, the path is returned as is. +/// +/// # Example +/// ```rust +/// # use util_macros::separator; +/// let path = separator!("path/to/file"); +/// #[cfg(target_os = "windows")] +/// assert_eq!(path, "path\\to\\file"); +/// #[cfg(not(target_os = "windows"))] +/// assert_eq!(path, "path/to/file"); +/// ``` +#[proc_macro] +pub fn separator(input: TokenStream) -> TokenStream { + let path = parse_macro_input!(input as LitStr); + let path = path.value(); + + #[cfg(target_os = "windows")] + let path = path.replace("/", "\\"); + + TokenStream::from(quote! { + #path + }) +} + +/// This macro replaces the path prefix `file:///` with `file:///C:/` for Windows. +/// But if the target OS is not Windows, the URI is returned as is. +/// +/// # Example +/// ```rust +/// use util_macros::uri; +/// +/// let uri = uri!("file:///path/to/file"); +/// #[cfg(target_os = "windows")] +/// assert_eq!(uri, "file:///C:/path/to/file"); +/// #[cfg(not(target_os = "windows"))] +/// assert_eq!(uri, "file:///path/to/file"); +/// ``` +#[proc_macro] +pub fn uri(input: TokenStream) -> TokenStream { + let uri = parse_macro_input!(input as LitStr); + let uri = uri.value(); + + #[cfg(target_os = "windows")] + let uri = uri.replace("file:///", "file:///C:/"); + + TokenStream::from(quote! { + #uri + }) +} diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index ef378c0e48..bc1b4ba201 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -1455,6 +1455,7 @@ mod test { use editor::Editor; use gpui::{Context, TestAppContext}; use indoc::indoc; + use util::path; use workspace::Workspace; #[gpui::test] @@ -1551,13 +1552,13 @@ mod test { #[gpui::test] async fn test_command_write(cx: &mut TestAppContext) { let mut cx = VimTestContext::new(cx, true).await; - let path = Path::new("/root/dir/file.rs"); + let path = Path::new(path!("/root/dir/file.rs")); let fs = cx.workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone()); cx.simulate_keystrokes("i @ escape"); cx.simulate_keystrokes(": w enter"); - assert_eq!(fs.load(path).await.unwrap(), "@\n"); + assert_eq!(fs.load(path).await.unwrap().replace("\r\n", "\n"), "@\n"); fs.as_fake().insert_file(path, b"oops\n".to_vec()).await; @@ -1567,12 +1568,12 @@ mod test { assert!(cx.has_pending_prompt()); // "Cancel" cx.simulate_prompt_answer(0); - assert_eq!(fs.load(path).await.unwrap(), "oops\n"); + assert_eq!(fs.load(path).await.unwrap().replace("\r\n", "\n"), "oops\n"); assert!(!cx.has_pending_prompt()); // force overwrite cx.simulate_keystrokes(": w ! enter"); assert!(!cx.has_pending_prompt()); - assert_eq!(fs.load(path).await.unwrap(), "@@\n"); + assert_eq!(fs.load(path).await.unwrap().replace("\r\n", "\n"), "@@\n"); } #[gpui::test] @@ -1664,7 +1665,7 @@ mod test { let file_path = file.as_local().unwrap().abs_path(cx); assert_eq!(text, expected_text); - assert_eq!(file_path.to_str().unwrap(), expected_path); + assert_eq!(file_path, Path::new(expected_path)); } #[gpui::test] @@ -1673,16 +1674,22 @@ mod test { // Assert base state, that we're in /root/dir/file.rs cx.workspace(|workspace, _, cx| { - assert_active_item(workspace, "/root/dir/file.rs", "", cx); + assert_active_item(workspace, path!("/root/dir/file.rs"), "", cx); }); // Insert a new file let fs = cx.workspace(|workspace, _, cx| workspace.project().read(cx).fs().clone()); fs.as_fake() - .insert_file("/root/dir/file2.rs", "This is file2.rs".as_bytes().to_vec()) + .insert_file( + path!("/root/dir/file2.rs"), + "This is file2.rs".as_bytes().to_vec(), + ) .await; fs.as_fake() - .insert_file("/root/dir/file3.rs", "go to file3".as_bytes().to_vec()) + .insert_file( + path!("/root/dir/file3.rs"), + "go to file3".as_bytes().to_vec(), + ) .await; // Put the path to the second file into the currently open buffer @@ -1694,7 +1701,12 @@ mod test { // We now have two items cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 2)); cx.workspace(|workspace, _, cx| { - assert_active_item(workspace, "/root/dir/file2.rs", "This is file2.rs", cx); + assert_active_item( + workspace, + path!("/root/dir/file2.rs"), + "This is file2.rs", + cx, + ); }); // Update editor to point to `file2.rs` @@ -1711,7 +1723,7 @@ mod test { // We now have three items cx.workspace(|workspace, _, cx| assert_eq!(workspace.items(cx).count(), 3)); cx.workspace(|workspace, _, cx| { - assert_active_item(workspace, "/root/dir/file3.rs", "go to file3", cx); + assert_active_item(workspace, path!("/root/dir/file3.rs"), "go to file3", cx); }); } diff --git a/crates/vim/src/normal/paste.rs b/crates/vim/src/normal/paste.rs index eb3acde6dc..e47e48a6b4 100644 --- a/crates/vim/src/normal/paste.rs +++ b/crates/vim/src/normal/paste.rs @@ -696,12 +696,20 @@ mod test { // not testing nvim as it doesn't have a filename cx.simulate_keystrokes("\" % p"); + #[cfg(not(target_os = "windows"))] cx.assert_state( indoc! {" The quick brown dogdir/file.rˇs"}, Mode::Normal, ); + #[cfg(target_os = "windows")] + cx.assert_state( + indoc! {" + The quick brown + dogdir\\file.rˇs"}, + Mode::Normal, + ); } #[gpui::test] diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index 955afbe209..8ba52747d1 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -1319,14 +1319,7 @@ impl LocalWorktree { let settings = self.settings.clone(); let (scan_states_tx, mut scan_states_rx) = mpsc::unbounded(); let background_scanner = cx.background_executor().spawn({ - let abs_path = &snapshot.abs_path; - #[cfg(target_os = "windows")] - let abs_path = abs_path - .as_path() - .canonicalize() - .unwrap_or_else(|_| abs_path.as_path().to_path_buf()); - #[cfg(not(target_os = "windows"))] - let abs_path = abs_path.as_path().to_path_buf(); + let abs_path = snapshot.abs_path.as_path().to_path_buf(); let background = cx.background_executor().clone(); async move { let (events, watcher) = fs.watch(&abs_path, FS_WATCH_LATENCY).await; diff --git a/crates/worktree/src/worktree_tests.rs b/crates/worktree/src/worktree_tests.rs index 533ae7eb87..2cee728aec 100644 --- a/crates/worktree/src/worktree_tests.rs +++ b/crates/worktree/src/worktree_tests.rs @@ -2156,7 +2156,13 @@ const CONFLICT: FileStatus = FileStatus::Unmerged(UnmergedStatus { second_head: UnmergedStatusCode::Updated, }); +// NOTE: +// This test always fails on Windows, because on Windows, unlike on Unix, you can't rename +// a directory which some program has already open. +// This is a limitation of the Windows. +// See: https://stackoverflow.com/questions/41365318/access-is-denied-when-renaming-folder #[gpui::test] +#[cfg_attr(target_os = "windows", ignore)] async fn test_rename_work_directory(cx: &mut TestAppContext) { init_test(cx); cx.executor().allow_parking(); @@ -2184,7 +2190,7 @@ async fn test_rename_work_directory(cx: &mut TestAppContext) { let repo = git_init(&root_path.join("projects/project1")); git_add("a", &repo); git_commit("init", &repo); - std::fs::write(root_path.join("projects/project1/a"), "aa").ok(); + std::fs::write(root_path.join("projects/project1/a"), "aa").unwrap(); cx.read(|cx| tree.read(cx).as_local().unwrap().scan_complete()) .await; @@ -2209,7 +2215,7 @@ async fn test_rename_work_directory(cx: &mut TestAppContext) { root_path.join("projects/project1"), root_path.join("projects/project2"), ) - .ok(); + .unwrap(); tree.flush_fs_events(cx).await; cx.read(|cx| { @@ -2335,7 +2341,13 @@ async fn test_git_repository_for_path(cx: &mut TestAppContext) { }); } +// NOTE: +// This test always fails on Windows, because on Windows, unlike on Unix, you can't rename +// a directory which some program has already open. +// This is a limitation of the Windows. +// See: https://stackoverflow.com/questions/41365318/access-is-denied-when-renaming-folder #[gpui::test] +#[cfg_attr(target_os = "windows", ignore)] async fn test_file_status(cx: &mut TestAppContext) { init_test(cx); cx.executor().allow_parking(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b037e1e703..f9793952f8 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1569,6 +1569,7 @@ mod tests { time::Duration, }; use theme::{ThemeRegistry, ThemeSettings}; + use util::{path, separator}; use workspace::{ item::{Item, ItemHandle}, open_new, open_paths, pane, NewFile, OpenVisible, SaveIntent, SplitDirection, @@ -1737,12 +1738,15 @@ mod tests { app_state .fs .as_fake() - .insert_tree("/root", json!({"a": "hey", "b": "", "dir": {"c": "f"}})) + .insert_tree( + path!("/root"), + json!({"a": "hey", "b": "", "dir": {"c": "f"}}), + ) .await; cx.update(|cx| { open_paths( - &[PathBuf::from("/root/dir")], + &[PathBuf::from(path!("/root/dir"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -1754,7 +1758,7 @@ mod tests { cx.update(|cx| { open_paths( - &[PathBuf::from("/root/a")], + &[PathBuf::from(path!("/root/a"))], app_state.clone(), workspace::OpenOptions { open_new_workspace: Some(false), @@ -1769,7 +1773,7 @@ mod tests { cx.update(|cx| { open_paths( - &[PathBuf::from("/root/dir/c")], + &[PathBuf::from(path!("/root/dir/c"))], app_state.clone(), workspace::OpenOptions { open_new_workspace: Some(true), @@ -1789,12 +1793,15 @@ mod tests { app_state .fs .as_fake() - .insert_tree("/root", json!({"dir1": {"a": "b"}, "dir2": {"c": "d"}})) + .insert_tree( + path!("/root"), + json!({"dir1": {"a": "b"}, "dir2": {"c": "d"}}), + ) .await; cx.update(|cx| { open_paths( - &[PathBuf::from("/root/dir1/a")], + &[PathBuf::from(path!("/root/dir1/a"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -1807,7 +1814,7 @@ mod tests { cx.update(|cx| { open_paths( - &[PathBuf::from("/root/dir2/c")], + &[PathBuf::from(path!("/root/dir2/c"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -1819,7 +1826,7 @@ mod tests { cx.update(|cx| { open_paths( - &[PathBuf::from("/root/dir2")], + &[PathBuf::from(path!("/root/dir2"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -1835,7 +1842,7 @@ mod tests { cx.update(|cx| { open_paths( - &[PathBuf::from("/root/dir2/c")], + &[PathBuf::from(path!("/root/dir2/c"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -1864,12 +1871,12 @@ mod tests { app_state .fs .as_fake() - .insert_tree("/root", json!({"a": "hey"})) + .insert_tree(path!("/root"), json!({"a": "hey"})) .await; cx.update(|cx| { open_paths( - &[PathBuf::from("/root/a")], + &[PathBuf::from(path!("/root/a"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -1951,7 +1958,7 @@ mod tests { // Opening the buffer again doesn't impact the window's edited state. cx.update(|cx| { open_paths( - &[PathBuf::from("/root/a")], + &[PathBuf::from(path!("/root/a"))], app_state, workspace::OpenOptions::default(), cx, @@ -2013,12 +2020,12 @@ mod tests { app_state .fs .as_fake() - .insert_tree("/root", json!({"a": "hey"})) + .insert_tree(path!("/root"), json!({"a": "hey"})) .await; cx.update(|cx| { open_paths( - &[PathBuf::from("/root/a")], + &[PathBuf::from(path!("/root/a"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -2070,7 +2077,7 @@ mod tests { // When we now reopen the window, the edited state and the edited buffer are back cx.update(|cx| { open_paths( - &[PathBuf::from("/root/a")], + &[PathBuf::from(path!("/root/a"))], app_state.clone(), workspace::OpenOptions::default(), cx, @@ -2166,7 +2173,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "a": { "file1": "contents 1", @@ -2177,7 +2184,7 @@ mod tests { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _cx| { project.languages().add(markdown_language()) }); @@ -2298,7 +2305,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/", + path!("/"), json!({ "dir1": { "a.txt": "" @@ -2316,7 +2323,7 @@ mod tests { cx.update(|cx| { open_paths( - &[PathBuf::from("/dir1/")], + &[PathBuf::from(path!("/dir1/"))], app_state, workspace::OpenOptions::default(), cx, @@ -2363,7 +2370,7 @@ mod tests { window .update(cx, |workspace, window, cx| { workspace.open_paths( - vec!["/dir1/a.txt".into()], + vec![path!("/dir1/a.txt").into()], OpenVisible::All, None, window, @@ -2374,7 +2381,12 @@ mod tests { .await; cx.read(|cx| { let workspace = workspace.read(cx); - assert_project_panel_selection(workspace, Path::new("/dir1"), Path::new("a.txt"), cx); + assert_project_panel_selection( + workspace, + Path::new(path!("/dir1")), + Path::new("a.txt"), + cx, + ); assert_eq!( workspace .active_pane() @@ -2393,7 +2405,7 @@ mod tests { window .update(cx, |workspace, window, cx| { workspace.open_paths( - vec!["/dir2/b.txt".into()], + vec![path!("/dir2/b.txt").into()], OpenVisible::All, None, window, @@ -2404,14 +2416,19 @@ mod tests { .await; cx.read(|cx| { let workspace = workspace.read(cx); - assert_project_panel_selection(workspace, Path::new("/dir2/b.txt"), Path::new(""), cx); + assert_project_panel_selection( + workspace, + Path::new(path!("/dir2/b.txt")), + Path::new(""), + cx, + ); let worktree_roots = workspace .worktrees(cx) .map(|w| w.read(cx).as_local().unwrap().abs_path().as_ref()) .collect::>(); assert_eq!( worktree_roots, - vec!["/dir1", "/dir2/b.txt"] + vec![path!("/dir1"), path!("/dir2/b.txt")] .into_iter() .map(Path::new) .collect(), @@ -2434,7 +2451,7 @@ mod tests { window .update(cx, |workspace, window, cx| { workspace.open_paths( - vec!["/dir3".into(), "/dir3/c.txt".into()], + vec![path!("/dir3").into(), path!("/dir3/c.txt").into()], OpenVisible::All, None, window, @@ -2445,14 +2462,19 @@ mod tests { .await; cx.read(|cx| { let workspace = workspace.read(cx); - assert_project_panel_selection(workspace, Path::new("/dir3"), Path::new("c.txt"), cx); + assert_project_panel_selection( + workspace, + Path::new(path!("/dir3")), + Path::new("c.txt"), + cx, + ); let worktree_roots = workspace .worktrees(cx) .map(|w| w.read(cx).as_local().unwrap().abs_path().as_ref()) .collect::>(); assert_eq!( worktree_roots, - vec!["/dir1", "/dir2/b.txt", "/dir3"] + vec![path!("/dir1"), path!("/dir2/b.txt"), path!("/dir3")] .into_iter() .map(Path::new) .collect(), @@ -2474,23 +2496,39 @@ mod tests { // Ensure opening invisibly a file outside an existing worktree adds a new, invisible worktree. window .update(cx, |workspace, window, cx| { - workspace.open_paths(vec!["/d.txt".into()], OpenVisible::None, None, window, cx) + workspace.open_paths( + vec![path!("/d.txt").into()], + OpenVisible::None, + None, + window, + cx, + ) }) .unwrap() .await; cx.read(|cx| { let workspace = workspace.read(cx); - assert_project_panel_selection(workspace, Path::new("/d.txt"), Path::new(""), cx); + assert_project_panel_selection( + workspace, + Path::new(path!("/d.txt")), + Path::new(""), + cx, + ); let worktree_roots = workspace .worktrees(cx) .map(|w| w.read(cx).as_local().unwrap().abs_path().as_ref()) .collect::>(); assert_eq!( worktree_roots, - vec!["/dir1", "/dir2/b.txt", "/dir3", "/d.txt"] - .into_iter() - .map(Path::new) - .collect(), + vec![ + path!("/dir1"), + path!("/dir2/b.txt"), + path!("/dir3"), + path!("/d.txt") + ] + .into_iter() + .map(Path::new) + .collect(), ); let visible_worktree_roots = workspace @@ -2499,7 +2537,7 @@ mod tests { .collect::>(); assert_eq!( visible_worktree_roots, - vec!["/dir1", "/dir2/b.txt", "/dir3"] + vec![path!("/dir1"), path!("/dir2/b.txt"), path!("/dir3")] .into_iter() .map(Path::new) .collect(), @@ -2535,7 +2573,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ ".gitignore": "ignored_dir\n", ".git": { @@ -2560,7 +2598,7 @@ mod tests { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _cx| { project.languages().add(markdown_language()) }); @@ -2569,9 +2607,9 @@ mod tests { let initial_entries = cx.read(|cx| workspace.file_project_paths(cx)); let paths_to_open = [ - Path::new("/root/excluded_dir/file").to_path_buf(), - Path::new("/root/.git/HEAD").to_path_buf(), - Path::new("/root/excluded_dir/ignored_subdir").to_path_buf(), + PathBuf::from(path!("/root/excluded_dir/file")), + PathBuf::from(path!("/root/.git/HEAD")), + PathBuf::from(path!("/root/excluded_dir/ignored_subdir")), ]; let (opened_workspace, new_items) = cx .update(|cx| { @@ -2616,8 +2654,8 @@ mod tests { opened_paths, vec![ None, - Some(".git/HEAD".to_string()), - Some("excluded_dir/file".to_string()), + Some(separator!(".git/HEAD").to_string()), + Some(separator!("excluded_dir/file").to_string()), ], "Excluded files should get opened, excluded dir should not get opened" ); @@ -2643,7 +2681,7 @@ mod tests { opened_buffer_paths.sort(); assert_eq!( opened_buffer_paths, - vec![".git/HEAD".to_string(), "excluded_dir/file".to_string()], + vec![separator!(".git/HEAD").to_string(), separator!("excluded_dir/file").to_string()], "Despite not being present in the worktrees, buffers for excluded files are opened and added to the pane" ); }); @@ -2655,10 +2693,10 @@ mod tests { app_state .fs .as_fake() - .insert_tree("/root", json!({ "a.txt": "" })) + .insert_tree(path!("/root"), json!({ "a.txt": "" })) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _cx| { project.languages().add(markdown_language()) }); @@ -2669,7 +2707,7 @@ mod tests { window .update(cx, |workspace, window, cx| { workspace.open_paths( - vec![PathBuf::from("/root/a.txt")], + vec![PathBuf::from(path!("/root/a.txt"))], OpenVisible::All, None, window, @@ -2693,7 +2731,7 @@ mod tests { app_state .fs .as_fake() - .insert_file("/root/a.txt", b"changed".to_vec()) + .insert_file(path!("/root/a.txt"), b"changed".to_vec()) .await; cx.run_until_parked(); @@ -2721,9 +2759,13 @@ mod tests { #[gpui::test] async fn test_open_and_save_new_file(cx: &mut TestAppContext) { let app_state = init_test(cx); - app_state.fs.create_dir(Path::new("/root")).await.unwrap(); + app_state + .fs + .create_dir(Path::new(path!("/root"))) + .await + .unwrap(); - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _| { project.languages().add(markdown_language()); project.languages().add(rust_lang()); @@ -2766,7 +2808,7 @@ mod tests { .unwrap(); cx.background_executor.run_until_parked(); cx.simulate_new_path_selection(|parent_dir| { - assert_eq!(parent_dir, Path::new("/root")); + assert_eq!(parent_dir, Path::new(path!("/root"))); Some(parent_dir.join("the-new-name.rs")) }); cx.read(|cx| { @@ -2922,7 +2964,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "a": { "file1": "contents 1", @@ -2933,7 +2975,7 @@ mod tests { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _cx| { project.languages().add(markdown_language()) }); @@ -3020,7 +3062,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "a": { "file1": "contents 1\n".repeat(20), @@ -3031,7 +3073,7 @@ mod tests { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _cx| { project.languages().add(markdown_language()) }); @@ -3262,7 +3304,7 @@ mod tests { .unwrap(); app_state .fs - .remove_file(Path::new("/root/a/file2"), Default::default()) + .remove_file(Path::new(path!("/root/a/file2")), Default::default()) .await .unwrap(); cx.background_executor.run_until_parked(); @@ -3403,7 +3445,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "a": { "file1": "", @@ -3415,7 +3457,7 @@ mod tests { ) .await; - let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; + let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await; project.update(cx, |project, _cx| { project.languages().add(markdown_language()) }); diff --git a/crates/zed/src/zed/open_listener.rs b/crates/zed/src/zed/open_listener.rs index 9389ff481e..4fa22fed79 100644 --- a/crates/zed/src/zed/open_listener.rs +++ b/crates/zed/src/zed/open_listener.rs @@ -535,6 +535,7 @@ mod tests { use editor::Editor; use gpui::TestAppContext; use serde_json::json; + use util::path; use workspace::{AppState, Workspace}; use crate::zed::{open_listener::open_local_workspace, tests::init_test}; @@ -547,7 +548,7 @@ mod tests { .fs .as_fake() .insert_tree( - "/root", + path!("/root"), json!({ "dir1": { "file1.txt": "content1", @@ -560,7 +561,7 @@ mod tests { assert_eq!(cx.windows().len(), 0); // First open the workspace directory - open_workspace_file("/root/dir1", None, app_state.clone(), cx).await; + open_workspace_file(path!("/root/dir1"), None, app_state.clone(), cx).await; assert_eq!(cx.windows().len(), 1); let workspace = cx.windows()[0].downcast::().unwrap(); @@ -571,7 +572,7 @@ mod tests { .unwrap(); // Now open a file inside that workspace - open_workspace_file("/root/dir1/file1.txt", None, app_state.clone(), cx).await; + open_workspace_file(path!("/root/dir1/file1.txt"), None, app_state.clone(), cx).await; assert_eq!(cx.windows().len(), 1); workspace @@ -581,7 +582,13 @@ mod tests { .unwrap(); // Now open a file inside that workspace, but tell Zed to open a new window - open_workspace_file("/root/dir1/file1.txt", Some(true), app_state.clone(), cx).await; + open_workspace_file( + path!("/root/dir1/file1.txt"), + Some(true), + app_state.clone(), + cx, + ) + .await; assert_eq!(cx.windows().len(), 2); @@ -599,12 +606,16 @@ mod tests { async fn test_open_workspace_with_nonexistent_files(cx: &mut TestAppContext) { let app_state = init_test(cx); - app_state.fs.as_fake().insert_tree("/root", json!({})).await; + app_state + .fs + .as_fake() + .insert_tree(path!("/root"), json!({})) + .await; assert_eq!(cx.windows().len(), 0); // Test case 1: Open a single file that does not exist yet - open_workspace_file("/root/file5.txt", None, app_state.clone(), cx).await; + open_workspace_file(path!("/root/file5.txt"), None, app_state.clone(), cx).await; assert_eq!(cx.windows().len(), 1); let workspace_1 = cx.windows()[0].downcast::().unwrap(); @@ -616,7 +627,7 @@ mod tests { // Test case 2: Open a single file that does not exist yet, // but tell Zed to add it to the current workspace - open_workspace_file("/root/file6.txt", Some(false), app_state.clone(), cx).await; + open_workspace_file(path!("/root/file6.txt"), Some(false), app_state.clone(), cx).await; assert_eq!(cx.windows().len(), 1); workspace_1 @@ -628,7 +639,7 @@ mod tests { // Test case 3: Open a single file that does not exist yet, // but tell Zed to NOT add it to the current workspace - open_workspace_file("/root/file7.txt", Some(true), app_state.clone(), cx).await; + open_workspace_file(path!("/root/file7.txt"), Some(true), app_state.clone(), cx).await; assert_eq!(cx.windows().len(), 2); let workspace_2 = cx.windows()[1].downcast::().unwrap(); diff --git a/script/exit-ci-if-dev-drive-is-full.ps1 b/script/exit-ci-if-dev-drive-is-full.ps1 new file mode 100644 index 0000000000..98684d58ee --- /dev/null +++ b/script/exit-ci-if-dev-drive-is-full.ps1 @@ -0,0 +1,22 @@ +param ( + [Parameter(Mandatory = $true)] + [int]$MAX_SIZE_IN_GB +) + +$ErrorActionPreference = "Stop" +$PSNativeCommandUseErrorActionPreference = $true +$ProgressPreference = "SilentlyContinue" + +if (-Not (Test-Path -Path "target")) { + Write-Host "target directory does not exist yet" + exit 0 +} + +$current_size_gb = (Get-ChildItem -Recurse -Force -File -Path "target" | Measure-Object -Property Length -Sum).Sum / 1GB + +Write-Host "target directory size: ${current_size_gb}GB. max size: ${MAX_SIZE_IN_GB}GB" + +if ($current_size_gb -gt $MAX_SIZE_IN_GB) { + Write-Host "Dev drive is almost full, increase the size first!" + exit 1 +} diff --git a/script/setup-dev-driver.ps1 b/script/setup-dev-driver.ps1 index 28a9c3ed6c..51aa17c267 100644 --- a/script/setup-dev-driver.ps1 +++ b/script/setup-dev-driver.ps1 @@ -3,7 +3,8 @@ # The current version of the Windows runner is 10.0.20348 which does not support DevDrive option. # Ref: https://learn.microsoft.com/en-us/windows/dev-drive/ -$Volume = New-VHD -Path C:/zed_dev_drive.vhdx -SizeBytes 30GB | +# Currently, total CI requires almost 45GB of space, here we are creating a 60GB drive. +$Volume = New-VHD -Path C:/zed_dev_drive.vhdx -SizeBytes 60GB | Mount-VHD -Passthru | Initialize-Disk -Passthru | New-Partition -AssignDriveLetter -UseMaximumSize |