@@ -217,8 +217,11 @@ impl Command {
217 split_selection, "Split selection into subselections on regex matches",
217 split_selection, "Split selection into subselections on regex matches",
218 split_selection_on_newline, "Split selection on newlines",
218 split_selection_on_newline, "Split selection on newlines",
219 search, "Search for regex pattern",
219 search, "Search for regex pattern",
220+ rsearch, "Reverse search for regex pattern",
220 search_next, "Select next search match",
221 search_next, "Select next search match",
222+ search_prev, "Select previous search match",
221 extend_search_next, "Add next search match to selection",
223 extend_search_next, "Add next search match to selection",
224+ extend_search_prev, "Add previous search match to selection",
222 search_selection, "Use current selection as search pattern",
225 search_selection, "Use current selection as search pattern",
223 global_search, "Global Search in workspace folder",
226 global_search, "Global Search in workspace folder",
224 extend_line, "Select current line, if already selected, extend to next line",
227 extend_line, "Select current line, if already selected, extend to next line",
@@ -1170,38 +1173,62 @@ fn split_selection_on_newline(cx: &mut Context) {
1170 doc.set_selection(view.id, selection);
1173 doc.set_selection(view.id, selection);
1173−fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Regex, extend: bool) {
1176+fn search_impl(
1177+ doc: &mut Document,
1181+ movement: Movement,
1182+ direction: Direction,
1174 let text = doc.text().slice(..);
1184 let text = doc.text().slice(..);
1175 let selection = doc.selection(view.id);
1185 let selection = doc.selection(view.id);
1177− // Get the right side of the primary block cursor.
1187+ // Get the right side of the primary block cursor for forward search, or the
1188+ //grapheme before the start of the selection for reverse search.
1189+ let start = match direction {
1178− let start = text.char_to_byte(graphemes::next_grapheme_boundary(
1190+ Direction::Forward => text.char_to_byte(graphemes::next_grapheme_boundary(
1192+ selection.primary().to(),
1194+ Direction::Backward => text.char_to_byte(graphemes::prev_grapheme_boundary(
1180− selection.primary().cursor(text),
1196+ selection.primary().from(),
1200+ //A regex::Match returns byte-positions in the str. In the case where we
1201+ //do a reverse search and wraparound to the end, we don't need to search
1202+ //the text before the current cursor position for matches, but by slicing
1203+ //it out, we need to add it back to the position of the selection.
1204+ let mut offset = 0;
1183 // use find_at to find the next match after the cursor, loop around the end
1206 // use find_at to find the next match after the cursor, loop around the end
1184 // Careful, `Regex` uses `bytes` as offsets, not character indices!
1207 // Careful, `Regex` uses `bytes` as offsets, not character indices!
1185− let mat = regex
1208+ let mat = match direction {
1209+ Direction::Forward => regex
1186− .find_at(contents, start)
1210+ .find_at(contents, start)
1187− .or_else(|| regex.find(contents));
1211+ .or_else(|| regex.find(contents)),
1212+ Direction::Backward => regex.find_iter(&contents[..start]).last().or_else(|| {
1214+ regex.find_iter(&contents[start..]).last()
1188 // TODO: message on wraparound
1217 // TODO: message on wraparound
1189 if let Some(mat) = mat {
1218 if let Some(mat) = mat {
1190− let start = text.byte_to_char(mat.start());
1219+ let start = text.byte_to_char(mat.start() + offset);
1191− let end = text.byte_to_char(mat.end());
1220+ let end = text.byte_to_char(mat.end() + offset);
1193 if end == 0 {
1222 if end == 0 {
1194 // skip empty matches that don't make sense
1223 // skip empty matches that don't make sense
1198− let selection = if extend {
1226+ let selection = match movement {
1199− selection.clone().push(Range::new(start, end))
1227+ Movement::Extend => selection.clone().push(Range::new(start, end)),
1201− selection
1228+ Movement::Move => selection
1202 .clone()
1229 .clone()
1203 .remove(selection.primary_index())
1230 .remove(selection.primary_index())
1204− .push(Range::new(start, end))
1231+ .push(Range::new(start, end)),
1207 doc.set_selection(view.id, selection);
1234 doc.set_selection(view.id, selection);
@@ -1220,6 +1247,14 @@ fn search_completions(cx: &mut Context, reg: Option<char>) -> Vec<String> {
1221// TODO: use one function for search vs extend
1248// TODO: use one function for search vs extend
1222fn search(cx: &mut Context) {
1249fn search(cx: &mut Context) {
1250+ searcher(cx, Direction::Forward)
1253+fn rsearch(cx: &mut Context) {
1254+ searcher(cx, Direction::Backward)
1256+// TODO: use one function for search vs extend
1257+fn searcher(cx: &mut Context, direction: Direction) {
1223 let reg = cx.register.unwrap_or('/');
1258 let reg = cx.register.unwrap_or('/');
1224 let (_, doc) = current!(cx.editor);
1259 let (_, doc) = current!(cx.editor);
@@ -1245,14 +1280,14 @@ fn search(cx: &mut Context) {
1245 if event != PromptEvent::Update {
1280 if event != PromptEvent::Update {
1248− search_impl(doc, view, &contents, ®ex, false);
1283+ search_impl(doc, view, &contents, ®ex, Movement::Move, direction);
1252 cx.push_layer(Box::new(prompt));
1287 cx.push_layer(Box::new(prompt));
1255−fn search_next_impl(cx: &mut Context, extend: bool) {
1290+fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Direction) {
1256 let (view, doc) = current!(cx.editor);
1291 let (view, doc) = current!(cx.editor);
1257 let registers = &cx.editor.registers;
1292 let registers = &cx.editor.registers;
1258 if let Some(query) = registers.read('/') {
1293 if let Some(query) = registers.read('/') {
@@ -1267,7 +1302,7 @@ fn search_next_impl(cx: &mut Context, extend: bool) {
1267 .case_insensitive(case_insensitive)
1302 .case_insensitive(case_insensitive)
1268 .build()
1303 .build()
1270− search_impl(doc, view, &contents, ®ex, extend);
1305+ search_impl(doc, view, &contents, ®ex, movement, direction);
1271 } else {
1306 } else {
1272 // get around warning `mutable_borrow_reservation_conflict`
1307 // get around warning `mutable_borrow_reservation_conflict`
1273 // which will be a hard error in the future
1308 // which will be a hard error in the future
@@ -1279,11 +1314,18 @@ fn search_next_impl(cx: &mut Context, extend: bool) {
1281fn search_next(cx: &mut Context) {
1316fn search_next(cx: &mut Context) {
1282− search_next_impl(cx, false);
1317+ search_next_or_prev_impl(cx, Movement::Move, Direction::Forward);
1320+fn search_prev(cx: &mut Context) {
1321+ search_next_or_prev_impl(cx, Movement::Move, Direction::Backward);
1285fn extend_search_next(cx: &mut Context) {
1323fn extend_search_next(cx: &mut Context) {
1286− search_next_impl(cx, true);
1324+ search_next_or_prev_impl(cx, Movement::Extend, Direction::Forward);
1327+fn extend_search_prev(cx: &mut Context) {
1328+ search_next_or_prev_impl(cx, Movement::Extend, Direction::Backward);
1289fn search_selection(cx: &mut Context) {
1331fn search_selection(cx: &mut Context) {