@@ -132,9 +132,9 @@ pub fn textobject_paragraph(
132132 if prev_empty_to_line || curr_empty_to_line {
133133 line_back += 1;
134134 }
135− let mut lines = slice.lines_at(line_back);
136135 // do not include current paragraph on paragraph end (include next)
137136 if !(curr_empty_to_line && last_char) {
137+ let mut lines = slice.lines_at(line_back);
138138 lines.reverse();
139139 let mut lines = lines.map(rope_is_line_ending).peekable();
140140 while lines.next_if(|&e| e).is_some() {
@@ -150,25 +150,46 @@ pub fn textobject_paragraph(
150150 line += 1;
151151 }
152152 let mut lines = slice.lines_at(line).map(rope_is_line_ending).peekable();
153− for _ in 0..count - 1 {
153+ let mut count_done = 0; // count how many non-whitespace paragraphs done
154+ for _ in 0..count {
155+ let mut done = false;
154156 while lines.next_if(|&e| !e).is_some() {
155157 line += 1;
158+ done = true;
156159 }
157160 while lines.next_if(|&e| e).is_some() {
158161 line += 1;
159162 }
163+ count_done += done as usize;
160164 }
161− while lines.next_if(|&e| !e).is_some() {
162− line += 1;
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+ }
176+ while lines.next_if(|&e| !e).is_some() {
177+ line_back -= 1;
178+ }
163179 }
180+
164181 // handle last whitespaces part separately depending on textobject
165182 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();
167189 while lines.next_if(|&e| e).is_some() {
168− line += 1;
190+ line -= 1;
169191 }
170192 }
171− TextObject::Inside => {}
172193 TextObject::Movement => unreachable!(),
173194 }
174195
@@ -364,7 +385,7 @@ mod test {
364385 "second\n\n#[paragraph\n|]#\n",
365386 ),
366387 ("#[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"),
368389 (
369390 "empty to line\n#[\n|]#paragraph boundary\n\n",
370391 "empty to line\n\n#[paragraph boundary\n|]#\n",
@@ -418,7 +439,7 @@ mod test {
418439 "second\n\n#[paragraph\n\n|]#",
419440 ),
420441 ("#[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|]#"),
422443 (
423444 "empty to line\n#[\n|]#paragraph boundary\n\n",
424445 "empty to line\n\n#[paragraph boundary\n\n|]#",