@@ -120,76 +120,84 @@ impl State {
120120 }
121121 }
122122
123− pub fn move_next_word_start(slice: RopeSlice, mut pos: usize) -> usize {
123+ pub fn move_next_word_start(slice: RopeSlice, mut pos: usize, count: usize) -> usize {
124124 // TODO: confirm it's fine without using graphemes, I think it should be
125− let ch = slice.char(pos);
126− let next = slice.char(pos.saturating_add(1));
127− if categorize(ch) != categorize(next) {
128− pos += 1;
129− }
125+ for _ in 0..count {
126+ let ch = slice.char(pos);
127+ let next = slice.char(pos.saturating_add(1));
128+ if categorize(ch) != categorize(next) {
129+ pos += 1;
130+ }
130131
131− // refetch
132− let ch = slice.char(pos);
132+ // refetch
133+ let ch = slice.char(pos);
133134
134− if is_word(ch) {
135− skip_over_next(slice, &mut pos, is_word);
136− } else if ch.is_ascii_punctuation() {
137− skip_over_next(slice, &mut pos, |ch| ch.is_ascii_punctuation());
138− }
135+ if is_word(ch) {
136+ skip_over_next(slice, &mut pos, is_word);
137+ } else if ch.is_ascii_punctuation() {
138+ skip_over_next(slice, &mut pos, |ch| ch.is_ascii_punctuation());
139+ }
139140
140− // TODO: don't include newline?
141− skip_over_next(slice, &mut pos, |ch| ch.is_ascii_whitespace());
141+ // TODO: don't include newline?
142+ skip_over_next(slice, &mut pos, |ch| ch.is_ascii_whitespace());
143+ }
142144
143145 pos
144146 }
145147
146− pub fn move_prev_word_start(slice: RopeSlice, mut pos: usize) -> usize {
148+ pub fn move_prev_word_start(slice: RopeSlice, mut pos: usize, count: usize) -> usize {
147149 // TODO: confirm it's fine without using graphemes, I think it should be
148− let ch = slice.char(pos);
149− let prev = slice.char(pos.saturating_sub(1)); // TODO: just return original pos if at start
150+ for _ in 0..count {
151+ let ch = slice.char(pos);
152+ let prev = slice.char(pos.saturating_sub(1)); // TODO: just return original pos if at start
150153
151− if categorize(ch) != categorize(prev) {
152− pos -= 1;
153− }
154+ if categorize(ch) != categorize(prev) {
155+ pos -= 1;
156+ }
154157
155− // TODO: skip while eol
158+ // TODO: skip while eol
156159
157− // TODO: don't include newline?
158− skip_over_prev(slice, &mut pos, |ch| ch.is_ascii_whitespace());
160+ // TODO: don't include newline?
161+ skip_over_prev(slice, &mut pos, |ch| ch.is_ascii_whitespace());
159162
160− // refetch
161− let ch = slice.char(pos);
163+ // refetch
164+ let ch = slice.char(pos);
162165
163− if is_word(ch) {
164− skip_over_prev(slice, &mut pos, is_word);
165− } else if ch.is_ascii_punctuation() {
166− skip_over_prev(slice, &mut pos, |ch| ch.is_ascii_punctuation());
166+ if is_word(ch) {
167+ skip_over_prev(slice, &mut pos, is_word);
168+ } else if ch.is_ascii_punctuation() {
169+ skip_over_prev(slice, &mut pos, |ch| ch.is_ascii_punctuation());
170+ }
171+ pos = pos.saturating_add(1)
167172 }
168173
169− pos.saturating_add(1)
174+ pos
170175 }
171176
172− pub fn move_next_word_end(slice: RopeSlice, mut pos: usize, _count: usize) -> usize {
173− // TODO: confirm it's fine without using graphemes, I think it should be
174− let ch = slice.char(pos);
175− let next = slice.char(pos.saturating_add(1));
176− if categorize(ch) != categorize(next) {
177− pos += 1;
178− }
177+ pub fn move_next_word_end(slice: RopeSlice, mut pos: usize, count: usize) -> usize {
178+ for _ in 0..count {
179+ // TODO: confirm it's fine without using graphemes, I think it should be
180+ let ch = slice.char(pos);
181+ let next = slice.char(pos.saturating_add(1));
182+ if categorize(ch) != categorize(next) {
183+ pos += 1;
184+ }
179185
180− // TODO: don't include newline?
181− skip_over_next(slice, &mut pos, |ch| ch.is_ascii_whitespace());
186+ // TODO: don't include newline?
187+ skip_over_next(slice, &mut pos, |ch| ch.is_ascii_whitespace());
182188
183− // refetch
184− let ch = slice.char(pos);
189+ // refetch
190+ let ch = slice.char(pos);
185191
186− if is_word(ch) {
187− skip_over_next(slice, &mut pos, is_word);
188− } else if ch.is_ascii_punctuation() {
189− skip_over_next(slice, &mut pos, |ch| ch.is_ascii_punctuation());
192+ if is_word(ch) {
193+ skip_over_next(slice, &mut pos, is_word);
194+ } else if ch.is_ascii_punctuation() {
195+ skip_over_next(slice, &mut pos, |ch| ch.is_ascii_punctuation());
196+ }
197+ pos = pos.saturating_sub(1)
190198 }
191199
192− pos.saturating_sub(1)
200+ pos
193201 }
194202
195203 pub fn move_selection(