Stabilize dired Qt attach path

ober

3f3f1ff5d6523192db8c7b859dbb3c738aec624d

diff --git a/lib/jerboa-emacs/qt/buffer.sls b/lib/jerboa-emacs/qt/buffer.sls
index 6447eb1..8d338a3 100644
--- a/lib/jerboa-emacs/qt/buffer.sls
+++ b/lib/jerboa-emacs/qt/buffer.sls
@@ -38,7 +38,7 @@
          (hash-remove! *doc-editor-map* doc)
          (hash-remove! *doc-buffer-map* doc)
          (buffer-list-remove! buf)))
-  (def (qt-buffer-attach! editor buf)
+  (def (qt-buffer-attach! editor buf (run-post-hook? #t))
        "Switch editor to display this buffer's document.\n   Re-applies the document's read-only state after swap because QScintilla\n   may have a widget-level readOnly flag that persists across document switches.\n   Runs post-buffer-attach-hook to handle image/text display toggling.\n   All visual changes are batched via setUpdatesEnabled to prevent flicker."
        (verbose-log! "qt-buffer-attach! buf=" (buffer-name buf))
        (qt-widget-set-updates-enabled! editor #f)
@@ -53,9 +53,10 @@
            (qt-plain-text-edit-set-line-wrap!
              editor
              (not (or (eq? lang 'terminal) (eq? lang 'shell)))))
-         (verbose-log!
-           "qt-buffer-attach! post-buffer-attach-hook begin")
-         (run-hooks! 'post-buffer-attach-hook editor buf)
+         (when run-post-hook?
+           (verbose-log!
+             "qt-buffer-attach! post-buffer-attach-hook begin")
+           (run-hooks! 'post-buffer-attach-hook editor buf))
          (verbose-log!
            "qt-buffer-attach! done buf="
            (buffer-name buf)))
diff --git a/lib/jerboa-emacs/qt/commands-edit.sls b/lib/jerboa-emacs/qt/commands-edit.sls
index 328e26a..0976880 100644
--- a/lib/jerboa-emacs/qt/commands-edit.sls
+++ b/lib/jerboa-emacs/qt/commands-edit.sls
@@ -1065,28 +1065,38 @@
                    (chat-send! cs input)))))))
   (def (dired-open-directory! app dir-path)
        "Open a directory listing in a new dired buffer."
-       (let* ([dir (strip-trailing-slash dir-path)]
-              [name (string-append dir "/")]
-              [fr (app-state-frame app)]
-              [ed (current-qt-editor app)]
-              [buf (qt-buffer-create! name ed dir)])
-         (buffer-lexer-lang-set! buf 'dired)
-         (qt-buffer-attach! ed buf)
-         (qt-edit-window-buffer-set! (qt-current-window fr) buf)
+       (let ([dir (strip-trailing-slash dir-path)])
+         (verbose-log! "dired-open: begin dir=" dir)
          (let-values ([(text entries) (dired-format-listing dir)])
-           (qt-plain-text-edit-set-text! ed text)
-           (qt-text-document-set-modified! (buffer-doc-pointer buf) #f)
-           (qt-plain-text-edit-move-cursor! ed QT_CURSOR_START)
-           (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
-           (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
-           (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
-           (qt-plain-text-edit-move-cursor!
-             ed
-             QT_CURSOR_START_OF_BLOCK)
-           (hash-put! *dired-entries* buf entries))
-         (echo-message!
-           (app-state-echo app)
-           (string-append "Directory: " dir))))
+           (verbose-log!
+             "dired-open: listing ready len="
+             (number->string (string-length text))
+             " entries="
+             (number->string (vector-length entries)))
+           (let* ([name (string-append dir "/")]
+                  [fr (app-state-frame app)]
+                  [ed (current-qt-editor app)]
+                  [buf (qt-buffer-create! name ed dir)])
+             (buffer-lexer-lang-set! buf 'dired)
+             (qt-buffer-attach! ed buf #f)
+             (verbose-log! "dired-open: attach without hook done")
+             (qt-edit-window-buffer-set! (qt-current-window fr) buf)
+             (qt-plain-text-edit-set-text! ed text)
+             (verbose-log! "dired-open: set text done")
+             (qt-text-document-set-modified! (buffer-doc-pointer buf) #f)
+             (qt-plain-text-edit-move-cursor! ed QT_CURSOR_START)
+             (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
+             (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
+             (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
+             (qt-plain-text-edit-move-cursor!
+               ed
+               QT_CURSOR_START_OF_BLOCK)
+             (hash-put! *dired-entries* buf entries)
+             (run-hooks! 'post-buffer-attach-hook ed buf)
+             (verbose-log! "dired-open: post attach hook done")
+             (echo-message!
+               (app-state-echo app)
+               (string-append "Directory: " dir))))))
   (def (cmd-dired-find-file app)
        "In a dired buffer, open the file or directory under cursor."
        (let* ([buf (current-qt-buffer app)]
diff --git a/src/jerboa-emacs/qt/buffer.ss b/src/jerboa-emacs/qt/buffer.ss
index 3cc31f2..57c9139 100644
--- a/src/jerboa-emacs/qt/buffer.ss
+++ b/src/jerboa-emacs/qt/buffer.ss
@@ -48,7 +48,7 @@
     (hash-remove! *doc-buffer-map* doc)
     (buffer-list-remove! buf)))
 
-(def (qt-buffer-attach! editor buf)
+(def (qt-buffer-attach! editor buf (run-post-hook? #t))
   "Switch editor to display this buffer's document.
    Re-applies the document's read-only state after swap because QScintilla
    may have a widget-level readOnly flag that persists across document switches.
@@ -74,8 +74,9 @@
     (let ((lang (buffer-lexer-lang buf)))
       (qt-plain-text-edit-set-line-wrap! editor
         (not (or (eq? lang 'terminal) (eq? lang 'shell)))))
-    (verbose-log! "qt-buffer-attach! post-buffer-attach-hook begin")
-    ;; Toggle image/editor display via hook (set up in qt/app.ss)
-    (run-hooks! 'post-buffer-attach-hook editor buf)
+    (when run-post-hook?
+      (verbose-log! "qt-buffer-attach! post-buffer-attach-hook begin")
+      ;; Toggle image/editor display via hook (set up in qt/app.ss)
+      (run-hooks! 'post-buffer-attach-hook editor buf))
     (verbose-log! "qt-buffer-attach! done buf=" (buffer-name buf)))
   (qt-widget-set-updates-enabled! editor #t))
diff --git a/src/jerboa-emacs/qt/commands-edit.ss b/src/jerboa-emacs/qt/commands-edit.ss
index b107b1f..05b36cd 100644
--- a/src/jerboa-emacs/qt/commands-edit.ss
+++ b/src/jerboa-emacs/qt/commands-edit.ss
@@ -964,29 +964,39 @@
 
 (def (dired-open-directory! app dir-path)
   "Open a directory listing in a new dired buffer."
-  (let* ((dir (strip-trailing-slash dir-path))
-         (name (string-append dir "/"))
-         (fr (app-state-frame app))
-         (ed (current-qt-editor app))
-         (buf (qt-buffer-create! name ed dir)))
-    ;; Mark as dired buffer
-    (set! (buffer-lexer-lang buf) 'dired)
-    ;; Attach buffer to editor
-    (qt-buffer-attach! ed buf)
-    (set! (qt-edit-window-buffer (qt-current-window fr)) buf)
-    ;; Generate and set listing
+  (let ((dir (strip-trailing-slash dir-path)))
+    (verbose-log! "dired-open: begin dir=" dir)
+    ;; Generate the listing before swapping the new document into the editor.
+    ;; The post-attach hook expects the displayed document to be stable.
     (let-values (((text entries) (dired-format-listing dir)))
-      (qt-plain-text-edit-set-text! ed text)
-      (qt-text-document-set-modified! (buffer-doc-pointer buf) #f)
-      ;; Position cursor at first entry (line 3, after header + count + blank)
-      (qt-plain-text-edit-move-cursor! ed QT_CURSOR_START)
-      (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
-      (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
-      (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
-      (qt-plain-text-edit-move-cursor! ed QT_CURSOR_START_OF_BLOCK)
-      ;; Store entries for navigation
-      (hash-put! *dired-entries* buf entries))
-    (echo-message! (app-state-echo app) (string-append "Directory: " dir))))
+      (verbose-log! "dired-open: listing ready len="
+                    (number->string (string-length text))
+                    " entries=" (number->string (vector-length entries)))
+      (let* ((name (string-append dir "/"))
+             (fr (app-state-frame app))
+             (ed (current-qt-editor app))
+             (buf (qt-buffer-create! name ed dir)))
+        ;; Mark as dired buffer
+        (set! (buffer-lexer-lang buf) 'dired)
+        ;; Attach without running post-buffer-attach-hook yet. We install the
+        ;; listing first, then run the hook once against the populated document.
+        (qt-buffer-attach! ed buf #f)
+        (verbose-log! "dired-open: attach without hook done")
+        (set! (qt-edit-window-buffer (qt-current-window fr)) buf)
+        (qt-plain-text-edit-set-text! ed text)
+        (verbose-log! "dired-open: set text done")
+        (qt-text-document-set-modified! (buffer-doc-pointer buf) #f)
+        ;; Position cursor at first entry (line 3, after header + count + blank)
+        (qt-plain-text-edit-move-cursor! ed QT_CURSOR_START)
+        (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
+        (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
+        (qt-plain-text-edit-move-cursor! ed QT_CURSOR_DOWN)
+        (qt-plain-text-edit-move-cursor! ed QT_CURSOR_START_OF_BLOCK)
+        ;; Store entries for navigation
+        (hash-put! *dired-entries* buf entries)
+        (run-hooks! 'post-buffer-attach-hook ed buf)
+        (verbose-log! "dired-open: post attach hook done")
+        (echo-message! (app-state-echo app) (string-append "Directory: " dir))))))
 
 (def (cmd-dired-find-file app)
   "In a dired buffer, open the file or directory under cursor."
@@ -1641,4 +1651,3 @@ Returns the clean text (without ANSI codes)."
             (qt-text-document-set-modified! (buffer-doc-pointer buf) #f)))
         (lambda (err)
           (echo-error! echo err))))))
-