Fix ESLint linebreak-style errors by preserving line endings in LSP communication (#38773)
Closes https://github.com/zed-industries/zed/issues/38453 Current `Buffer` API only allows getting buffer text with `\n` line breaks — even if the `\r\n` was used in the original file's text. This it not correct in certain cases like LSP formatting, where language servers need to have original document context for e.g. formatting purposes. Added new `Buffer` API, replaced all buffer LSP registration places with the new one and added more tests. Release Notes: - Fixed ESLint linebreak-style errors by preserving line endings in LSP communication --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
co-authored by
Claude
Kirill Bulatov
parent
55bc679c19
commit
435eab6896
@@ -12629,6 +12629,12 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let line_ending = "\r\n";
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let line_ending = "\n";
|
||||
|
||||
// Handle formatting requests to the language server.
|
||||
cx.lsp
|
||||
.set_request_handler::<lsp::request::Formatting, _, _>({
|
||||
@@ -12652,7 +12658,7 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
|
||||
),
|
||||
(
|
||||
lsp::Range::new(lsp::Position::new(3, 4), lsp::Position::new(3, 4)),
|
||||
"\n".into()
|
||||
line_ending.into()
|
||||
),
|
||||
]
|
||||
);
|
||||
@@ -12663,14 +12669,14 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut TestAppContext) {
|
||||
lsp::Position::new(1, 0),
|
||||
lsp::Position::new(1, 0),
|
||||
),
|
||||
new_text: "\n".into(),
|
||||
new_text: line_ending.into(),
|
||||
},
|
||||
lsp::TextEdit {
|
||||
range: lsp::Range::new(
|
||||
lsp::Position::new(2, 0),
|
||||
lsp::Position::new(2, 0),
|
||||
),
|
||||
new_text: "\n".into(),
|
||||
new_text: line_ending.into(),
|
||||
},
|
||||
]))
|
||||
}
|
||||
@@ -26656,6 +26662,83 @@ async fn test_paste_url_from_other_app_creates_markdown_link_selectively_in_mult
|
||||
));
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_non_linux_line_endings_registration(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let unix_newlines_file_text = "fn main() {
|
||||
let a = 5;
|
||||
}";
|
||||
let clrf_file_text = unix_newlines_file_text.lines().join("\r\n");
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/a"),
|
||||
json!({
|
||||
"first.rs": &clrf_file_text,
|
||||
}),
|
||||
)
|
||||
.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, cx);
|
||||
|
||||
let registered_text = Arc::new(Mutex::new(Vec::new()));
|
||||
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(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
color_provider: Some(lsp::ColorProviderCapability::Simple(true)),
|
||||
..lsp::ServerCapabilities::default()
|
||||
},
|
||||
name: "rust-analyzer",
|
||||
initializer: Some({
|
||||
let registered_text = registered_text.clone();
|
||||
Box::new(move |fake_server| {
|
||||
fake_server.handle_notification::<lsp::notification::DidOpenTextDocument, _>({
|
||||
let registered_text = registered_text.clone();
|
||||
move |params, _| {
|
||||
registered_text.lock().push(params.text_document.text);
|
||||
}
|
||||
});
|
||||
})
|
||||
}),
|
||||
..FakeLspAdapter::default()
|
||||
},
|
||||
);
|
||||
|
||||
let editor = workspace
|
||||
.update(cx, |workspace, window, cx| {
|
||||
workspace.open_abs_path(
|
||||
PathBuf::from(path!("/a/first.rs")),
|
||||
OpenOptions::default(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.unwrap()
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
let _fake_language_server = fake_servers.next().await.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
assert_eq!(
|
||||
editor.update(cx, |editor, cx| editor.text(cx)),
|
||||
unix_newlines_file_text,
|
||||
"Default text API returns \n-separated text",
|
||||
);
|
||||
assert_eq!(
|
||||
vec![clrf_file_text],
|
||||
registered_text.lock().drain(..).collect::<Vec<_>>(),
|
||||
"Expected the language server to receive the exact same text from the FS",
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_race_in_multibuffer_save(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
Reference in New Issue
Block a user