Fix buffer content bleed: vterm render and async file load write to wrong document

ober

480ae93101031bef6c80e14a845b2f61f7d0bd8a

diff --git a/lib/jerboa-emacs/qt/app.sls b/lib/jerboa-emacs/qt/app.sls
index 24ca0c5..dd6a38f 100644
--- a/lib/jerboa-emacs/qt/app.sls
+++ b/lib/jerboa-emacs/qt/app.sls
@@ -512,7 +512,8 @@
          (let loop ([wins (qt-frame-windows fr)])
            (when (pair? wins)
              (if (eq? (qt-edit-window-buffer (car wins)) buf)
-                 (let ([ed (qt-edit-window-editor (car wins))])
+                 (let ([ed (qt-edit-window-editor (car wins))]
+                       [win (car wins)])
                    (when (and vt (not (terminal-state-pre-pty-text ts)))
                      (let ([pre-text (qt-plain-text-edit-text ed)])
                        (terminal-state-pre-pty-text-set! ts pre-text)
@@ -537,7 +538,8 @@
                          (vterm-cap-scrollback! ts)
                          (when *qt-app-ref*
                            (qt-app-process-events! *qt-app-ref*))
-                         (when (vterm-render-due? ts)
+                         (when (and (eq? (qt-edit-window-buffer win) buf)
+                                    (vterm-render-due? ts))
                            (if (not (hash-ref *vterm-initialized* ts #f))
                                (let* ([rendered (vtscreen-render vt)]
                                       [full (if (vtscreen-alt-screen? vt)
@@ -2383,19 +2385,29 @@
             (qt-buffer-attach! ed buf)
             (qt-edit-window-buffer-set! (qt-current-window fr) buf)
             (if (file-exists? filename)
-                (begin
+                (let ([target-buf buf]
+                      [target-doc (buffer-doc-pointer buf)])
                   (qt-plain-text-edit-set-text! ed "Loading...")
                   (async-read-file!
                     filename
                     (lambda (text)
                       (when text
-                        (let ([ed (qt-current-editor
-                                    (app-state-frame app))])
+                        (let* ([ed (qt-current-editor
+                                     (app-state-frame app))]
+                               [current-doc (sci-send
+                                              ed
+                                              SCI_GETDOCPOINTER
+                                              0)])
+                          (sci-send ed SCI_SETDOCPOINTER 0 target-doc)
                           (qt-plain-text-edit-set-text! ed text)
-                          (qt-text-document-set-modified!
-                            (buffer-doc-pointer buf)
-                            #f)
-                          (qt-plain-text-edit-set-cursor-position! ed 0)))
+                          (qt-text-document-set-modified! target-doc #f)
+                          (sci-send ed SCI_SETDOCPOINTER 0 current-doc)
+                          (when (eq? (qt-current-buffer
+                                       (app-state-frame app))
+                                     target-buf)
+                            (qt-plain-text-edit-set-cursor-position!
+                              ed
+                              0))))
                       (file-mtime-record! filename)
                       (qt-setup-highlighting! app buf)
                       (let ([mode (detect-major-mode filename)])
diff --git a/src/jerboa-emacs/qt/app.ss b/src/jerboa-emacs/qt/app.ss
index 1fbeb7e..536887f 100644
--- a/src/jerboa-emacs/qt/app.ss
+++ b/src/jerboa-emacs/qt/app.ss
@@ -559,7 +559,8 @@
     (let loop ((wins (qt-frame-windows fr)))
       (when (pair? wins)
         (if (eq? (qt-edit-window-buffer (car wins)) buf)
-          (let ((ed (qt-edit-window-editor (car wins))))
+          (let ((ed (qt-edit-window-editor (car wins)))
+                (win (car wins)))
             ;; Save pre-PTY text on first data chunk
             (when (and vt (not (terminal-state-pre-pty-text ts)))
               (let ((pre-text (qt-plain-text-edit-text ed)))
@@ -583,8 +584,11 @@
                 ;; stays responsive even when terminal output is flooding.
                 (when *qt-app-ref*
                   (qt-app-process-events! *qt-app-ref*))
-                ;; Only render to the widget if enough time has elapsed
-                (when (vterm-render-due? ts)
+                ;; Re-check: processEvents may have triggered switch-buffer,
+                ;; changing the editor's active document.  If the window no
+                ;; longer shows this terminal buffer, skip the render.
+                (when (and (eq? (qt-edit-window-buffer win) buf)
+                           (vterm-render-due? ts))
                   (if (not (hash-ref *vterm-initialized* ts #f))
                     ;; First render: full set-text to establish the document.
                     ;; Wrap in set-updates-enabled #f/#t to prevent bounce:
@@ -1913,16 +1917,27 @@
        (qt-buffer-attach! ed buf)
        (set! (qt-edit-window-buffer (qt-current-window fr)) buf)
        (if (file-exists? filename)
-         ;; Read file content in background thread
-         (begin
+         ;; Read file content in background thread.
+         ;; Capture the target buffer and its doc pointer NOW — by the time
+         ;; the async callback fires, the user may have switched to a different
+         ;; buffer, so qt-current-editor would write into the wrong document.
+         (let ((target-buf buf)
+               (target-doc (buffer-doc-pointer buf)))
            (qt-plain-text-edit-set-text! ed "Loading...")
            (async-read-file! filename
              (lambda (text)
                (when text
-                 (let ((ed (qt-current-editor (app-state-frame app))))
+                 (let* ((ed (qt-current-editor (app-state-frame app)))
+                        (current-doc (sci-send ed SCI_GETDOCPOINTER 0)))
+                   ;; Switch to the target buffer's document before writing
+                   (sci-send ed SCI_SETDOCPOINTER 0 target-doc)
                    (qt-plain-text-edit-set-text! ed text)
-                   (qt-text-document-set-modified! (buffer-doc-pointer buf) #f)
-                   (qt-plain-text-edit-set-cursor-position! ed 0)))
+                   (qt-text-document-set-modified! target-doc #f)
+                   ;; Switch back to whatever document was active
+                   (sci-send ed SCI_SETDOCPOINTER 0 current-doc)
+                   ;; If the target buffer IS the current buffer, also set cursor
+                   (when (eq? (qt-current-buffer (app-state-frame app)) target-buf)
+                     (qt-plain-text-edit-set-cursor-position! ed 0))))
                (file-mtime-record! filename)
                (qt-setup-highlighting! app buf)
                (let ((mode (detect-major-mode filename)))