diff --git a/crates/fs/src/fake_git_repo.rs b/crates/fs/src/fake_git_repo.rs index 83c563fc0dc3dfdb83dec15092c4cf4ac8a41a14..be9b84ff6acd5e13080148f15103b8a21111de7a 100644 --- a/crates/fs/src/fake_git_repo.rs +++ b/crates/fs/src/fake_git_repo.rs @@ -23,6 +23,7 @@ use std::{ path::PathBuf, sync::{Arc, LazyLock}, }; +use text::LineEnding; use util::{paths::PathStyle, rel_path::RelPath}; pub static LOAD_INDEX_TEXT_TASK: LazyLock = LazyLock::new(TaskLabel::new); @@ -452,7 +453,12 @@ impl GitRepository for FakeGitRepository { }) } - fn blame(&self, path: RepoPath, _content: Rope) -> BoxFuture<'_, Result> { + fn blame( + &self, + path: RepoPath, + _content: Rope, + _line_ending: LineEnding, + ) -> BoxFuture<'_, Result> { self.with_state_async(false, move |state| { state .blames diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 5be94ab6302b0a950b91e32dc43da374f0c62f29..e8357e359696bfcfbc7cfd829f84222c1303402a 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -803,7 +803,7 @@ impl Fs for RealFs { } let file = smol::fs::File::create(path).await?; let mut writer = smol::io::BufWriter::with_capacity(buffer_size, file); - for chunk in chunks(text, line_ending) { + for chunk in text::chunks_with_line_ending(text, line_ending) { writer.write_all(chunk.as_bytes()).await?; } writer.flush().await?; @@ -2555,7 +2555,7 @@ impl Fs for FakeFs { async fn save(&self, path: &Path, text: &Rope, line_ending: LineEnding) -> Result<()> { self.simulate_random_delay().await; let path = normalize_path(path); - let content = chunks(text, line_ending).collect::(); + let content = text::chunks_with_line_ending(text, line_ending).collect::(); if let Some(path) = path.parent() { self.create_dir(path).await?; } @@ -2773,25 +2773,6 @@ impl Fs for FakeFs { } } -fn chunks(rope: &Rope, line_ending: LineEnding) -> impl Iterator { - rope.chunks().flat_map(move |chunk| { - let mut newline = false; - let end_with_newline = chunk.ends_with('\n').then_some(line_ending.as_str()); - chunk - .lines() - .flat_map(move |line| { - let ending = if newline { - Some(line_ending.as_str()) - } else { - None - }; - newline = true; - ending.into_iter().chain([line]) - }) - .chain(end_with_newline) - }) -} - pub fn normalize_path(path: &Path) -> PathBuf { let mut components = path.components().peekable(); let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { diff --git a/crates/git/src/blame.rs b/crates/git/src/blame.rs index 6325eacc8201d812d14dfdf4853f4004e22c263e..c3bbeff3f7d15d84b779f2ab92cb89799f63c4e8 100644 --- a/crates/git/src/blame.rs +++ b/crates/git/src/blame.rs @@ -8,7 +8,7 @@ use gpui::SharedString; use serde::{Deserialize, Serialize}; use std::process::Stdio; use std::{ops::Range, path::Path}; -use text::Rope; +use text::{LineEnding, Rope}; use time::OffsetDateTime; use time::UtcOffset; use time::macros::format_description; @@ -35,8 +35,10 @@ impl Blame { working_directory: &Path, path: &RepoPath, content: &Rope, + line_ending: LineEnding, ) -> Result { - let output = run_git_blame(git_binary, working_directory, path, content).await?; + let output = + run_git_blame(git_binary, working_directory, path, content, line_ending).await?; let mut entries = parse_git_blame(&output)?; entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start)); @@ -63,12 +65,12 @@ async fn run_git_blame( working_directory: &Path, path: &RepoPath, contents: &Rope, + line_ending: LineEnding, ) -> Result { let mut child = util::command::new_smol_command(git_binary) .current_dir(working_directory) .arg("blame") .arg("--incremental") - .arg("-w") .arg("--contents") .arg("-") .arg(path.as_unix_str()) @@ -83,7 +85,7 @@ async fn run_git_blame( .as_mut() .context("failed to get pipe to stdin of git blame command")?; - for chunk in contents.chunks() { + for chunk in text::chunks_with_line_ending(contents, line_ending) { stdin.write_all(chunk.as_bytes()).await?; } stdin.flush().await?; diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs index 9f77ddc7cfc8e9e8d4ebca836e12f86496dc7c0b..c3dd0995ff83d4bfdd494e4b5c192ff5999c21f8 100644 --- a/crates/git/src/repository.rs +++ b/crates/git/src/repository.rs @@ -14,6 +14,7 @@ use rope::Rope; use schemars::JsonSchema; use serde::Deserialize; use smol::io::{AsyncBufReadExt, AsyncReadExt, BufReader}; +use text::LineEnding; use std::collections::HashSet; use std::ffi::{OsStr, OsString}; @@ -487,7 +488,12 @@ pub trait GitRepository: Send + Sync { fn show(&self, commit: String) -> BoxFuture<'_, Result>; fn load_commit(&self, commit: String, cx: AsyncApp) -> BoxFuture<'_, Result>; - fn blame(&self, path: RepoPath, content: Rope) -> BoxFuture<'_, Result>; + fn blame( + &self, + path: RepoPath, + content: Rope, + line_ending: LineEnding, + ) -> BoxFuture<'_, Result>; fn file_history(&self, path: RepoPath) -> BoxFuture<'_, Result>; fn file_history_paginated( &self, @@ -1512,7 +1518,12 @@ impl GitRepository for RealGitRepository { .boxed() } - fn blame(&self, path: RepoPath, content: Rope) -> BoxFuture<'_, Result> { + fn blame( + &self, + path: RepoPath, + content: Rope, + line_ending: LineEnding, + ) -> BoxFuture<'_, Result> { let working_directory = self.working_directory(); let git_binary_path = self.any_git_binary_path.clone(); let executor = self.executor.clone(); @@ -1524,6 +1535,7 @@ impl GitRepository for RealGitRepository { &working_directory?, &path, &content, + line_ending, ) .await }) diff --git a/crates/project/src/git_store.rs b/crates/project/src/git_store.rs index ae39cc331c3dae44261392e1a4d1782901443795..c73ab914b788fb92e69ea3a47db5446223098c2d 100644 --- a/crates/project/src/git_store.rs +++ b/crates/project/src/git_store.rs @@ -1031,6 +1031,7 @@ impl GitStore { Some(version) => buffer.rope_for_version(version), None => buffer.as_rope().clone(), }; + let line_ending = buffer.line_ending(); let version = version.unwrap_or(buffer.version()); let buffer_id = buffer.remote_id(); @@ -1042,7 +1043,7 @@ impl GitStore { .map_err(|err| anyhow::anyhow!(err))?; match repository_state { RepositoryState::Local(LocalRepositoryState { backend, .. }) => backend - .blame(repo_path.clone(), content) + .blame(repo_path.clone(), content, line_ending) .await .with_context(|| format!("Failed to blame {:?}", repo_path.as_ref())) .map(Some), diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 6f570d61d8b743c2703ba221009ccc9e4727c87a..866552e4e5d9039a9517a556323a4ba7a89fcee1 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -3387,6 +3387,25 @@ impl LineEnding { } } +pub fn chunks_with_line_ending(rope: &Rope, line_ending: LineEnding) -> impl Iterator { + rope.chunks().flat_map(move |chunk| { + let mut newline = false; + let end_with_newline = chunk.ends_with('\n').then_some(line_ending.as_str()); + chunk + .lines() + .flat_map(move |line| { + let ending = if newline { + Some(line_ending.as_str()) + } else { + None + }; + newline = true; + ending.into_iter().chain([line]) + }) + .chain(end_with_newline) + }) +} + #[cfg(debug_assertions)] pub mod debug { use super::*;