@@ -217,8 +217,11 @@ impl Command {
217217 split_selection, "Split selection into subselections on regex matches",
218218 split_selection_on_newline, "Split selection on newlines",
219219 search, "Search for regex pattern",
220+ rsearch, "Reverse search for regex pattern",
220221 search_next, "Select next search match",
222+ search_prev, "Select previous search match",
221223 extend_search_next, "Add next search match to selection",
224+ extend_search_prev, "Add previous search match to selection",
222225 search_selection, "Use current selection as search pattern",
223226 global_search, "Global Search in workspace folder",
224227 extend_line, "Select current line, if already selected, extend to next line",
@@ -1170,38 +1173,62 @@ fn split_selection_on_newline(cx: &mut Context) {
11701173 doc.set_selection(view.id, selection);
11711174}
11721175
1173−fn search_impl(doc: &mut Document, view: &mut View, contents: &str, regex: &Regex, extend: bool) {
1176+fn search_impl(
1177+ doc: &mut Document,
1178+ view: &mut View,
1179+ contents: &str,
1180+ regex: &Regex,
1181+ movement: Movement,
1182+ direction: Direction,
1183+) {
11741184 let text = doc.text().slice(..);
11751185 let selection = doc.selection(view.id);
11761186
1177− // Get the right side of the primary block cursor.
1178− let start = text.char_to_byte(graphemes::next_grapheme_boundary(
1179− text,
1180− selection.primary().cursor(text),
1181− ));
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 {
1190+ Direction::Forward => text.char_to_byte(graphemes::next_grapheme_boundary(
1191+ text,
1192+ selection.primary().to(),
1193+ )),
1194+ Direction::Backward => text.char_to_byte(graphemes::prev_grapheme_boundary(
1195+ text,
1196+ selection.primary().from(),
1197+ )),
1198+ };
1199+
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;
11821205
11831206 // use find_at to find the next match after the cursor, loop around the end
11841207 // Careful, `Regex` uses `bytes` as offsets, not character indices!
1185− let mat = regex
1186− .find_at(contents, start)
1187− .or_else(|| regex.find(contents));
1208+ let mat = match direction {
1209+ Direction::Forward => regex
1210+ .find_at(contents, start)
1211+ .or_else(|| regex.find(contents)),
1212+ Direction::Backward => regex.find_iter(&contents[..start]).last().or_else(|| {
1213+ offset = start;
1214+ regex.find_iter(&contents[start..]).last()
1215+ }),
1216+ };
11881217 // TODO: message on wraparound
11891218 if let Some(mat) = mat {
1190− let start = text.byte_to_char(mat.start());
1191− let end = text.byte_to_char(mat.end());
1219+ let start = text.byte_to_char(mat.start() + offset);
1220+ let end = text.byte_to_char(mat.end() + offset);
11921221
11931222 if end == 0 {
11941223 // skip empty matches that don't make sense
11951224 return;
11961225 }
1197−
1198− let selection = if extend {
1199− selection.clone().push(Range::new(start, end))
1200− } else {
1201− selection
1226+ let selection = match movement {
1227+ Movement::Extend => selection.clone().push(Range::new(start, end)),
1228+ Movement::Move => selection
12021229 .clone()
12031230 .remove(selection.primary_index())
1204− .push(Range::new(start, end))
1231+ .push(Range::new(start, end)),
12051232 };
12061233
12071234 doc.set_selection(view.id, selection);
@@ -1220,6 +1247,14 @@ fn search_completions(cx: &mut Context, reg: Option<char>) -> Vec<String> {
12201247
12211248// TODO: use one function for search vs extend
12221249fn search(cx: &mut Context) {
1250+ searcher(cx, Direction::Forward)
1251+}
1252+
1253+fn rsearch(cx: &mut Context) {
1254+ searcher(cx, Direction::Backward)
1255+}
1256+// TODO: use one function for search vs extend
1257+fn searcher(cx: &mut Context, direction: Direction) {
12231258 let reg = cx.register.unwrap_or('/');
12241259 let (_, doc) = current!(cx.editor);
12251260
@@ -1245,14 +1280,14 @@ fn search(cx: &mut Context) {
12451280 if event != PromptEvent::Update {
12461281 return;
12471282 }
1248− search_impl(doc, view, &contents, ®ex, false);
1283+ search_impl(doc, view, &contents, ®ex, Movement::Move, direction);
12491284 },
12501285 );
12511286
12521287 cx.push_layer(Box::new(prompt));
12531288}
12541289
1255−fn search_next_impl(cx: &mut Context, extend: bool) {
1290+fn search_next_or_prev_impl(cx: &mut Context, movement: Movement, direction: Direction) {
12561291 let (view, doc) = current!(cx.editor);
12571292 let registers = &cx.editor.registers;
12581293 if let Some(query) = registers.read('/') {
@@ -1267,7 +1302,7 @@ fn search_next_impl(cx: &mut Context, extend: bool) {
12671302 .case_insensitive(case_insensitive)
12681303 .build()
12691304 {
1270− search_impl(doc, view, &contents, ®ex, extend);
1305+ search_impl(doc, view, &contents, ®ex, movement, direction);
12711306 } else {
12721307 // get around warning `mutable_borrow_reservation_conflict`
12731308 // which will be a hard error in the future
@@ -1279,11 +1314,18 @@ fn search_next_impl(cx: &mut Context, extend: bool) {
12791314}
12801315
12811316fn search_next(cx: &mut Context) {
1282− search_next_impl(cx, false);
1317+ search_next_or_prev_impl(cx, Movement::Move, Direction::Forward);
12831318}
12841319
1320+fn search_prev(cx: &mut Context) {
1321+ search_next_or_prev_impl(cx, Movement::Move, Direction::Backward);
1322+}
12851323fn extend_search_next(cx: &mut Context) {
1286− search_next_impl(cx, true);
1324+ search_next_or_prev_impl(cx, Movement::Extend, Direction::Forward);
1325+}
1326+
1327+fn extend_search_prev(cx: &mut Context) {
1328+ search_next_or_prev_impl(cx, Movement::Extend, Direction::Backward);
12871329}
12881330
12891331fn search_selection(cx: &mut Context) {