@@ -49,6 +49,7 @@ module terminal_mod |
| 49 | 49 | logical :: mode_autowrap = .true. ! Auto-wrap at end of line |
| 50 | 50 | logical :: mode_origin = .false. ! Origin mode (cursor relative to scroll region) |
| 51 | 51 | logical :: mode_insert = .false. ! Insert mode |
| 52 | + logical :: pending_wrap = .false. ! Deferred wrap: cursor at EOL, wrap on next char |
| 52 | 53 | |
| 53 | 54 | ! Tab stops |
| 54 | 55 | logical, allocatable :: tabstops(:) |
@@ -182,6 +183,13 @@ contains |
| 182 | 183 | ! Skip zero-width characters (combining marks, etc.) |
| 183 | 184 | if (width == 0) return |
| 184 | 185 | |
| 186 | + ! Handle deferred wrap: if pending, execute wrap now before writing new char |
| 187 | + if (term%pending_wrap) then |
| 188 | + call terminal_newline(term) |
| 189 | + term%cursor%col = 1 |
| 190 | + term%pending_wrap = .false. |
| 191 | + end if |
| 192 | + |
| 185 | 193 | ! Check if wide character fits before line end |
| 186 | 194 | if (width == 2 .and. term%cursor%col + 1 > term%cols) then |
| 187 | 195 | if (term%mode_autowrap) then |
@@ -218,11 +226,12 @@ contains |
| 218 | 226 | ! Advance cursor by character width |
| 219 | 227 | term%cursor%col = term%cursor%col + width |
| 220 | 228 | |
| 221 | | - ! Handle wrap at end of line |
| 229 | + ! Handle wrap at end of line - use deferred wrap |
| 222 | 230 | if (term%cursor%col > term%cols) then |
| 223 | 231 | if (term%mode_autowrap) then |
| 224 | | - call terminal_newline(term) |
| 225 | | - term%cursor%col = 1 |
| 232 | + ! Don't wrap yet - set pending flag, wrap on next char |
| 233 | + term%cursor%col = term%cols ! Keep cursor at last column |
| 234 | + term%pending_wrap = .true. |
| 226 | 235 | else |
| 227 | 236 | term%cursor%col = term%cols ! Stay at edge |
| 228 | 237 | end if |
@@ -247,6 +256,7 @@ contains |
| 247 | 256 | type(terminal_t), intent(inout) :: term |
| 248 | 257 | |
| 249 | 258 | term%cursor%col = 1 |
| 259 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 250 | 260 | end subroutine terminal_carriage_return |
| 251 | 261 | |
| 252 | 262 | ! Move to next tab stop |
@@ -254,6 +264,8 @@ contains |
| 254 | 264 | type(terminal_t), intent(inout) :: term |
| 255 | 265 | integer :: col |
| 256 | 266 | |
| 267 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 268 | + |
| 257 | 269 | do col = term%cursor%col + 1, term%cols |
| 258 | 270 | if (term%tabstops(col)) then |
| 259 | 271 | term%cursor%col = col |
@@ -269,6 +281,7 @@ contains |
| 269 | 281 | subroutine terminal_backspace(term) |
| 270 | 282 | type(terminal_t), intent(inout) :: term |
| 271 | 283 | |
| 284 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 272 | 285 | if (term%cursor%col > 1) then |
| 273 | 286 | term%cursor%col = term%cursor%col - 1 |
| 274 | 287 | end if |
@@ -392,6 +405,7 @@ contains |
| 392 | 405 | type(terminal_t), intent(inout) :: term |
| 393 | 406 | integer, intent(in) :: row, col |
| 394 | 407 | |
| 408 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 395 | 409 | term%cursor%row = max(1, min(row, term%rows)) |
| 396 | 410 | term%cursor%col = max(1, min(col, term%cols)) |
| 397 | 411 | end subroutine terminal_cursor_move |
@@ -401,6 +415,7 @@ contains |
| 401 | 415 | type(terminal_t), intent(inout) :: term |
| 402 | 416 | integer, intent(in) :: n |
| 403 | 417 | |
| 418 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 404 | 419 | term%cursor%row = max(1, term%cursor%row - n) |
| 405 | 420 | end subroutine terminal_cursor_up |
| 406 | 421 | |
@@ -409,6 +424,7 @@ contains |
| 409 | 424 | type(terminal_t), intent(inout) :: term |
| 410 | 425 | integer, intent(in) :: n |
| 411 | 426 | |
| 427 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 412 | 428 | term%cursor%row = min(term%rows, term%cursor%row + n) |
| 413 | 429 | end subroutine terminal_cursor_down |
| 414 | 430 | |
@@ -417,6 +433,7 @@ contains |
| 417 | 433 | type(terminal_t), intent(inout) :: term |
| 418 | 434 | integer, intent(in) :: n |
| 419 | 435 | |
| 436 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 420 | 437 | term%cursor%col = min(term%cols, term%cursor%col + n) |
| 421 | 438 | end subroutine terminal_cursor_forward |
| 422 | 439 | |
@@ -425,6 +442,7 @@ contains |
| 425 | 442 | type(terminal_t), intent(inout) :: term |
| 426 | 443 | integer, intent(in) :: n |
| 427 | 444 | |
| 445 | + term%pending_wrap = .false. ! Cancel deferred wrap |
| 428 | 446 | term%cursor%col = max(1, term%cursor%col - n) |
| 429 | 447 | end subroutine terminal_cursor_backward |
| 430 | 448 | |
@@ -628,6 +646,7 @@ contains |
| 628 | 646 | term%mode_autowrap = .true. |
| 629 | 647 | term%mode_origin = .false. |
| 630 | 648 | term%mode_insert = .false. |
| 649 | + term%pending_wrap = .false. |
| 631 | 650 | |
| 632 | 651 | ! Reset cursor |
| 633 | 652 | term%cursor%row = 1 |