YILSJ2SNU6DNZVSFL4MD6QSNLQHUNSBSTGGEQCFTWFTRR62IXBWAC -- Active window height. Reserve 1 line for status bar?-- UI logic reserves 1 for status bar in split rendering.let linesInView := if h > 1 then h - 1 else 1
-- Active window height already excludes the global status line.let linesInView := if h > 0 then h else 1
def getSelectedIndex (state : EditorState) (explorer : ExplorerState) : Option Nat :=let cursor := state.getCursorif cursor.row < 2 thennoneelselet idx := cursor.row - 2if idx.val < explorer.entries.length thensome idx.valelsenonedef buildWorkspacePreviewLines (ws : WorkspaceState) : List String :=let header := [s!"Workspace: {ws.name}", ""]let entries := ws.buffers.map fun b =>match b.filename with| some name => name| none => s!"[Untitled:{b.id}]"if entries.isEmpty thenheader ++ ["(no buffers)"]elseheader ++ entriesdef buildWorkgroupPreviewLines (wg : WorkgroupState) : List String :=let header := [s!"Workgroup: {wg.name}", ""]let entries := wg.workspaces.toList.map (fun ws => ws.name)if entries.isEmpty thenheader ++ ["(no workspaces)"]elseheader ++ entriesdef ensurePreviewTextBuffer (state : EditorState) (explorer : ExplorerState) (title : String) (lines : List String) : (EditorState × Nat) :=let content : TextBuffer := if lines.isEmpty then #[#[]] else lines.toArray.map stringToLinelet buf := { ViE.Buffer.fileBufferFromTextBuffer 0 (some s!"preview://{title}") content with dirty := false }match explorer.previewBufferId with| some pid =>let ws := state.getCurrentWorkspaceif ws.buffers.any (fun b => b.id == pid) thenlet updated := { buf with id := pid }let s' := state.updateCurrentWorkspace fun ws =>{ ws with buffers := ws.buffers.map (fun b => if b.id == pid then updated else b) }(s', pid)elselet (pid, s') := ViE.Buffer.addBuffer state buf(s', pid)| none =>let (pid, s') := ViE.Buffer.addBuffer state buf(s', pid)def getPreviewWorkspace (state : EditorState) (explorer : ExplorerState) : WorkspaceState :=let wg := state.getCurrentWorkgroupmatch getSelectedIndex state explorer with| some idx =>if idx >= 2 thenlet real := idx - 2if h : real < wg.workspaces.size thenwg.workspaces[real]!elsestate.getCurrentWorkspaceelsestate.getCurrentWorkspace| none => state.getCurrentWorkspace
def getPreviewWorkgroup (state : EditorState) (explorer : ExplorerState) : WorkgroupState :=match getSelectedIndex state explorer with| some idx =>if idx >= 2 thenlet real := idx - 2if h : real < state.workgroups.size thenstate.workgroups[real]!elsestate.getCurrentWorkgroupelsestate.getCurrentWorkgroup| none => state.getCurrentWorkgroup
match getSelectedEntry state explorer with| none => return state| some entry =>if entry.isDirectory thenreturn stateelselet (s1, previewBufId) ← ensurePreviewBuffer state explorer entry
match explorer.mode with| .files =>match getSelectedEntry state explorer with| none => return state| some entry =>if entry.isDirectory thenreturn stateelselet (s1, previewBufId) ← ensurePreviewBuffer state explorer entrylet s2 := s1.updateCurrentWorkspace fun ws =>{ ws with layout := ws.layout.updateView previewWinId (fun v =>{ v with bufferId := previewBufId, cursor := Point.zero, scrollRow := 0, scrollCol := 0 }) }let s3 := updateExplorerState s2 bufId (fun ex => { ex with previewBufferId := some previewBufId })return s3| .workspaces =>let ws := getPreviewWorkspace state explorerlet lines := buildWorkspacePreviewLines wslet (s1, previewBufId) := ensurePreviewTextBuffer state explorer ws.name lineslet s2 := s1.updateCurrentWorkspace fun ws =>{ ws with layout := ws.layout.updateView previewWinId (fun v =>{ v with bufferId := previewBufId, cursor := Point.zero, scrollRow := 0, scrollCol := 0 }) }let s3 := updateExplorerState s2 bufId (fun ex => { ex with previewBufferId := some previewBufId })return s3| .workgroups =>let wg := getPreviewWorkgroup state explorerlet lines := buildWorkgroupPreviewLines wglet (s1, previewBufId) := ensurePreviewTextBuffer state explorer wg.name lines
match entryOpt with| some entry =>if entry.isDirectory then
match explorer.mode with| .files =>let entryOpt := getSelectedEntry s1 explorermatch entryOpt with| some entry =>if entry.isDirectory thenlet content : TextBuffer := #[#[]]let buf := { ViE.Buffer.fileBufferFromTextBuffer 0 (some "preview://") content with dirty := false }let (pid, s2) := ViE.Buffer.addBuffer s1 bufpure (s2, pid)elseensurePreviewBuffer s1 explorer entry| none =>
elseensurePreviewBuffer s1 explorer entry| none =>let content : TextBuffer := #[#[]]let buf := { ViE.Buffer.fileBufferFromTextBuffer 0 (some "preview://") content with dirty := false }let (pid, s2) := ViE.Buffer.addBuffer s1 buf
| .workspaces =>let ws := getPreviewWorkspace s1 explorerlet lines := buildWorkspacePreviewLines wslet (s2, pid) := ensurePreviewTextBuffer s1 explorer ws.name linespure (s2, pid)| .workgroups =>let wg := getPreviewWorkgroup s1 explorerlet lines := buildWorkgroupPreviewLines wglet (s2, pid) := ensurePreviewTextBuffer s1 explorer wg.name lines
match getSelectedEntry state explorer with| none => return { state with message := "Preview: no selection" }| some entry =>if entry.isDirectory thenreturn { state with message := "Preview: directory" }else
match explorer.mode with| .files =>match getSelectedEntry state explorer with| none => return { state with message := "Preview: no selection" }| some entry =>if entry.isDirectory thenreturn { state with message := "Preview: directory" }elselet ws := state.getCurrentWorkspacelet previewWinId := ws.nextWindowIdlet s1 := ViE.Window.splitWindow state falselet (s2, previewBufId) ← ensurePreviewBuffer s1 explorer entrylet s3 := s2.updateCurrentWorkspace fun ws =>{ ws with layout := ws.layout.updateView previewWinId (fun v =>{ v with bufferId := previewBufId, cursor := Point.zero, scrollRow := 0, scrollCol := 0 }) }let s4 := updateExplorerState s3 bufId (fun ex => { ex with previewWindowId := some previewWinId, previewBufferId := some previewBufId })return { s4 with message := "Preview opened" }| .workspaces =>
return { s4 with message := s!"Preview: {entry.name}" }
return { s4 with message := "Preview opened" }| .workgroups =>let ws := state.getCurrentWorkspacelet previewWinId := ws.nextWindowIdlet s1 := ViE.Window.splitWindow state falselet previewWg := getPreviewWorkgroup s1 explorerlet lines := buildWorkgroupPreviewLines previewWglet (s2, previewBufId) := ensurePreviewTextBuffer s1 explorer previewWg.name lineslet s3 := s2.updateCurrentWorkspace fun ws =>{ ws with layout := ws.layout.updateView previewWinId (fun v =>{ v with bufferId := previewBufId, cursor := Point.zero, scrollRow := 0, scrollCol := 0 }) }let s4 := updateExplorerState s3 bufId (fun ex => { ex with previewWindowId := some previewWinId, previewBufferId := some previewBufId })return { s4 with message := "Preview opened" }
IO.println "Starting Workspace Explorer Preview Test..."let bufP1 : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 1 (some "/tmp/a.txt") #[stringToLine "A"]let bufP2 : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 2 (some "/tmp/b.txt") #[stringToLine "B"]let wsP : WorkspaceState := {name := "WS-P"rootPath := nonebuffers := [bufP1, bufP2]nextBufferId := 3layout := .window 0 { initialView with bufferId := 1 }activeWindowId := 0nextWindowId := 1}let s12a := addWorkspace s12 wsPlet wgP := s12a.getCurrentWorkgrouplet idxP := if wgP.workspaces.size == 0 then 0 else wgP.workspaces.size - 1let s12b := switchWorkspace s12a idxPlet s12c ← ViE.Feature.openWorkspaceExplorer s12blet explorerOpt := s12c.explorers.find? (fun (id, _) => id == s12c.getActiveBuffer.id)match explorerOpt with| none => throw (IO.userError "Workspace explorer not registered")| some (_, explorer) =>assertEqual "Workspace explorer preview window exists" true explorer.previewWindowId.isSomelet previewWinId := explorer.previewWindowId.get!let wsPrev := s12c.getCurrentWorkspacelet previewView := wsPrev.layout.findView previewWinId |>.getD initialViewlet previewBuf := wsPrev.buffers.find? (fun b => b.id == previewView.bufferId) |>.getD initialBufferlet previewText := previewBuf.table.toStringassertEqual "Workspace preview includes a.txt" true (previewText.contains "/tmp/a.txt")assertEqual "Workspace preview includes b.txt" true (previewText.contains "/tmp/b.txt")
let explorerOpt := s2.explorers.find? (fun (id, _) => id == s2.getActiveBuffer.id)match explorerOpt with| none => throw (IO.userError "Workgroup explorer not registered")| some (_, explorer) =>assertEqual "Workgroup explorer preview window exists" true explorer.previewWindowId.isSomelet previewWinId := explorer.previewWindowId.get!let wsPrev := s2.getCurrentWorkspacelet previewView := wsPrev.layout.findView previewWinId |>.getD initialViewlet previewBuf := wsPrev.buffers.find? (fun b => b.id == previewView.bufferId) |>.getD initialBufferlet previewText := previewBuf.table.toStringassertEqual "Workgroup preview includes Project A" true (previewText.contains "Project A")assertEqual "Workgroup preview includes Project B" true (previewText.contains "Project B")
import ViE.Window.Actionsimport ViE.Stateimport ViE.Typesimport ViE.Confignamespace Test.Scrollopen ViEdef assert (msg : String) (cond : Bool) : IO Unit := doif cond thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"throw (IO.userError s!"Assertion failed: {msg}")def test : IO Unit := dolet config := ViE.defaultConfiglet state := ViE.initialState-- Set window size (e.g., 10 lines)let state := { state with windowHeight := 10, windowWidth := 80 }-- Move cursor to line 100let state := state.setCursor { row := ⟨100⟩, col := ⟨0⟩ }-- Enforce scrolllet state := ViE.Window.enforceScroll statelet (sRow, _) := state.getScrollIO.println s!"Cursor Row: 100, Scroll Row: {sRow.val}, Window Height: 10"-- View range is [sRow, sRow + 9] (10 lines)-- We expect 100 to be in [sRow, sRow + 9]-- So sRow <= 100 AND 100 < sRow + 10 => sRow > 90let visible := sRow.val <= 100 && 100 < sRow.val + 10assert "Cursor is visible" visible-- Upper boundary: cursor above scroll should pull scroll uplet state2 := { state withmode := .normal}let state2 := state2.setScroll ⟨10⟩ ⟨0⟩let state2 := state2.setCursor { row := ⟨2⟩, col := ⟨0⟩ }let state2 := ViE.Window.enforceScroll state2let (sRow2, _) := state2.getScrollassert "Scroll moves up to include cursor" (sRow2.val == 2)-- Horizontal right boundary: cursor beyond view should scroll rightlet state3 := { state with windowHeight := 5, windowWidth := 20 }let state3 := state3.setScroll ⟨0⟩ ⟨0⟩let state3 := state3.setCursor { row := ⟨0⟩, col := ⟨30⟩ }let state3 := ViE.Window.enforceScroll state3let (_, sCol3) := state3.getScrolllet colsInView := 20 -- showLineNumbers is false by defaultlet visible3 := sCol3.val <= 30 && 30 < sCol3.val + colsInViewassert "Horizontal scroll keeps cursor visible" visible3-- Horizontal left boundary: cursor before scroll should pull scroll leftlet state4 := { state with windowHeight := 5, windowWidth := 20 }let state4 := state4.setScroll ⟨0⟩ ⟨10⟩let state4 := state4.setCursor { row := ⟨0⟩, col := ⟨2⟩ }let state4 := ViE.Window.enforceScroll state4let (_, sCol4) := state4.getScrollassert "Horizontal scroll moves left to include cursor" (sCol4.val == 2)