diff --git a/assets/icons/file_icons/file_types.json b/assets/icons/file_icons/file_types.json index f73cd708f18794bb10874633a1a71e33bb836162..ce6dd7953dc77a4b3e46a8e65b894c1c8f90983e 100644 --- a/assets/icons/file_icons/file_types.json +++ b/assets/icons/file_icons/file_types.json @@ -23,6 +23,7 @@ "c++": "cpp", "cc": "cpp", "cjs": "javascript", + "cjsx": "react", "coffee": "coffeescript", "conf": "settings", "cpp": "cpp", @@ -30,6 +31,7 @@ "csv": "storage", "cxx": "cpp", "cts": "typescript", + "ctsx": "react", "dart": "dart", "dat": "storage", "db": "storage", @@ -121,6 +123,7 @@ "metadata": "code", "metal": "metal", "mjs": "javascript", + "mjsx": "react", "mka": "audio", "mkv": "video", "ml": "ocaml", @@ -130,6 +133,7 @@ "mp3": "audio", "mp4": "video", "mts": "typescript", + "mtsx": "react", "myd": "storage", "myi": "storage", "nim": "nim", @@ -186,6 +190,17 @@ "sh": "terminal", "sql": "storage", "sqlite": "storage", + "stylelint.config.cjs": "stylelint", + "stylelint.config.js": "stylelint", + "stylelint.config.mjs": "stylelint", + "stylelintignore": "stylelint", + "stylelintrc": "stylelint", + "stylelintrc.cjs": "stylelint", + "stylelintrc.js": "stylelint", + "stylelintrc.json": "stylelint", + "stylelintrc.mjs": "stylelint", + "stylelintrc.yaml": "stylelint", + "stylelintrc.yml": "stylelint", "svelte": "svelte", "svg": "image", "swift": "swift", diff --git a/crates/buffer_diff/src/buffer_diff.rs b/crates/buffer_diff/src/buffer_diff.rs index 772835f9a93f382934b91e016ba4c8c2cb91fef7..fb244abbb7ec70f7daa18ab18969a366d9427840 100644 --- a/crates/buffer_diff/src/buffer_diff.rs +++ b/crates/buffer_diff/src/buffer_diff.rs @@ -587,18 +587,16 @@ impl BufferDiff { range: Range, buffer: &text::BufferSnapshot, cx: &App, - ) -> Option> { + ) -> Range { let start = self .hunks_intersecting_range(range.clone(), &buffer, cx) - .next()? - .buffer_range - .start; + .next() + .map_or(Anchor::MIN, |hunk| hunk.buffer_range.start); let end = self .hunks_intersecting_range_rev(range.clone(), &buffer) - .next()? - .buffer_range - .end; - Some(start..end) + .next() + .map_or(Anchor::MAX, |hunk| hunk.buffer_range.end); + start..end } #[allow(clippy::too_many_arguments)] diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 27266f3dd6ee89d4ef425059c29e6527932184f5..61481e3f9212becf6a80c109c0adee42faa8d8ad 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -265,6 +265,8 @@ gpui::actions!( Copy, CopyFileLocation, CopyHighlightJson, + CopyFileName, + CopyFileNameWithoutExtension, CopyPath, CopyPermalinkToLine, CopyRelativePath, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 957648f4b8aa06353df970091e7f81b5472dcf72..15414e5b111f89adef4d9a33d7bc3d38b681a60e 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5457,19 +5457,27 @@ impl Editor { }; if &accept_keystroke.modifiers == modifiers { - if let Some(completion) = self.active_inline_completion.as_ref() { - if self.edit_prediction_preview.start( - &completion.completion, - &position_map.snapshot, - self.selections - .newest_anchor() - .head() - .to_display_point(&position_map.snapshot), - ) { - self.request_autoscroll(Autoscroll::fit(), cx); - self.update_visible_inline_completion(window, cx); - cx.notify(); - } + let Some(completion) = self.active_inline_completion.as_ref() else { + return; + }; + + if !self.edit_prediction_requires_modifier() && !self.has_visible_completions_menu() { + return; + } + + let transitioned = self.edit_prediction_preview.start( + &completion.completion, + &position_map.snapshot, + self.selections + .newest_anchor() + .head() + .to_display_point(&position_map.snapshot), + ); + + if transitioned { + self.request_autoscroll(Autoscroll::fit(), cx); + self.update_visible_inline_completion(window, cx); + cx.notify(); } } else if self.edit_prediction_preview.end( self.selections @@ -13116,6 +13124,31 @@ impl Editor { } } + pub fn copy_file_name_without_extension( + &mut self, + _: &CopyFileNameWithoutExtension, + _: &mut Window, + cx: &mut Context, + ) { + if let Some(file) = self.target_file(cx) { + if let Some(file_stem) = file.path().file_stem() { + if let Some(name) = file_stem.to_str() { + cx.write_to_clipboard(ClipboardItem::new_string(name.to_string())); + } + } + } + } + + pub fn copy_file_name(&mut self, _: &CopyFileName, _: &mut Window, cx: &mut Context) { + if let Some(file) = self.target_file(cx) { + if let Some(file_name) = file.path().file_name() { + if let Some(name) = file_name.to_str() { + cx.write_to_clipboard(ClipboardItem::new_string(name.to_string())); + } + } + } + } + pub fn toggle_git_blame( &mut self, _: &ToggleGitBlame, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 03388a19d0d8e04b71cb3cad2466bd69dd4b4e6d..0cc4ac46fa990400674902f81aec058eb208a51e 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -5362,6 +5362,21 @@ async fn test_select_previous_with_single_caret(cx: &mut gpui::TestAppContext) { cx.assert_editor_state("«abcˇ»\n«abcˇ» «abcˇ»\ndef«abcˇ»\n«abcˇ»"); } +#[gpui::test] +async fn test_select_previous_empty_buffer(cx: &mut gpui::TestAppContext) { + init_test(cx, |_| {}); + + let mut cx = EditorTestContext::new(cx).await; + cx.set_state("aˇ"); + + cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx)) + .unwrap(); + cx.assert_editor_state("«aˇ»"); + cx.update_editor(|e, window, cx| e.select_previous(&SelectPrevious::default(), window, cx)) + .unwrap(); + cx.assert_editor_state("«aˇ»"); +} + #[gpui::test] async fn test_select_previous_with_multiple_carets(cx: &mut gpui::TestAppContext) { init_test(cx, |_| {}); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 377a620594230d810992c36b502a90b1b7290cfd..5695d8c0d38f3e4ae4decf3d250094a41e54a92e 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -408,6 +408,8 @@ impl EditorElement { register_action(editor, window, Editor::reveal_in_finder); register_action(editor, window, Editor::copy_path); register_action(editor, window, Editor::copy_relative_path); + register_action(editor, window, Editor::copy_file_name); + register_action(editor, window, Editor::copy_file_name_without_extension); register_action(editor, window, Editor::copy_highlight_json); register_action(editor, window, Editor::copy_permalink_to_line); register_action(editor, window, Editor::open_permalink_to_line); @@ -3628,6 +3630,16 @@ impl EditorElement { return None; } + // Adjust text origin for horizontal scrolling (in some cases here) + let start_point = + text_bounds.origin - gpui::Point::new(scroll_pixel_position.x, Pixels(0.0)); + + // Clamp left offset after extreme scrollings + let clamp_start = |point: gpui::Point| gpui::Point { + x: point.x.max(text_bounds.origin.x), + y: point.y, + }; + match &active_inline_completion.completion { InlineCompletion::Move { target, .. } => { if editor.edit_prediction_requires_modifier() { @@ -3684,6 +3696,7 @@ impl EditorElement { )?; let size = element.layout_as_root(AvailableSpace::min_size(), window, cx); let offset = point((text_bounds.size.width - size.width) / 2., PADDING_Y); + element.prepaint_at(text_bounds.origin + offset, window, cx); Some(element) } else if (target_display_point.row().as_f32() + 1.) > scroll_bottom { @@ -3699,6 +3712,7 @@ impl EditorElement { (text_bounds.size.width - size.width) / 2., text_bounds.size.height - size.height - PADDING_Y, ); + element.prepaint_at(text_bounds.origin + offset, window, cx); Some(element) } else { @@ -3709,7 +3723,6 @@ impl EditorElement { window, cx, )?; - let target_line_end = DisplayPoint::new( target_display_point.row(), editor_snapshot.line_len(target_display_point.row()), @@ -3717,8 +3730,9 @@ impl EditorElement { let origin = self.editor.update(cx, |editor, _cx| { editor.display_to_pixel_point(target_line_end, editor_snapshot, window) })?; + element.prepaint_as_root( - text_bounds.origin + origin + point(PADDING_X, px(0.)), + clamp_start(start_point + origin + point(PADDING_X, px(0.))), AvailableSpace::min_size(), window, cx, @@ -3778,12 +3792,11 @@ impl EditorElement { })?; element.prepaint_as_root( - text_bounds.origin + origin + point(PADDING_X, px(0.)), + clamp_start(start_point + origin + point(PADDING_X, px(0.))), AvailableSpace::min_size(), window, cx, ); - return Some(element); } EditDisplayMode::Inline => return None, diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index defa935c2827152437b00d29e3bccbd35621403e..826c6275672e1419cac8fc14b0472e313d3074ae 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1102,6 +1102,10 @@ impl Buffer { let mut syntax_snapshot = self.syntax_map.lock().snapshot(); cx.background_executor().spawn(async move { if !edits.is_empty() { + if let Some(language) = language.clone() { + syntax_snapshot.reparse(&old_snapshot, registry.clone(), language); + } + branch_buffer.edit(edits.iter().cloned()); let snapshot = branch_buffer.snapshot(); syntax_snapshot.interpolate(&snapshot); diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 2adbb6a81c76b80b2122e277952277a3a0e2136e..ca52eadd9a402824f22b62d5c2152081c270a668 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -7121,7 +7121,7 @@ impl<'a> Iterator for ReversedMultiBufferChunks<'a> { self.offset -= 1; Some("\n") } else { - let chunk = self.current_chunks.as_mut().unwrap().next().unwrap(); + let chunk = self.current_chunks.as_mut().unwrap().next()?; self.offset -= chunk.len(); Some(chunk) } diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 0a8721b4b05a4ef22912f1bbee8d91396a3f337b..0955f8e869b59c438270fa0548b49cdabd4df081 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -260,15 +260,12 @@ impl BufferDiffState { let changed_range = match (unstaged_changed_range, uncommitted_changed_range) { (None, None) => None, (Some(unstaged_range), None) => { - uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx) + Some(uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx)) } (None, Some(uncommitted_range)) => Some(uncommitted_range), (Some(unstaged_range), Some(uncommitted_range)) => maybe!({ - let expanded_range = uncommitted_diff.range_to_hunk_range( - unstaged_range, - &buffer, - cx, - )?; + let expanded_range = + uncommitted_diff.range_to_hunk_range(unstaged_range, &buffer, cx); let start = expanded_range.start.min(&uncommitted_range.start, &buffer); let end = expanded_range.end.max(&uncommitted_range.end, &buffer); Some(start..end) diff --git a/crates/theme/src/icon_theme.rs b/crates/theme/src/icon_theme.rs index cd6e5e5e58df8db8c8b607e19712e79c58142af8..132826a9d5a87e2478c8342cd38f289036c048ca 100644 --- a/crates/theme/src/icon_theme.rs +++ b/crates/theme/src/icon_theme.rs @@ -113,6 +113,7 @@ const FILE_ICONS: &[(&str, &str)] = &[ ("scala", "icons/file_icons/scala.svg"), ("settings", "icons/file_icons/settings.svg"), ("storage", "icons/file_icons/database.svg"), + ("stylelint", "icons/file_icons/javascript.svg"), ("svelte", "icons/file_icons/html.svg"), ("swift", "icons/file_icons/swift.svg"), ("tcl", "icons/file_icons/tcl.svg"), diff --git a/crates/zeta/src/zeta.rs b/crates/zeta/src/zeta.rs index 9365f1945d4520f3e1d1c7c5080d4b44dc4db011..f679bb6827242f0b9253c421e3377b78c88c6551 100644 --- a/crates/zeta/src/zeta.rs +++ b/crates/zeta/src/zeta.rs @@ -664,11 +664,17 @@ and then another let mut did_retry = false; loop { - let request_builder = http_client::Request::builder().method(Method::POST).uri( - http_client - .build_zed_llm_url("/predict_edits/v2", &[])? - .as_ref(), - ); + let request_builder = http_client::Request::builder().method(Method::POST); + let request_builder = + if let Ok(predict_edits_url) = std::env::var("ZED_PREDICT_EDITS_URL") { + request_builder.uri(predict_edits_url) + } else { + request_builder.uri( + http_client + .build_zed_llm_url("/predict_edits/v2", &[])? + .as_ref(), + ) + }; let request = request_builder .header("Content-Type", "application/json") .header("Authorization", format!("Bearer {}", token))