Sprint 1 COMPLETE: Qt foundation modules and build infrastructure

ober

c0b80698c5662e16eaf643bd9a5f68cbc1069469

diff --git a/Makefile b/Makefile
index a6f06c6..91aacee 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ export CHEZ_SCINTILLA_LIB := $(HOME)/mine/chez-scintilla
 .PHONY: all build rebuild run test-tier0 test-tier2 test-tier3 test-tier4 test-tier5 test-org test-extra test clean clean-generated \
         test-org-duration test-org-element test-org-fold test-org-footnote \
         test-org-lint test-org-num test-org-property test-org-src test-org-tempo \
-        test-vtscreen test-debug-repl test-qt
+        test-vtscreen test-debug-repl test-qt build-qt
 
 all: build test
 
@@ -25,6 +25,11 @@ rebuild:
 run: build
 	$(SCHEME) $(LIBDIRS) --script main.ss
 
+# Qt backend build target (for future use)
+build-qt: build
+	@echo "Qt modules: qt/sci-shim.sls qt/keymap.sls qt/buffer.sls qt/window.sls (stub)"
+	@echo "Sprint 1 complete - 4/54 Qt modules ported"
+
 test: build test-tier0 test-tier2 test-tier3 test-tier4 test-tier5 test-org test-extra
 
 test-tier0:
