fortrangoingonforty/fortty / 6d71706

Browse files

Implement deferred wrap to fix ZSH prompt marker issue

Add pending_wrap flag for deferred line wrapping - cursor stays at EOL
until next character is written, then wraps. This matches xterm behavior
and fixes the ZSH PROMPT_EOL_MARK (%) appearing incorrectly.

Also update DA1/DA2 responses to identify as VT220 with ANSI color support.
Authored by espadonne
SHA
6d71706b48f467b4c30aea87e5574d8a349cbaba
Parents
3ab4723
Tree
7da6c43

2 changed files

StatusFile+-
M src/terminal/parser.f90 10 2
M src/terminal/terminal.f90 22 3
src/terminal/parser.f90modified
@@ -367,6 +367,13 @@ contains
367367
       return
368368
     end if
369369
 
370
+    ! Handle DA2 (CSI > c) - Secondary Device Attributes
371
+    if (p%has_private .and. p%private_marker == '>' .and. cmd == 99) then
372
+      ! Respond: CSI > 0 ; 10 ; 0 c (xterm-compatible: type=0, version=10, rom=0)
373
+      call terminal_queue_response(term, char(27) // '[>0;10;0c')
374
+      return
375
+    end if
376
+
370377
     ! Handle DECSCUSR (CSI Ps SP q) - Set Cursor Style
371378
     if (p%has_intermediate .and. p%intermediate == ' ' .and. cmd == 113) then
372379
       ! Ps=0,1: blinking block, Ps=2: steady block
@@ -461,9 +468,10 @@ contains
461468
         end if
462469
 
463470
       case (99)  ! 'c' - DA1 (Primary Device Attributes)
464
-        ! Respond with VT102 identification: ESC [ ? 6 c
471
+        ! Respond as VT220 with ANSI color support
472
+        ! 62=VT220, 22=ANSI color
465473
         if (p%param_count == 0 .or. (p%param_count == 1 .and. p%params(1) == 0)) then
466
-          call terminal_queue_response(term, char(27) // '[?6c')
474
+          call terminal_queue_response(term, char(27) // '[?62;22c')
467475
         end if
468476
 
469477
       case (110)  ! 'n' - DSR (Device Status Report)
src/terminal/terminal.f90modified
@@ -49,6 +49,7 @@ module terminal_mod
4949
     logical :: mode_autowrap = .true.   ! Auto-wrap at end of line
5050
     logical :: mode_origin = .false.    ! Origin mode (cursor relative to scroll region)
5151
     logical :: mode_insert = .false.    ! Insert mode
52
+    logical :: pending_wrap = .false.   ! Deferred wrap: cursor at EOL, wrap on next char
5253
 
5354
     ! Tab stops
5455
     logical, allocatable :: tabstops(:)
@@ -182,6 +183,13 @@ contains
182183
     ! Skip zero-width characters (combining marks, etc.)
183184
     if (width == 0) return
184185
 
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
+
185193
     ! Check if wide character fits before line end
186194
     if (width == 2 .and. term%cursor%col + 1 > term%cols) then
187195
       if (term%mode_autowrap) then
@@ -218,11 +226,12 @@ contains
218226
     ! Advance cursor by character width
219227
     term%cursor%col = term%cursor%col + width
220228
 
221
-    ! Handle wrap at end of line
229
+    ! Handle wrap at end of line - use deferred wrap
222230
     if (term%cursor%col > term%cols) then
223231
       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.
226235
       else
227236
         term%cursor%col = term%cols  ! Stay at edge
228237
       end if
@@ -247,6 +256,7 @@ contains
247256
     type(terminal_t), intent(inout) :: term
248257
 
249258
     term%cursor%col = 1
259
+    term%pending_wrap = .false.  ! Cancel deferred wrap
250260
   end subroutine terminal_carriage_return
251261
 
252262
   ! Move to next tab stop
@@ -254,6 +264,8 @@ contains
254264
     type(terminal_t), intent(inout) :: term
255265
     integer :: col
256266
 
267
+    term%pending_wrap = .false.  ! Cancel deferred wrap
268
+
257269
     do col = term%cursor%col + 1, term%cols
258270
       if (term%tabstops(col)) then
259271
         term%cursor%col = col
@@ -269,6 +281,7 @@ contains
269281
   subroutine terminal_backspace(term)
270282
     type(terminal_t), intent(inout) :: term
271283
 
284
+    term%pending_wrap = .false.  ! Cancel deferred wrap
272285
     if (term%cursor%col > 1) then
273286
       term%cursor%col = term%cursor%col - 1
274287
     end if
@@ -392,6 +405,7 @@ contains
392405
     type(terminal_t), intent(inout) :: term
393406
     integer, intent(in) :: row, col
394407
 
408
+    term%pending_wrap = .false.  ! Cancel deferred wrap
395409
     term%cursor%row = max(1, min(row, term%rows))
396410
     term%cursor%col = max(1, min(col, term%cols))
397411
   end subroutine terminal_cursor_move
@@ -401,6 +415,7 @@ contains
401415
     type(terminal_t), intent(inout) :: term
402416
     integer, intent(in) :: n
403417
 
418
+    term%pending_wrap = .false.  ! Cancel deferred wrap
404419
     term%cursor%row = max(1, term%cursor%row - n)
405420
   end subroutine terminal_cursor_up
406421
 
@@ -409,6 +424,7 @@ contains
409424
     type(terminal_t), intent(inout) :: term
410425
     integer, intent(in) :: n
411426
 
427
+    term%pending_wrap = .false.  ! Cancel deferred wrap
412428
     term%cursor%row = min(term%rows, term%cursor%row + n)
413429
   end subroutine terminal_cursor_down
414430
 
@@ -417,6 +433,7 @@ contains
417433
     type(terminal_t), intent(inout) :: term
418434
     integer, intent(in) :: n
419435
 
436
+    term%pending_wrap = .false.  ! Cancel deferred wrap
420437
     term%cursor%col = min(term%cols, term%cursor%col + n)
421438
   end subroutine terminal_cursor_forward
422439
 
@@ -425,6 +442,7 @@ contains
425442
     type(terminal_t), intent(inout) :: term
426443
     integer, intent(in) :: n
427444
 
445
+    term%pending_wrap = .false.  ! Cancel deferred wrap
428446
     term%cursor%col = max(1, term%cursor%col - n)
429447
   end subroutine terminal_cursor_backward
430448
 
@@ -628,6 +646,7 @@ contains
628646
     term%mode_autowrap = .true.
629647
     term%mode_origin = .false.
630648
     term%mode_insert = .false.
649
+    term%pending_wrap = .false.
631650
 
632651
     ! Reset cursor
633652
     term%cursor%row = 1