Translate one on_bytes event to edit commands. event: (start_row, start_col, old_end_row, old_end_col, new_end_row, new_end_col) Note: column values are UTF-8 BYTE offsets (Neovim convention). old_lines: remembered buffer slice (list of strings, snippet region) new_buf: current
(event, old_lines, new_buf, snippet_start)
| 196 | |
| 197 | |
| 198 | def _on_bytes_to_edits(event, old_lines, new_buf, snippet_start): |
| 199 | """Translate one on_bytes event to edit commands. |
| 200 | |
| 201 | event: (start_row, start_col, old_end_row, old_end_col, new_end_row, new_end_col) |
| 202 | Note: column values are UTF-8 BYTE offsets (Neovim convention). |
| 203 | old_lines: remembered buffer slice (list of strings, snippet region) |
| 204 | new_buf: current full buffer (for reading inserted text) |
| 205 | snippet_start: absolute line number of snippet start |
| 206 | |
| 207 | Returns list of edit commands using CHARACTER columns (UltiSnips |
| 208 | convention), or None if the event extends beyond available data |
| 209 | (caller should fall back to detect_edits/diff). |
| 210 | """ |
| 211 | start_row, start_col_b, old_end_row, old_end_col_b, new_end_row, new_end_col_b = ( |
| 212 | event |
| 213 | ) |
| 214 | rel_row = start_row - snippet_start |
| 215 | |
| 216 | # Bounds checks: change must be within the snippet region we know about |
| 217 | if rel_row < 0 or rel_row >= len(old_lines): |
| 218 | return None |
| 219 | if old_end_row > 0 and rel_row + old_end_row >= len(old_lines): |
| 220 | return None |
| 221 | if new_end_row > 0 and start_row + new_end_row >= len(new_buf): |
| 222 | return None |
| 223 | |
| 224 | cmds = [] |
| 225 | # The line at start_row exists in both old and new (change starts AT this |
| 226 | # row, not above), so byte→char conversion at start_col_b yields the same |
| 227 | # character position in both. |
| 228 | start_col = _byte_to_char_col(old_lines[rel_row], start_col_b) |
| 229 | |
| 230 | # --- Deletion --- |
| 231 | if old_end_row == 0 and old_end_col_b > 0: |
| 232 | end_col = _byte_to_char_col(old_lines[rel_row], start_col_b + old_end_col_b) |
| 233 | deleted = old_lines[rel_row][start_col:end_col] |
| 234 | cmds.append(("D", start_row, start_col, deleted)) |
| 235 | elif old_end_row > 0: |
| 236 | # Multi-line deletion — all at (start_row, start_col) |
| 237 | rest = old_lines[rel_row][start_col:] |
| 238 | if rest: |
| 239 | cmds.append(("D", start_row, start_col, rest)) |
| 240 | cmds.append(("D", start_row, start_col, "\n")) |
| 241 | for i in range(1, old_end_row): |
| 242 | content = old_lines[rel_row + i] |
| 243 | if content: |
| 244 | cmds.append(("D", start_row, start_col, content)) |
| 245 | cmds.append(("D", start_row, start_col, "\n")) |
| 246 | last_line = old_lines[rel_row + old_end_row] |
| 247 | last_end_col = _byte_to_char_col(last_line, old_end_col_b) |
| 248 | last = last_line[:last_end_col] |
| 249 | if last: |
| 250 | cmds.append(("D", start_row, start_col, last)) |
| 251 | |
| 252 | # --- Insertion --- |
| 253 | if new_end_row == 0 and new_end_col_b > 0: |
| 254 | end_col = _byte_to_char_col(new_buf[start_row], start_col_b + new_end_col_b) |
| 255 | inserted = new_buf[start_row][start_col:end_col] |