on master help sign in
Make textobject select last paragraph (#1992)
62283fdIvan Tham
helix-core/src/textobject.rs+30−9
@@ -132,9 +132,9 @@ pub fn textobject_paragraph(
132 if prev_empty_to_line || curr_empty_to_line {
132 if prev_empty_to_line || curr_empty_to_line {
133 line_back += 1;
133 line_back += 1;
134 }
134 }
135− let mut lines = slice.lines_at(line_back);
136 // do not include current paragraph on paragraph end (include next)
135 // do not include current paragraph on paragraph end (include next)
137 if !(curr_empty_to_line && last_char) {
136 if !(curr_empty_to_line && last_char) {
137+ let mut lines = slice.lines_at(line_back);
138 lines.reverse();
138 lines.reverse();
139 let mut lines = lines.map(rope_is_line_ending).peekable();
139 let mut lines = lines.map(rope_is_line_ending).peekable();
140 while lines.next_if(|&e| e).is_some() {
140 while lines.next_if(|&e| e).is_some() {
@@ -150,25 +150,46 @@ pub fn textobject_paragraph(
150 line += 1;
150 line += 1;
151 }
151 }
152 let mut lines = slice.lines_at(line).map(rope_is_line_ending).peekable();
152 let mut lines = slice.lines_at(line).map(rope_is_line_ending).peekable();
153+ let mut count_done = 0; // count how many non-whitespace paragraphs done
153− for _ in 0..count - 1 {
154+ for _ in 0..count {
155+ let mut done = false;
154 while lines.next_if(|&e| !e).is_some() {
156 while lines.next_if(|&e| !e).is_some() {
155 line += 1;
157 line += 1;
158+ done = true;
156 }
159 }
157 while lines.next_if(|&e| e).is_some() {
160 while lines.next_if(|&e| e).is_some() {
158 line += 1;
161 line += 1;
159 }
162 }
163+ count_done += done as usize;
160 }
164 }
165+
166+ // search one paragraph backwards for last paragraph
167+ // makes `map` at the end of the paragraph with trailing newlines useful
168+ let last_paragraph = count_done != count && lines.peek().is_none();
169+ if last_paragraph {
170+ let mut lines = slice.lines_at(line_back);
171+ lines.reverse();
172+ let mut lines = lines.map(rope_is_line_ending).peekable();
173+ while lines.next_if(|&e| e).is_some() {
174+ line_back -= 1;
175+ }
161− while lines.next_if(|&e| !e).is_some() {
176+ while lines.next_if(|&e| !e).is_some() {
162− line += 1;
177+ line_back -= 1;
178+ }
163 }
179 }
180+
164 // handle last whitespaces part separately depending on textobject
181 // handle last whitespaces part separately depending on textobject
165 match textobject {
182 match textobject {
166− TextObject::Around => {
183+ TextObject::Around => {}
184+ TextObject::Inside => {
185+ // remove last whitespace paragraph
186+ let mut lines = slice.lines_at(line);
187+ lines.reverse();
188+ let mut lines = lines.map(rope_is_line_ending).peekable();
167 while lines.next_if(|&e| e).is_some() {
189 while lines.next_if(|&e| e).is_some() {
168− line += 1;
190+ line -= 1;
169 }
191 }
170 }
192 }
171− TextObject::Inside => {}
172 TextObject::Movement => unreachable!(),
193 TextObject::Movement => unreachable!(),
173 }
194 }
174
195
@@ -364,7 +385,7 @@ mod test {
364 "second\n\n#[paragraph\n|]#\n",
385 "second\n\n#[paragraph\n|]#\n",
365 ),
386 ),
366 ("#[f|]#irst char\n\n", "#[first char\n|]#\n"),
387 ("#[f|]#irst char\n\n", "#[first char\n|]#\n"),
367− ("last char\n#[\n|]#", "last char\n\n#[|]#"),
388+ ("last char\n#[\n|]#", "#[last char\n|]#\n"),
368 (
389 (
369 "empty to line\n#[\n|]#paragraph boundary\n\n",
390 "empty to line\n#[\n|]#paragraph boundary\n\n",
370 "empty to line\n\n#[paragraph boundary\n|]#\n",
391 "empty to line\n\n#[paragraph boundary\n|]#\n",
@@ -418,7 +439,7 @@ mod test {
418 "second\n\n#[paragraph\n\n|]#",
439 "second\n\n#[paragraph\n\n|]#",
419 ),
440 ),
420 ("#[f|]#irst char\n\n", "#[first char\n\n|]#"),
441 ("#[f|]#irst char\n\n", "#[first char\n\n|]#"),
421− ("last char\n#[\n|]#", "last char\n\n#[|]#"),
442+ ("last char\n#[\n|]#", "#[last char\n\n|]#"),
422 (
443 (
423 "empty to line\n#[\n|]#paragraph boundary\n\n",
444 "empty to line\n#[\n|]#paragraph boundary\n\n",
424 "empty to line\n\n#[paragraph boundary\n\n|]#",
445 "empty to line\n\n#[paragraph boundary\n\n|]#",