diff --git a/lib/jerboa-emacs/qt/window.sls b/lib/jerboa-emacs/qt/window.sls
new file mode 100644
index 0000000..f4a3e83
--- /dev/null
+++ b/lib/jerboa-emacs/qt/window.sls
@@ -0,0 +1,88 @@
+#!chezscheme
+;;; Generated by jerbuild — DO NOT EDIT
+;;; Source: src/jerboa-emacs/qt/window.ss
+
+(library (jerboa-emacs qt window)
+  (export qt-edit-window::t make-qt-edit-window qt-edit-window?
+   qt-edit-window-editor qt-edit-window-container
+   qt-edit-window-buffer qt-edit-window-line-number-area
+   qt-edit-window-image-scroll qt-edit-window-image-label
+   qt-edit-window-editor-set! qt-edit-window-container-set!
+   qt-edit-window-buffer-set!
+   qt-edit-window-line-number-area-set!
+   qt-edit-window-image-scroll-set!
+   qt-edit-window-image-label-set! qt-frame::t make-qt-frame
+   qt-frame? qt-frame-splitter qt-frame-root qt-frame-windows
+   qt-frame-current-idx qt-frame-main-win
+   qt-frame-splitter-set! qt-frame-root-set!
+   qt-frame-windows-set! qt-frame-current-idx-set!
+   qt-frame-main-win-set! split-leaf::t make-split-leaf
+   split-leaf? split-leaf-edit-window
+   split-leaf-edit-window-set! split-node::t make-split-node
+   split-node? split-node-orientation split-node-splitter
+   split-node-children split-node-orientation-set!
+   split-node-splitter-set! split-node-children-set!
+   qt-current-window qt-current-editor qt-current-buffer
+   qt-frame-init! qt-frame-split! qt-frame-split-right!
+   qt-frame-delete-window! qt-frame-delete-other-windows!
+   qt-frame-other-window! qt-apply-editor-theme!
+   split-tree-flatten split-tree-find-parent
+   split-tree-find-leaf split-tree-collect-sub-splitters)
+  (import
+    (except (chezscheme) make-hash-table hash-table? iota \x31;+ \x31;-
+      getenv path-extension path-absolute? thread? make-mutex
+      mutex? mutex-name)
+    (std sugar) (jerboa-emacs qt sci-shim) (jerboa-emacs core)
+    (jerboa-emacs face) (jerboa-emacs qt buffer) (jerboa core)
+    (jerboa runtime))
+  (defstruct
+    qt-edit-window
+    (editor container buffer line-number-area image-scroll
+      image-label)
+    transparent:
+    #t)
+  (defstruct split-leaf (edit-window) transparent: #t)
+  (defstruct
+    split-node
+    (orientation splitter children)
+    transparent:
+    #t)
+  (defstruct
+    qt-frame
+    (splitter root windows current-idx main-win)
+    transparent:
+    #t)
+  (def (qt-current-window fr)
+       (list-ref (qt-frame-windows fr) (qt-frame-current-idx fr)))
+  (def (qt-current-editor fr)
+       (qt-edit-window-editor (qt-current-window fr)))
+  (def (qt-current-buffer fr)
+       (qt-edit-window-buffer (qt-current-window fr)))
+  (def (split-tree-flatten root)
+       (if (split-leaf? root)
+           (list (split-leaf-edit-window root))
+           '()))
+  (def (split-tree-find-parent tree target-splitter) #f)
+  (def (split-tree-find-leaf tree pred) #f)
+  (def (split-tree-collect-sub-splitters tree) '())
+  (def (qt-frame-init! main-win buffer)
+       (let* ([editor (qt-plain-text-edit-create)]
+              [container editor]
+              [win (make-qt-edit-window editor container buffer #f #f #f)]
+              [leaf (make-split-leaf win)]
+              [splitter #f]
+              [fr (make-qt-frame splitter leaf (list win) 0 main-win)])
+         (qt-buffer-attach! editor buffer)
+         fr))
+  (def (qt-frame-split! fr (horizontal? #t)) (void))
+  (def (qt-frame-split-right! fr) (void))
+  (def (qt-frame-delete-window! fr) (void))
+  (def (qt-frame-delete-other-windows! fr) (void))
+  (def (qt-frame-other-window! fr (delta 1))
+       (let* ([wins (qt-frame-windows fr)]
+              [n (length wins)]
+              [cur (qt-frame-current-idx fr)]
+              [new-idx (modulo (+ cur delta) n)])
+         (qt-frame-current-idx-set! fr new-idx)
+         (qt-current-window fr)))
+  (def (qt-apply-editor-theme! editor) (void)))
diff --git a/src/jerboa-emacs/qt/window.ss b/src/jerboa-emacs/qt/window.ss
new file mode 100644
index 0000000..71a207e
--- /dev/null
+++ b/src/jerboa-emacs/qt/window.ss
@@ -0,0 +1,139 @@
+;;; -*- Gerbil -*-
+;;; Qt frame/window management for jerboa-emacs
+;;;
+;;; Ported from gerbil-emacs/qt/window.ss
+;;; STUB VERSION - Full implementation to be completed in Sprint 2
+;;; Uses nested QSplitters to hold multiple QPlainTextEdit panes.
+
+(export (struct-out qt-edit-window)
+        (struct-out qt-frame)
+        (struct-out split-leaf)
+        (struct-out split-node)
+        qt-current-window
+        qt-current-editor
+        qt-current-buffer
+        qt-frame-init!
+        qt-frame-split!
+        qt-frame-split-right!
+        qt-frame-delete-window!
+        qt-frame-delete-other-windows!
+        qt-frame-other-window!
+        qt-apply-editor-theme!
+        split-tree-flatten
+        split-tree-find-parent
+        split-tree-find-leaf
+        split-tree-collect-sub-splitters)
+
+(import :std/sugar
+        :jerboa-emacs/qt/sci-shim
+        :jerboa-emacs/core
+        :jerboa-emacs/face
+        :jerboa-emacs/qt/buffer)
+
+;;;============================================================================
+;;; Structures
+;;;============================================================================
+
+(defstruct qt-edit-window
+  (editor           ; QScintilla editor pointer
+   container        ; QStackedWidget wrapping editor + image widget
+   buffer           ; buffer struct
+   line-number-area ; line-number-area pointer or #f
+   image-scroll     ; QScrollArea for image display, or #f (lazy)
+   image-label)     ; QLabel inside scroll area, or #f (lazy)
+  transparent: #t)
+
+(defstruct split-leaf (edit-window) transparent: #t)
+
+(defstruct split-node
+  (orientation   ; QT_HORIZONTAL | QT_VERTICAL
+   splitter      ; QSplitter widget pointer
+   children)     ; list of split-leaf | split-node
+  transparent: #t)
+
+(defstruct qt-frame
+  (splitter     ; QSplitter pointer (permanent root splitter)
+   root         ; split-leaf | split-node
+   windows      ; list of qt-edit-window
+   current-idx  ; index of active window
+   main-win)    ; QMainWindow pointer
+  transparent: #t)
+
+;;;============================================================================
+;;; Accessors
+;;;============================================================================
+
+(def (qt-current-window fr)
+  (list-ref (qt-frame-windows fr) (qt-frame-current-idx fr)))
+
+(def (qt-current-editor fr)
+  (qt-edit-window-editor (qt-current-window fr)))
+
+(def (qt-current-buffer fr)
+  (qt-edit-window-buffer (qt-current-window fr)))
+
+;;;============================================================================
+;;; Split tree helpers (STUB - to be implemented)
+;;;============================================================================
+
+(def (split-tree-flatten root)
+  ;; STUB: Returns flat list of edit windows from split tree
+  (if (split-leaf? root)
+    (list (split-leaf-edit-window root))
+    '()))
+
+(def (split-tree-find-parent tree target-splitter)
+  ;; STUB: Finds parent of a splitter in tree
+  #f)
+
+(def (split-tree-find-leaf tree pred)
+  ;; STUB: Finds first leaf matching predicate
+  #f)
+
+(def (split-tree-collect-sub-splitters tree)
+  ;; STUB: Collects all sub-splitters
+  '())
+
+;;;============================================================================
+;;; Frame operations (STUB - to be implemented)
+;;;============================================================================
+
+(def (qt-frame-init! main-win buffer)
+  ;; STUB: Initialize frame with initial buffer
+  (let* ((editor (qt-plain-text-edit-create))
+         (container editor)  ; Simplified - should be QStackedWidget
+         (win (make-qt-edit-window editor container buffer #f #f #f))
+         (leaf (make-split-leaf win))
+         (splitter #f)  ; To be created
+         (fr (make-qt-frame splitter leaf (list win) 0 main-win)))
+    (qt-buffer-attach! editor buffer)
+    fr))
+
+(def (qt-frame-split! fr (horizontal? #t))
+  ;; STUB: Split current window
+  (void))
+
+(def (qt-frame-split-right! fr)
+  ;; STUB: Split window vertically (side-by-side)
+  (void))
+
+(def (qt-frame-delete-window! fr)
+  ;; STUB: Delete current window
+  (void))
+
+(def (qt-frame-delete-other-windows! fr)
+  ;; STUB: Delete all windows except current
+  (void))
+
+(def (qt-frame-other-window! fr (delta 1))
+  ;; STUB: Switch to other window
+  (let* ((wins (qt-frame-windows fr))
+         (n (length wins))
+         (cur (qt-frame-current-idx fr))
+         (new-idx (modulo (+ cur delta) n)))
+    (set! (qt-frame-current-idx fr) new-idx)
+    (qt-current-window fr)))
+
+(def (qt-apply-editor-theme! editor)
+  ;; STUB: Apply theme to editor
+  (void))