Files
openidea/crates/ide-vcs/src/text_encoding.rs
l0ng-ai cdb6cc7c0d fix(vcs): decode git blobs in the file's encoding (#301)
Git blobs were always decoded as UTF-8, so files in GBK or other legacy encodings showed every line as changed in the gutter, diff, blame and commit views, and staging/committing could re-encode them wrongly.

Blob text is now decoded and encoded with the file's encoding: the open document's encoding, else detection on the working file with the project default as fallback, else detection on the blob. Covers line hunks, file-at-revision, blame, stage patch/hunks, commit, conflict versions, diff text, and the agent worktree's file_at, partial commit and patch helpers. Text that cannot be represented in the target encoding is reported as an error instead of being written lossily.

Fixes #243

未在 Windows 实机验证。
2026-10-30 09:00:21 +08:00

47 lines
1.6 KiB
Rust

//! File content uses the document's encoding; Git metadata remains UTF-8.
use encoding_rs::{Encoding, UTF_8, UTF_16BE, UTF_16LE};
use ide_core::{IdeError, IdeResult};
use std::borrow::Cow;
pub(crate) fn has_bom(bytes: &[u8], encoding: &'static Encoding) -> bool {
Encoding::for_bom(bytes).is_some_and(|(enc, _)| enc == encoding)
}
pub(crate) fn decode<'a>(bytes: &'a [u8], encoding: &'static Encoding) -> Cow<'a, str> {
let body = match Encoding::for_bom(bytes) {
Some((enc, len)) if enc == encoding => &bytes[len..],
_ => bytes,
};
encoding.decode_without_bom_handling(body).0
}
pub(crate) fn is_binary(bytes: &[u8], encoding: &'static Encoding) -> bool {
encoding != UTF_16LE && encoding != UTF_16BE && bytes.contains(&0)
}
pub(crate) fn encode(text: &str, encoding: &'static Encoding, bom: bool) -> IdeResult<Vec<u8>> {
let mut out = Vec::new();
if encoding == UTF_16LE || encoding == UTF_16BE {
let le = encoding == UTF_16LE;
if bom {
out.extend_from_slice(if le { &[0xff, 0xfe] } else { &[0xfe, 0xff] });
}
for unit in text.encode_utf16() {
out.extend_from_slice(&if le { unit.to_le_bytes() } else { unit.to_be_bytes() });
}
} else {
let (bytes, _, errors) = encoding.encode(text);
if errors {
return Err(IdeError::invalid(format!(
"text contains characters not representable in {}",
encoding.name()
)));
}
if bom && encoding == UTF_8 {
out.extend_from_slice(&[0xef, 0xbb, 0xbf]);
}
out.extend_from_slice(&bytes);
}
Ok(out)
}