@@ -5,7 +5,7 @@ use helix_core::{
55 regex::{self, Regex},
66 register, search, selection,
77 state::{coords_at_pos, pos_at_coords, Direction, Granularity, State},
8− Change, ChangeSet, Position, Range, Selection, Tendril, Transaction,
8+ Change, ChangeSet, Position, Range, RopeSlice, Selection, Tendril, Transaction,
99};
1010
1111use once_cell::sync::Lazy;
@@ -236,7 +236,13 @@ pub fn extend_next_word_end(cx: &mut Context) {
236236 doc.set_selection(selection);
237237}
238238
239−pub fn find_next_char(cx: &mut Context) {
239+#[inline]
240+fn _find_char<F>(cx: &mut Context, search_fn: F, inclusive: bool, extend: bool)
241+where
242+ // TODO: make an options struct for and abstract this Fn into a searcher type
243+ // use the definition for w/b/e too
244+ F: Fn(RopeSlice, char, usize, usize, bool) -> Option<usize>,
245+{
240246 // TODO: count is reset to 1 before next key so we move it into the closure here.
241247 // Would be nice to carry over.
242248 let count = cx.count;
@@ -252,9 +258,13 @@ pub fn find_next_char(cx: &mut Context) {
252258 let text = doc.text().slice(..);
253259
254260 let selection = doc.selection().transform(|mut range| {
255− if let Some(pos) = search::find_nth_next(text, ch, range.head, count) {
256− Range::new(range.head, pos)
257− // or (range.anchor, pos) for extend
261+ if let Some(pos) = search::find_nth_next(text, ch, range.head, count, inclusive) {
262+ if extend {
263+ Range::new(range.anchor, pos)
264+ } else {
265+ // select
266+ Range::new(range.head, pos)
267+ }
258268 // or (pos, pos) to move to found val
259269 } else {
260270 range
@@ -266,6 +276,78 @@ pub fn find_next_char(cx: &mut Context) {
266276 })
267277}
268278
279+pub fn find_till_char(cx: &mut Context) {
280+ _find_char(
281+ cx,
282+ search::find_nth_next,
283+ false, /* inclusive */
284+ false, /* extend */
285+ )
286+}
287+
288+pub fn find_next_char(cx: &mut Context) {
289+ _find_char(
290+ cx,
291+ search::find_nth_next,
292+ true, /* inclusive */
293+ false, /* extend */
294+ )
295+}
296+
297+pub fn extend_till_char(cx: &mut Context) {
298+ _find_char(
299+ cx,
300+ search::find_nth_next,
301+ false, /* inclusive */
302+ true, /* extend */
303+ )
304+}
305+
306+pub fn extend_next_char(cx: &mut Context) {
307+ _find_char(
308+ cx,
309+ search::find_nth_next,
310+ true, /* inclusive */
311+ true, /* extend */
312+ )
313+}
314+
315+pub fn till_prev_char(cx: &mut Context) {
316+ _find_char(
317+ cx,
318+ search::find_nth_prev,
319+ false, /* inclusive */
320+ false, /* extend */
321+ )
322+}
323+
324+pub fn find_prev_char(cx: &mut Context) {
325+ _find_char(
326+ cx,
327+ search::find_nth_prev,
328+ true, /* inclusive */
329+ false, /* extend */
330+ )
331+}
332+
333+pub fn extend_till_prev_char(cx: &mut Context) {
334+ _find_char(
335+ cx,
336+ search::find_nth_prev,
337+ false, /* inclusive */
338+ true, /* extend */
339+ )
340+}
341+
342+pub fn extend_prev_char(cx: &mut Context) {
343+ _find_char(
344+ cx,
345+ search::find_nth_prev,
346+ true, /* inclusive */
347+ true, /* extend */
348+ )
349+}
350+
269351fn scroll(view: &mut View, offset: usize, direction: Direction) {
270352 use Direction::*;
271353 let text = view.doc.text().slice(..);