Compare commits
8 Commits
edit-diffs
...
add_wrapsc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce9582df9 | ||
|
|
b47fd3a496 | ||
|
|
140a620494 | ||
|
|
b2554fe94e | ||
|
|
22d6917d1b | ||
|
|
76df24d57e | ||
|
|
ce13c243b5 | ||
|
|
df70d5fadf |
@@ -262,6 +262,8 @@
|
||||
// to both the horizontal and vertical delta values while scrolling.
|
||||
"scroll_sensitivity": 1.0,
|
||||
"relative_line_numbers": false,
|
||||
// If 'search_wrap' is disabled, search result do not wrap around the end of the file.
|
||||
"search_wrap": true,
|
||||
// When to populate a new search's query based on the text under the cursor.
|
||||
// This setting can take the following three values:
|
||||
//
|
||||
|
||||
@@ -25,6 +25,7 @@ pub struct EditorSettings {
|
||||
pub expand_excerpt_lines: u32,
|
||||
#[serde(default)]
|
||||
pub double_click_in_multibuffer: DoubleClickInMultibuffer,
|
||||
pub search_wrap: bool,
|
||||
#[serde(default)]
|
||||
pub jupyter: Jupyter,
|
||||
}
|
||||
@@ -228,6 +229,10 @@ pub struct EditorSettingsContent {
|
||||
///
|
||||
/// Default: select
|
||||
pub double_click_in_multibuffer: Option<DoubleClickInMultibuffer>,
|
||||
/// Whether the editor search results will loop
|
||||
///
|
||||
/// Default: true
|
||||
pub search_wrap: Option<bool>,
|
||||
|
||||
/// Jupyter REPL settings.
|
||||
pub jupyter: Option<Jupyter>,
|
||||
|
||||
@@ -9,7 +9,7 @@ use any_vec::AnyVec;
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
actions::{Tab, TabPrev},
|
||||
DisplayPoint, Editor, EditorElement, EditorStyle,
|
||||
DisplayPoint, Editor, EditorElement, EditorSettings, EditorStyle,
|
||||
};
|
||||
use futures::channel::oneshot;
|
||||
use gpui::{
|
||||
@@ -777,6 +777,15 @@ impl BufferSearchBar {
|
||||
.get(&searchable_item.downgrade())
|
||||
.filter(|matches| !matches.is_empty())
|
||||
{
|
||||
// If 'wrapscan' is disabled, searches do not wrap around the end of the file.
|
||||
if !EditorSettings::get_global(cx).search_wrap {
|
||||
if (direction == Direction::Next && index + count >= matches.len())
|
||||
|| (direction == Direction::Prev && index < count)
|
||||
{
|
||||
crate::show_no_more_matches(cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let new_match_index = searchable_item
|
||||
.match_index_for_direction(matches, index, direction, count, cx);
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ use editor::{
|
||||
actions::SelectAll,
|
||||
items::active_match_index,
|
||||
scroll::{Autoscroll, Axis},
|
||||
Anchor, Editor, EditorElement, EditorEvent, EditorStyle, MultiBuffer, MAX_TAB_TITLE_LEN,
|
||||
Anchor, Editor, EditorElement, EditorEvent, EditorSettings, EditorStyle, MultiBuffer,
|
||||
MAX_TAB_TITLE_LEN,
|
||||
};
|
||||
use gpui::{
|
||||
actions, div, Action, AnyElement, AnyView, AppContext, Context as _, Element, EntityId,
|
||||
@@ -970,6 +971,16 @@ impl ProjectSearchView {
|
||||
fn select_match(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
|
||||
if let Some(index) = self.active_match_index {
|
||||
let match_ranges = self.model.read(cx).match_ranges.clone();
|
||||
|
||||
if !EditorSettings::get_global(cx).search_wrap {
|
||||
if (direction == Direction::Next && index + 1 >= match_ranges.len())
|
||||
|| (direction == Direction::Prev && index == 0)
|
||||
{
|
||||
crate::show_no_more_matches(cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let new_index = self.results_editor.update(cx, |editor, cx| {
|
||||
editor.match_index_for_direction(&match_ranges, index, direction, 1, cx)
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ use project::search::SearchQuery;
|
||||
pub use project_search::ProjectSearchView;
|
||||
use ui::{prelude::*, Tooltip};
|
||||
use ui::{ButtonStyle, IconButton};
|
||||
use workspace::notifications::NotificationId;
|
||||
use workspace::{Toast, Workspace};
|
||||
|
||||
pub mod buffer_search;
|
||||
pub mod project_search;
|
||||
@@ -107,3 +109,21 @@ impl SearchOptions {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn show_no_more_matches(cx: &mut WindowContext) {
|
||||
cx.defer(|cx| {
|
||||
struct NotifType();
|
||||
let notification_id = NotificationId::unique::<NotifType>();
|
||||
let Some(workspace) = cx.window_handle().downcast::<Workspace>() else {
|
||||
return;
|
||||
};
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.show_toast(
|
||||
Toast::new(notification_id.clone(), "No more matches").autohide(),
|
||||
cx,
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -526,14 +526,15 @@ fn parse_replace_all(query: &str) -> Replacement {
|
||||
mod test {
|
||||
use std::time::Duration;
|
||||
|
||||
use editor::{display_map::DisplayRow, DisplayPoint};
|
||||
use indoc::indoc;
|
||||
use search::BufferSearchBar;
|
||||
|
||||
use crate::{
|
||||
state::Mode,
|
||||
test::{NeovimBackedTestContext, VimTestContext},
|
||||
};
|
||||
use editor::EditorSettings;
|
||||
use editor::{display_map::DisplayRow, DisplayPoint};
|
||||
use indoc::indoc;
|
||||
use search::BufferSearchBar;
|
||||
use settings::SettingsStore;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_move_to_next(cx: &mut gpui::TestAppContext) {
|
||||
@@ -572,6 +573,44 @@ mod test {
|
||||
cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_move_to_next_with_no_search_wrap(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store.update_user_settings::<EditorSettings>(cx, |s| s.search_wrap = Some(false));
|
||||
});
|
||||
|
||||
cx.set_state("ˇhi\nhigh\nhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("*");
|
||||
cx.run_until_parked();
|
||||
cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("*");
|
||||
cx.run_until_parked();
|
||||
cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("#");
|
||||
cx.run_until_parked();
|
||||
cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("3 *");
|
||||
cx.run_until_parked();
|
||||
cx.assert_state("ˇhi\nhigh\nhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("g *");
|
||||
cx.run_until_parked();
|
||||
cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("n");
|
||||
cx.assert_state("hi\nhigh\nˇhi\n", Mode::Normal);
|
||||
|
||||
cx.simulate_keystrokes("g #");
|
||||
cx.run_until_parked();
|
||||
cx.assert_state("hi\nˇhigh\nhi\n", Mode::Normal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = VimTestContext::new(cx, true).await;
|
||||
@@ -649,6 +688,27 @@ mod test {
|
||||
cx.assert_editor_state("«oneˇ» two one");
|
||||
cx.simulate_keystrokes("*");
|
||||
cx.assert_state("one two ˇone", Mode::Normal);
|
||||
|
||||
// check that searching with unable search wrap
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store.update_user_settings::<EditorSettings>(cx, |s| s.search_wrap = Some(false));
|
||||
});
|
||||
cx.set_state("aa\nbˇb\ncc\ncc\ncc\n", Mode::Normal);
|
||||
cx.simulate_keystrokes("/ c c enter");
|
||||
|
||||
cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
|
||||
|
||||
// n to go to next/N to go to previous
|
||||
cx.simulate_keystrokes("n");
|
||||
cx.assert_state("aa\nbb\ncc\nˇcc\ncc\n", Mode::Normal);
|
||||
cx.simulate_keystrokes("shift-n");
|
||||
cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
|
||||
|
||||
// ?<enter> to go to previous
|
||||
cx.simulate_keystrokes("? enter");
|
||||
cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
|
||||
cx.simulate_keystrokes("? enter");
|
||||
cx.assert_state("aa\nbb\nˇcc\ncc\ncc\n", Mode::Normal);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
||||
@@ -7,7 +7,7 @@ use gpui::{
|
||||
};
|
||||
use language::DiagnosticSeverity;
|
||||
|
||||
use std::{any::TypeId, ops::DerefMut};
|
||||
use std::{any::TypeId, ops::DerefMut, time::Duration};
|
||||
use ui::{prelude::*, Tooltip};
|
||||
use util::ResultExt;
|
||||
|
||||
@@ -174,7 +174,7 @@ impl Workspace {
|
||||
|
||||
pub fn show_toast(&mut self, toast: Toast, cx: &mut ViewContext<Self>) {
|
||||
self.dismiss_notification(&toast.id, cx);
|
||||
self.show_notification(toast.id, cx, |cx| {
|
||||
self.show_notification(toast.id.clone(), cx, |cx| {
|
||||
cx.new_view(|_cx| match toast.on_click.as_ref() {
|
||||
Some((click_msg, on_click)) => {
|
||||
let on_click = on_click.clone();
|
||||
@@ -184,7 +184,20 @@ impl Workspace {
|
||||
}
|
||||
None => simple_message_notification::MessageNotification::new(toast.msg.clone()),
|
||||
})
|
||||
})
|
||||
});
|
||||
if toast.autohide {
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(5000))
|
||||
.await;
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.dismiss_toast(&toast.id, cx)
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dismiss_toast(&mut self, id: &NotificationId, cx: &mut ViewContext<Self>) {
|
||||
|
||||
@@ -218,9 +218,11 @@ impl_actions!(
|
||||
]
|
||||
);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Toast {
|
||||
id: NotificationId,
|
||||
msg: Cow<'static, str>,
|
||||
autohide: bool,
|
||||
on_click: Option<(Cow<'static, str>, Arc<dyn Fn(&mut WindowContext)>)>,
|
||||
}
|
||||
|
||||
@@ -230,6 +232,7 @@ impl Toast {
|
||||
id,
|
||||
msg: msg.into(),
|
||||
on_click: None,
|
||||
autohide: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +244,11 @@ impl Toast {
|
||||
self.on_click = Some((message.into(), Arc::new(on_click)));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn autohide(mut self) -> Self {
|
||||
self.autohide = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Toast {
|
||||
@@ -251,16 +259,6 @@ impl PartialEq for Toast {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Toast {
|
||||
fn clone(&self) -> Self {
|
||||
Toast {
|
||||
id: self.id.clone(),
|
||||
msg: self.msg.clone(),
|
||||
on_click: self.on_click.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, PartialEq)]
|
||||
pub struct OpenTerminal {
|
||||
pub working_directory: PathBuf,
|
||||
|
||||
Reference in New Issue
Block a user