@@ -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;
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;
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;
157 while lines.next_if(|&e| e).is_some() {
160 while lines.next_if(|&e| e).is_some() {
158 line += 1;
161 line += 1;
163+ count_done += done as usize;
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();
170+ let mut lines = slice.lines_at(line_back);
172+ let mut lines = lines.map(rope_is_line_ending).peekable();
173+ while lines.next_if(|&e| e).is_some() {
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;
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);
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;
171− TextObject::Inside => {}
172 TextObject::Movement => unreachable!(),
193 TextObject::Movement => unreachable!(),
@@ -364,7 +385,7 @@ mod test {
364 "second\n\n#[paragraph\n|]#\n",
385 "second\n\n#[paragraph\n|]#\n",
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"),
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|]#",
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|]#"),
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|]#",