KK6O2B6FHJHMRVSE3N54S47OY4TWV2UGI53CYLYSWAL6WSACFEBAC CUJ744WGUGBQHTO7BN7II2KB7R4N3IBXO62OG5UJAEZU2LFHHHUAC BUDL4NZH6PIACJKSIPSZNL3F7XPNLB2BVYCCVUO4C3T47IAIQCEAC 4GUZZYJSWKA44VKYZ2U3G7SQ32JHHHPBSYUWWNNWWBOEDUN7RLKAC U3IMOYALG24OXCZQNPCZJIFKFOMDDREEQY4GNZWAPKQCTZ66H6PAC R6L2TITEEB2XRNCTCGETAQ6UBYRMDKYL6TCPA3KVQTBCMPSN5ZJQC 5SFTBD4FW6GQPKRUJBAII5R2EZPDYG6CH57BIJD4IVZBE6JZB2ZQC WRBKZMYVNHRWT7TTUGTDJ3TMWZB32QYW5PCLKTTVAJ2YF6OI3LTAC GUD2J453E3UTRJZIEEUUJ3ZIGL72ETUXTO57XQIZ2Y3ZU3C2R55QC U45XPF3YKPXXRJ4MN24T2RV7GOL4FZKQSWX5P5I7WT4HC5XF4FHAC XCVPXP4UBFU25NF6VQC3MEYHHH45RL2UXM5TA6O23NA64FPCCCJQC KJLCU4X5Z3CZM4AORMWIKOPBWPVTDMLK4LYH7UXBJFUGL7AUQCSQC YILSJ2SNU6DNZVSFL4MD6QSNLQHUNSBSTGGEQCFTWFTRR62IXBWAC SEIYD7YXZ5XI4W7UVF5O2UFLA6OCZZCOLV2XB46RKJ5EN6UQC4UAC TKAAHADV62OQORAO7MYRRRHTGPMG3GKXU4Z4MFNLAO6DLTSDU3CAC A6E5SE5JPWUJJYIUS4CF7NMB5GM2SIIUXUZASSOVD467YWI5SCQQC WBKXA5RVL5DSEJZ4OBDBCTNRT3Q4VFEVQNA344CET3BHY7CY7H5AC O27AXDRWCFLPVPLPZJTRUJRJFGXXCSR623MJADCSQTUUIEF5ZARAC BVKFLEK2IK7AOIXLU3KGN7XNWP7KSJLGOPJKJIR7MU4LGN4J5EGQC CNJGJCJZ6LRHHIRSRATSE3D3Z5OQGSSTDMXCPVXSKRQLYJFME6IAC ZSBXK2GGVYHLXXMAIIEXW45COI3GSP5CLBRUSY5JNRDSHTLGGBVAC ZL7ZSOEOGP2E24UEFC5VVPN5GTL3EUL34FA7F47WWWFPHJH2RS2AC NVJSIX3GL5WPI3FQ6U7TQQE5T4EDEHSI6SDQM7TPJTGBKCPILJFAC TIAIRD7L5GWH22TOLDAGCIDCJDZ53YWK3KHQOH3EIU7RZEOEP32AC 4V7W7A6YX2J57RZUANMZKMKFHIDFNTXRMDR6M57DC6M3B4AHOK6AC B7Q32HRFDMDDMQPLBKWS42NWSXLARPAOVZXCDQG2I3J2WVTWNFNQC 25KGF2W2ORVH3Z7RIBDBRKILMM624MNHIMN5QXNMZOWQ5DOJUCZAC 32SDOYNGPOAAMEQMZLZUE3XE6D5CFSZU6M3JDRERUVPAB4TLZOOAC KFKFELOTH4NL4DMXXWJ36OK62HWNJ7JJTHWA76CYHWGTGXMRGYFQC MESNWCLGM5N5HTO35HVLZTJDJ2BE74MHBRWXISACW7L5FNAB3LNAC -- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import Test.Undoimport Test.Bufferimport Test.Layoutimport Test.Integrationimport Test.Keybindsimport Test.Modeimport Test.PieceTable.Basicimport Test.PieceTable.UndoRedoimport Test.PieceTable.Appendedimport Test.PieceTable.Stressimport Test.PieceTable.Searchimport Test.MissingEolimport Test.Scrollimport Test.Checkpointimport Test.SearchHighlightimport Test.TreeStatsimport Test.SyntaxHighlightimport Test.InfoViewimport Test.Lspimport Test.CursorReproductionimport Test.PasteReproductionimport Test.Workspaceimport Test.PreviewDataimport Test.WorkgroupExplorerimport Test.ExplorerPreviewIO.println "Running all tests..."Test.Undo.testTest.Buffer.testTest.Layout.testTest.Integration.testTest.Keybinds.testTest.Mode.testTest.PieceTable.Basic.testTest.PieceTable.UndoRedo.testTest.PieceTable.Appended.testTest.PieceTable.Stress.testTest.PieceTable.Search.testTest.MissingEol.testTest.Checkpoint.testTest.SearchHighlight.testTest.TreeStats.testTest.SyntaxHighlight.testTest.InfoView.testTest.Lsp.testTest.CursorReproduction.testTest.PasteReproduction.testTest.Workspace.testTest.PreviewData.testTest.WorkgroupExplorer.testTest.ExplorerPreview.testTest.Scroll.testIO.println "All tests finished."def main : IO Unit := dotestdef test : IO Unit := do
import Test.TreeStatsdef main (args : List String) : IO Unit :=Test.TreeStats.main args-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.Command.Explorerimport ViE.Window.Analysisimport ViE.Window.Actionsimport Test.Utilsnamespace Test.ExplorerPreviewopen Test.Utilsopen ViEdef findEntryIndex (entries : List FileEntry) (name : String) : Option Nat :=let rec loop (list : List FileEntry) (idx : Nat) : Option Nat :=match list with| [] => none| e :: rest =>if e.name == name thensome idxelseloop rest (idx + 1)loop entries 0def test : IO Unit := doIO.println "Starting Explorer Preview Test..."let path := "Test/test_paths/dir0/dir1"let s1 ← ViE.Feature.openExplorer s0 pathlet buf := s1.getActiveBufferlet ws1 := s1.getCurrentWorkspacelet baseBufId := s0.getActiveBuffer.idlet baseWinId :=(ViE.Window.getWindowIds ws1.layout).find? (fun wid =>if wid == ws1.activeWindowId thenfalseelsematch ws1.layout.findView wid with| some v => v.bufferId == baseBufId| none => false)let baseWindowStillExists :=(ViE.Window.getWindowIds ws1.layout).any (fun wid =>if wid == ws1.activeWindowId thenfalseelsematch ws1.layout.findView wid with| some v => v.bufferId == baseBufId| none => false)assert "Opening explorer keeps original buffer window intact" baseWindowStillExistsassert "File explorer opens in regular window" (!ws1.isFloatingWindow ws1.activeWindowId)let model1 := ViE.BlikuAdapter.toModel s1let explorerClustered :=match model1.workspace.floatingClusters[0]? with| some cluster =>match cluster.root, cluster.sizePolicy with| .group gid, .multiPane => gid == buf.id| _, _ => false| none => falseassert "Bliku adapter lifts explorer/preview into floating multi-pane cluster" explorerClusteredlet explorerOpt := s1.explorers.find? (fun (id, _) => id == buf.id)match explorerOpt with| none =>throw (IO.userError "Explorer buffer not registered")| some (_, explorer) =>let idx1 := findEntryIndex explorer.entries "file1.txt"let idx2 := findEntryIndex explorer.entries "file2.txt"assert "file1.txt exists in explorer entries" idx1.isSomeassert "file2.txt exists in explorer entries" idx2.isSomelet row1 : Row := ⟨2 + idx1.get!⟩let row2 : Row := ⟨2 + idx2.get!⟩let exAfterOpen := s1.explorers.find? (fun (id, _) => id == buf.id) |>.map (fun (_, ex) => ex)match exAfterOpen with| none => throw (IO.userError "Explorer buffer not registered after open")| some exOpen =>assert "Preview window created on open" exOpen.previewWindowId.isSomematch exOpen.previewWindowId with| none => falselet pairSideBySide :=match exOpen.previewWindowId with| some wid =>et == pt && eh == ph && ((el + ew <= pl) || (pl + pw <= el))| _, _ => false| none => falseassert "Explorer/preview pair is side-by-side" pairSideBySidelet bounds := ViE.Window.getAllWindowBounds s1.getCurrentWorkspace.layout(if s1.windowHeight > 0 then s1.windowHeight - 1 else 0) s1.windowWidthmatch bounds.find? (fun (id, _, _, _, _) => id == s1.getCurrentWorkspace.activeWindowId),bounds.find? (fun (id, _, _, _, _) => id == wid) with| some (_, et, el, eh, ew), some (_, pt, pl, ph, pw) =>assert "Preview window opens in regular window" previewRegular| some wid => !s1.getCurrentWorkspace.isFloatingWindow widlet s2 := s1.updateActiveView fun v => { v with cursor := { row := row1, col := 0 } }let s3 ← ViE.Feature.refreshExplorerPreview s2let exAfterOpt := s3.explorers.find? (fun (id, _) => id == buf.id)match exAfterOpt with| none =>throw (IO.userError "Explorer buffer not registered after preview")| some (_, exAfter) =>assert "Preview window created" exAfter.previewWindowId.isSomeassert "Preview buffer created" exAfter.previewBufferId.isSomelet ws := s3.getCurrentWorkspacelet previewWinId := exAfter.previewWindowId.get!let previewView := ws.layout.findView previewWinId |>.getD initialViewlet previewBuf := ws.buffers.find? (fun b => b.id == previewView.bufferId) |>.getD initialBufferassert "Preview filename has prefix" ((previewBuf.filename.getD "").startsWith "preview://")assert "Preview filename contains file1.txt" ((previewBuf.filename.getD "").contains "file1.txt")let dirIdx := findEntryIndex exAfter.entries "dir0"assert "dir0 exists in explorer entries" dirIdx.isSomelet dirRow : Row := ⟨2 + dirIdx.get!⟩let s3a := s3.updateActiveView fun v => { v with cursor := { row := dirRow, col := 0 } }let s3b ← ViE.Feature.handleExplorerEnter s3alet idsAfterDir := ViE.Window.getWindowIds s3b.getCurrentWorkspace.layoutassert "Preview window kept on directory navigation" (idsAfterDir.contains previewWinId)let newExplorerOpt := s3b.explorers.find? (fun (id, _) => id == s3b.getActiveBuffer.id)match newExplorerOpt with| none => throw (IO.userError "Explorer buffer not registered after directory nav")| some (_, exAfterDir) =>assert "Preview window id preserved on directory nav" (exAfterDir.previewWindowId == exAfter.previewWindowId)let s4 := s3.updateActiveView fun v => { v with cursor := { row := row2, col := 0 } }let s5 ← ViE.Feature.refreshExplorerPreview s4let ws5 := s5.getCurrentWorkspacelet previewView2 := ws5.layout.findView previewWinId |>.getD initialViewlet previewBuf2 := ws5.buffers.find? (fun b => b.id == previewView2.bufferId) |>.getD initialBufferassert "Preview filename updates to file2.txt" ((previewBuf2.filename.getD "").contains "file2.txt")let s5a := s5.updateActiveView fun v => { v with cursor := { row := row2, col := 0 } }let explorerWinId := s5a.getCurrentWorkspace.activeWindowIdlet s5b ← ViE.Feature.handleExplorerEnter s5alet ws5b := s5b.getCurrentWorkspacematch baseWinId with| some wid =>assert "Opening file from explorer uses original buffer window" (ws5b.activeWindowId == wid)| none =>throw (IO.userError "Base window id not found after explorer open")assert "Opening file from explorer leaves regular window" (!ws5b.isFloatingWindow ws5b.activeWindowId)let idsAfterOpen := ViE.Window.getWindowIds s5b.getCurrentWorkspace.layoutassert "Explorer window removed on file open" (!idsAfterOpen.contains explorerWinId)assert "Preview window removed on file open" (!idsAfterOpen.contains previewWinId)let bufIdsAfterOpen := s5b.getCurrentWorkspace.buffers.map (fun b => b.id)let previewBufId := exAfter.previewBufferId.getD 0assert "Preview buffer removed on file open" (!bufIdsAfterOpen.contains previewBufId)let sClose := ViE.Window.closeActiveWindow s5let idsAfter := ViE.Window.getWindowIds sClose.getCurrentWorkspace.layoutassert "Preview window removed on explorer close" (!idsAfter.contains previewWinId)let bufIdsAfterClose := sClose.getCurrentWorkspace.buffers.map (fun b => b.id)assert "Preview buffer removed on explorer close" (!bufIdsAfterClose.contains previewBufId)let s6 ← ViE.Feature.toggleExplorerPreview s5let exAfterClose := s6.explorers.find? (fun (id, _) => id == buf.id) |>.map (fun (_, ex) => ex)match exAfterClose with| none => throw (IO.userError "Explorer buffer missing after close")| some exClose =>assert "Preview window cleared" exClose.previewWindowId.isNoneIO.println "Explorer Preview Test passed!"end Test.ExplorerPreviewlet previewRegular :=let s0 := { ViE.initialState with windowHeight := 40, windowWidth := 120 }import ViE.BlikuAdapter-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.Buffer.LowIOimport ViE.State.Configimport ViE.State.Layoutimport Test.Utilsnamespace Test.PreviewDataopen Test.Utilsdef basePath : String := "Test/test_paths"def testFiles : List String := [s!"{basePath}/file0.txt",s!"{basePath}/dir0/file0.txt",s!"{basePath}/dir0/file1.txt",s!"{basePath}/dir0/file2.txt",s!"{basePath}/dir0/file3.txt",s!"{basePath}/dir0/dir0/file0.txt",s!"{basePath}/dir0/dir1/file1.txt",s!"{basePath}/dir0/dir1/file2.txt",s!"{basePath}/dir0/dir1/file3.txt",s!"{basePath}/dir0/dir1/file4.txt",s!"{basePath}/dir0/dir1/file5.txt"]def test : IO Unit := doIO.println "Starting Preview Data Test..."for path in testFiles dolet buf ← ViE.loadBufferByteArray pathlet content := buf.table.toStringlet byteLen := content.toUTF8.sizeassertEqual s!"{path} has content" true (content.length > 0)assertEqual s!"{path} has bytes" true (byteLen > 0)assertEqual s!"{path} has lines" true (buf.lineCount > 0)let firstLine := ViE.getLineFromBuffer buf 0 |>.getD ""assertEqual s!"{path} has first line" true (firstLine.length > 0)IO.println "PreviewDataTest passed!"end Test.PreviewData-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.State.Layoutimport ViE.Command.Explorerimport ViE.State.Configimport ViE.Command.Implimport Test.Utilsnamespace Test.WorkgroupExploreropen Test.Utilsopen ViEdef addWorkspace (state : EditorState) (ws : WorkspaceState) : EditorState :=state.updateCurrentWorkgroup fun wg =>{ wg with workspaces := wg.workspaces.push ws }def addWorkgroup (state : EditorState) (name : String) : EditorState :=let nextBufId := state.workgroups.foldl (fun m g =>g.workspaces.foldl (fun m2 ws => max m2 ws.nextBufferId) m) 0let newWg := createEmptyWorkgroup name nextBufId{ state with workgroups := state.workgroups.push newWg }def setCursorRow (state : EditorState) (row : Nat) : EditorState :=state.updateActiveView fun v => { v with cursor := { v.cursor with row := ⟨row⟩ } }def test : IO Unit := doIO.println "Starting Workgroup Explorer Test..."let ws1 := makeWorkspaceState "Project A" (some "/tmp/project-a") 10let ws2 := makeWorkspaceState "Project B" (some "/tmp/project-b") 20let s1 := addWorkspace (addWorkspace s0 ws1) ws2let s1 := addWorkgroup s1 "Group B"let s1 := addWorkgroup s1 "Group C"let s2 ← ViE.Command.cmdWg ["list"] s1assertEqual "Explorer buffer is active" (some "explorer://workgroups") s2.getActiveBuffer.filenamelet ws2 := s2.getCurrentWorkspaceassertEqual "Workgroup explorer opens in regular window" false (ws2.isFloatingWindow ws2.activeWindowId)let bufText := s2.getActiveBuffer.table.toStringassertEqual "Explorer contains Workgroup header" true (bufText.contains "Workgroup Explorer")assertEqual "Explorer contains New Workgroup entry" true (bufText.contains "[New Workgroup]")assertEqual "Explorer contains Rename Workgroup entry" true (bufText.contains "[Rename Workgroup]")assertEqual "Explorer contains Group 0" true (bufText.contains "0")assertEqual "Explorer contains Group B" true (bufText.contains "Group B")assertEqual "Explorer marks current workgroup" true (bufText.contains "*")let s3 := setCursorRow s2 2let s4 ← ViE.Feature.handleExplorerEnter s3let s3b := setCursorRow s2 3let s4b ← ViE.Feature.handleExplorerEnter s3blet s4c ← ViE.Command.cmdWg ["rename", ""] s2assertEqual "Rename workgroup rejects empty" "Workgroup name cannot be empty" s4c.messagelet s4d ← ViE.Command.cmdWg ["rename", "Group B"] s2assertEqual "Rename workgroup rejects duplicate" true (s4d.message.contains "already exists")let s5 ← ViE.Command.cmdWg ["list"] s1let s6 := setCursorRow s5 5let s7 ← ViE.Feature.handleExplorerEnter s6assertEqual "Switched to workgroup index 1" 1 s7.currentGroupassertEqual "Switch restores workgroup layout" true (s7.getActiveBuffer.filename != some "explorer://workgroup")IO.println "WorkgroupExplorerTest passed!"end Test.WorkgroupExplorerassertEqual "Rename workgroup opens floating input" true s4b.floatingOverlay.isSomeassertEqual "Rename workgroup floating command prefix" (some "wg rename ") s4b.floatingInputCommandassertEqual "New workgroup opens floating input" true s4.floatingOverlay.isSomeassertEqual "New workgroup floating command prefix" (some "wg new ") s4.floatingInputCommandlet 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 pairSideBySide :=et == pt && eh == ph && ((el + ew <= pl) || (pl + pw <= el))| _, _ => falseassertEqual "Workgroup explorer/preview pair is side-by-side" true pairSideBySidematch (ViE.Window.getAllWindowBounds wsPrev.layout (if s2.windowHeight > 0 then s2.windowHeight - 1 else 0) s2.windowWidth).find? (fun (id, _, _, _, _) => id == wsPrev.activeWindowId),(ViE.Window.getAllWindowBounds wsPrev.layout (if s2.windowHeight > 0 then s2.windowHeight - 1 else 0) s2.windowWidth).find? (fun (id, _, _, _, _) => id == previewWinId) with| some (_, et, el, eh, ew), some (_, pt, pl, ph, pw) =>assertEqual "Workgroup explorer preview window is regular" false (wsPrev.isFloatingWindow previewWinId)let 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")let s0 := { ViE.initialState with windowHeight := 40, windowWidth := 120 }-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.State.Layoutimport ViE.Workspaceimport ViE.Command.Implimport ViE.Appimport ViE.Buffer.Contentimport ViE.Basicimport Test.Utilsnamespace Test.Workspaceopen Test.Utilsopen ViEdef addWorkspace (state : EditorState) (ws : WorkspaceState) : EditorState :=state.updateCurrentWorkgroup fun wg =>{ wg with workspaces := wg.workspaces.push ws }def switchWorkspace (state : EditorState) (idx : Nat) : EditorState :=state.updateCurrentWorkgroup fun wg =>if idx < wg.workspaces.size then{ wg with currentWorkspace := idx }elsewgdef test : IO Unit := doIO.println "Starting Workspace Test..."let wg0 := s0.getCurrentWorkgroupassertEqual "Initial workgroups size" 10 s0.workgroups.sizeassertEqual "Initial workgroup count" 1 wg0.workspaces.sizeassertEqual "Initial workspace index" 0 wg0.currentWorkspaceassertEqual "Initial workspace name" "ViE" s0.getCurrentWorkspace.namelet ws1 := makeWorkspaceState "Project A" (some "/tmp/project-a") 10let s1 := addWorkspace s0 ws1let wg1 := s1.getCurrentWorkgroupassertEqual "Workgroup has 2 workspaces" 2 wg1.workspaces.sizelet s2 := switchWorkspace s1 1assertEqual "Switched to workspace 1" "Project A" s2.getCurrentWorkspace.nameassertEqual "Active buffer comes from workspace 1" 10 (s2.getActiveBuffer.id)let resolved := s2.getCurrentWorkspace.resolvePath "file.txt"assertEqual "ResolvePath uses workspace root" "/tmp/project-a/file.txt" resolvedlet resolvedAbs := s2.getCurrentWorkspace.resolvePath "/abs/file.txt"assertEqual "ResolvePath keeps absolute path" "/abs/file.txt" resolvedAbsIO.println "Starting Workspace Command Test..."let s3 ← ViE.Command.cmdWs ["new", "Project B", "/tmp/project-b"] s2assertEqual "Workspace created and switched" "Project B" s3.getCurrentWorkspace.nameassertEqual "Workspace rootPath set" (some "/tmp/project-b") s3.getCurrentWorkspace.rootPathassertEqual "Active buffer id is new workspace buffer" 11 s3.getActiveBuffer.idlet s4 ← ViE.Command.cmdWs ["rename", "Project B2"] s3assertEqual "Workspace rename applied" "Project B2" s4.getCurrentWorkspace.namelet s5 ← ViE.Command.cmdWs ["new", "Project C"] s4assertEqual "Workspace C created" "Project C" s5.getCurrentWorkspace.namelet s6 ← ViE.Command.cmdWs ["prev"] s5assertEqual "Workspace prev switches back" "Project B2" s6.getCurrentWorkspace.namelet s7 ← ViE.Command.cmdWs ["0"] s6assertEqual "Workspace switch by index" "ViE" s7.getCurrentWorkspace.namelet s8 ← ViE.Command.cmdWs ["list"] s7assertEqual "Workspace list opens explorer buffer" (some "explorer://workgroup") s8.getActiveBuffer.filenamelet ws8 := s8.getCurrentWorkspaceassertEqual "Workspace explorer opens in regular window" false (ws8.isFloatingWindow ws8.activeWindowId)let wsListText := s8.getActiveBuffer.table.toStringassertEqual "Workspace list contains New Workspace entry" true (wsListText.contains "[New Workspace]")assertEqual "Workspace list contains Rename Workspace entry" true (wsListText.contains "[Rename Workspace]")assertEqual "Workspace list contains Project B2" true (wsListText.contains "Project B2")assertEqual "Workspace list marks current workspace" true (wsListText.contains "*")let s8a := s8.updateActiveView fun v => { v with cursor := {row := ⟨2⟩, col := 0} }let s8b ← ViE.Feature.handleExplorerEnter s8alet s8c := s8.updateActiveView fun v => { v with cursor := {row := ⟨3⟩, col := 0} }let s8d ← ViE.Feature.handleExplorerEnter s8clet s8e ← ViE.Command.cmdWorkspace ["rename", ""] s8assertEqual "Rename workspace rejects empty" "Workspace name cannot be empty" s8e.messagelet s8f ← ViE.Command.cmdWorkspace ["rename", "Project B2"] s8assertEqual "Rename workspace rejects duplicate" true (s8f.message.contains "already exists")let s9 ← ViE.Command.cmdWs ["open", "/tmp/project-d"] s8assertEqual "Workspace open uses path name" "project-d" s9.getCurrentWorkspace.nameassertEqual "Workspace open rootPath set" (some "/tmp/project-d") s9.getCurrentWorkspace.rootPathlet s10 ← ViE.Command.cmdWs ["open", "--name", "Project E", "/tmp/project-e"] s9assertEqual "Workspace open --name prefix" "Project E" s10.getCurrentWorkspace.nameassertEqual "Workspace open --name prefix rootPath" (some "/tmp/project-e") s10.getCurrentWorkspace.rootPathlet s11 ← ViE.Command.cmdWs ["open", "/tmp/project-f", "--name", "Project F"] s10assertEqual "Workspace open --name suffix" "Project F" s11.getCurrentWorkspace.nameassertEqual "Workspace open --name suffix rootPath" (some "/tmp/project-f") s11.getCurrentWorkspace.rootPathlet s12 ← ViE.Command.cmdWs ["close"] s11assertEqual "Workspace close switches to previous" "Project E" s12.getCurrentWorkspace.nameIO.println "Starting Workspace Restore Test..."let bufA : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 0 none #[stringToLine "WS-A"]let bufB : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 10 none #[stringToLine "WS-B"]let wsA : WorkspaceState := {name := "WS-A"rootPath := nonebuffers := [bufA]nextBufferId := 1layout := .window 0 { initialView with bufferId := 0 }activeWindowId := 0nextWindowId := 1}let wsB : WorkspaceState := {name := "WS-B"rootPath := nonebuffers := [bufB]nextBufferId := 11layout := .window 0 { initialView with bufferId := 10 }activeWindowId := 0nextWindowId := 1}let s12a := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsB], currentWorkspace := 0 }let s12b ← ViE.Feature.openWorkspaceExplorer s12alet s12c := s12b.updateActiveView fun v => { v with cursor := {row := ⟨5⟩, col := 0} }let s12d ← ViE.Feature.handleExplorerEnter s12cassertEqual "Workspace selection switches current workspace" "WS-B" s12d.getCurrentWorkspace.nameassertEqual "Workspace selection restores active buffer" "WS-B" s12d.getActiveBuffer.table.toStringIO.println "Starting Workspace Restore Layout Test..."let bufC : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 20 none #[stringToLine "WS-C1"]let bufD : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 21 none #[stringToLine "WS-C2"]let layoutC : Layout :=.hsplit(.window 0 { initialView with bufferId := 20 })(.window 1 { initialView with bufferId := 21, cursor := ⟨⟨0⟩, ⟨2⟩⟩ })0.3let wsC : WorkspaceState := {name := "WS-C"rootPath := nonebuffers := [bufC, bufD]nextBufferId := 22layout := layoutCactiveWindowId := 1nextWindowId := 2}let s12e := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsC], currentWorkspace := 0 }let s12f ← ViE.Feature.openWorkspaceExplorer s12elet s12g := s12f.updateActiveView fun v => { v with cursor := {row := ⟨5⟩, col := 0} }let s12h ← ViE.Feature.handleExplorerEnter s12gassertEqual "Workspace layout restore (active buffer)" "WS-C2" s12h.getActiveBuffer.table.toStringlet ratio := match s12h.getCurrentWorkspace.layout with| .hsplit _ _ r => r| _ => 0.0assertEqual "Workspace layout restore (ratio)" 0.3 ratiolet cursor := s12h.getCursorassertEqual "Workspace layout restore (cursor row)" 0 cursor.row.valassertEqual "Workspace layout restore (cursor col)" 2 cursor.col.valIO.println "Starting Workspace Restore VSplit Test..."let bufE : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 30 none #[stringToLine "WS-D1"]let bufF : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 31 none #[stringToLine "WS-D2"]let layoutD : Layout :=.vsplit(.window 0 { initialView with bufferId := 30, cursor := ⟨⟨1⟩, ⟨0⟩⟩, scrollRow := ⟨1⟩, scrollCol := ⟨2⟩ })(.window 1 { initialView with bufferId := 31, cursor := ⟨⟨2⟩, ⟨3⟩⟩, scrollRow := ⟨0⟩, scrollCol := ⟨1⟩ })0.7let wsD : WorkspaceState := {name := "WS-D"rootPath := nonebuffers := [bufE, bufF]nextBufferId := 32layout := layoutDactiveWindowId := 1nextWindowId := 2}let s12i := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsC, wsD], currentWorkspace := 0 }let s12j ← ViE.Feature.openWorkspaceExplorer s12ilet s12k := s12j.updateActiveView fun v => { v with cursor := {row := ⟨6⟩, col := 0} }let s12l ← ViE.Feature.handleExplorerEnter s12kassertEqual "Workspace vsplit restore (active buffer)" "WS-D2" s12l.getActiveBuffer.table.toStringlet ratio2 := match s12l.getCurrentWorkspace.layout with| .vsplit _ _ r => r| _ => 0.0assertEqual "Workspace vsplit restore (ratio)" 0.7 ratio2let cursor2 := s12l.getCursorassertEqual "Workspace vsplit restore (cursor row)" 2 cursor2.row.valassertEqual "Workspace vsplit restore (cursor col)" 3 cursor2.col.vallet (sRow, sCol) := s12l.getScrollassertEqual "Workspace vsplit restore (scroll row)" 0 sRow.valassertEqual "Workspace vsplit restore (scroll col)" 1 sCol.valIO.println "Starting Workspace Multi-View Restore Test..."let bufG : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 40 none #[stringToLine "WS-E1"]let bufH : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 41 none #[stringToLine "WS-E2"]let bufI : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 42 none #[stringToLine "WS-E3"]let layoutE : Layout :=.hsplit(.vsplit(.window 0 { initialView with bufferId := 40, cursor := ⟨⟨0⟩, ⟨1⟩⟩, scrollRow := ⟨0⟩, scrollCol := ⟨0⟩ })(.window 1 { initialView with bufferId := 41, cursor := ⟨⟨2⟩, ⟨2⟩⟩, scrollRow := ⟨1⟩, scrollCol := ⟨1⟩ })0.4)(.window 2 { initialView with bufferId := 42, cursor := ⟨⟨3⟩, ⟨0⟩⟩, scrollRow := ⟨2⟩, scrollCol := ⟨2⟩ })0.6let wsE : WorkspaceState := {name := "WS-E"rootPath := nonebuffers := [bufG, bufH, bufI]nextBufferId := 43layout := layoutEactiveWindowId := 1nextWindowId := 3}let s12m := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsC, wsD, wsE], currentWorkspace := 0 }let s12n ← ViE.Feature.openWorkspaceExplorer s12mlet s12o := s12n.updateActiveView fun v => { v with cursor := {row := ⟨7⟩, col := 0} }let s12p ← ViE.Feature.handleExplorerEnter s12oassertEqual "Multi-view restore (active buffer)" "WS-E2" s12p.getActiveBuffer.table.toStringlet (sr2, sc2) := s12p.getScrollassertEqual "Multi-view restore (scroll row)" 1 sr2.valassertEqual "Multi-view restore (scroll col)" 1 sc2.vallet cursor3 := s12p.getCursorassertEqual "Multi-view restore (cursor row)" 2 cursor3.row.valassertEqual "Multi-view restore (cursor col)" 2 cursor3.col.valassertEqual "Multi-view restore (layout hsplit ratio)" 0.6 (match s12p.getCurrentWorkspace.layout with | .hsplit _ _ r => r | _ => 0.0)let subRatio := match s12p.getCurrentWorkspace.layout with| .hsplit left _ _ =>match left with| .vsplit _ _ r => r| _ => 0.0| _ => 0.0assertEqual "Multi-view restore (layout vsplit ratio)" 0.4 subRatioIO.println "Starting Workspace Startup Target Test..."let base := "Test/test_paths"let dirOnly := s!"{base}/dir0"let (wsDirOnly, fileDirOnly) ← ViE.resolveStartupTarget (some dirOnly)assertEqual "Directory arg has no file" none fileDirOnlylet fileNested := s!"{base}/dir0/dir1/dir2/file0.txt"let (wsFileNested, fileFileNested) ← ViE.resolveStartupTarget (some fileNested)let fileTop := s!"{base}/file0.txt"let (wsFileTop, fileFileTop) ← ViE.resolveStartupTarget (some fileTop)IO.println "Starting Explorer Path Resolution Test..."let s13 := s12.updateCurrentWorkspace fun ws =>IO.println "WorkspaceTest passed!"end Test.Workspace{ ws with rootPath := some absTest, name := "Test" }let s14 ← ViE.Feature.openExplorer s13 "."assertEqual "Explorer opens absolute workspace root from dot" (some s!"explorer://{absTest}") s14.getActiveBuffer.filenamelet s15 ← ViE.Feature.openExplorer s13 "test_paths"assertEqual "Explorer resolves relative path from absolute workspace root" (some s!"explorer://{absTest}/test_paths") s15.getActiveBuffer.filenamelet absTest ← ViE.resolveAbsolutePath none "Test"assertEqual "Top-level file sets workspace to base" (some absBase) wsFileTopassertEqual "Top-level file keeps filename" (some absFileTop) fileFileToplet newNested := s!"{base}/newdir/newfile.txt"let absNewNested ← ViE.resolveAbsolutePath none newNestedlet newNestedParent := match (System.FilePath.mk absNewNested).parent with| some p => p.toString| none => "/"let (wsNewNested, fileNewNested) ← ViE.resolveStartupTarget (some newNested)assertEqual "Nonexistent file sets workspace to parent dir" (some newNestedParent) wsNewNestedassertEqual "Nonexistent file keeps absolute filename" (some absNewNested) fileNewNestedlet absFileTop ← ViE.resolveAbsolutePath none fileTopassertEqual "File arg sets workspace to parent dir" (some nestedParent) wsFileNestedassertEqual "File arg keeps filename" (some absFileNested) fileFileNestedlet absFileNested ← ViE.resolveAbsolutePath none fileNestedlet nestedParent := match (System.FilePath.mk absFileNested).parent with| some p => p.toString| none => "/"assertEqual "Directory arg sets workspace" (some absDirOnly) wsDirOnlylet absDirOnly ← ViE.resolveAbsolutePath none dirOnlylet absBase ← ViE.resolveAbsolutePath none baseIO.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 pairSideBySide :=et == pt && eh == ph && ((el + ew <= pl) || (pl + pw <= el))| _, _ => falseassertEqual "Workspace explorer/preview pair is side-by-side" true pairSideBySidematch (ViE.Window.getAllWindowBounds wsPrev.layout (if s12c.windowHeight > 0 then s12c.windowHeight - 1 else 0) s12c.windowWidth).find? (fun (id, _, _, _, _) => id == wsPrev.activeWindowId),(ViE.Window.getAllWindowBounds wsPrev.layout (if s12c.windowHeight > 0 then s12c.windowHeight - 1 else 0) s12c.windowWidth).find? (fun (id, _, _, _, _) => id == previewWinId) with| some (_, et, el, eh, ew), some (_, pt, pl, ph, pw) =>assertEqual "Workspace explorer preview window is regular" false (wsPrev.isFloatingWindow previewWinId)let 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")IO.println "Starting Workspace Relative Path Resolution Test..."let stamp ← IO.monoMsNowlet relBase := s!"/tmp/vie-ws-rel-{stamp}"IO.FS.createDirAll (System.FilePath.mk relBase)let sRel0 := s11.updateCurrentWorkspace fun ws =>{ ws with rootPath := some relBase, name := "RelBase" }let sRel1 ← ViE.Command.cmdCd ["child"] sRel0assertEqual "cd relative resolves from workspace root" (some s!"{relBase}/child") sRel1.getCurrentWorkspace.rootPathlet sRel2 ← ViE.Command.cmdWs ["open", "nested"] sRel0assertEqual "ws open relative resolves from workspace root" (some s!"{relBase}/nested") sRel2.getCurrentWorkspace.rootPathassertEqual "ws open relative uses basename from absolute path" "nested" sRel2.getCurrentWorkspace.namelet sRel3 ← ViE.Command.cmdWs ["new", "RelChild", "child2"] sRel0assertEqual "ws new relative resolves from workspace root" (some s!"{relBase}/child2") sRel3.getCurrentWorkspace.rootPathassertEqual "Rename workspace opens floating input" true s8d.floatingOverlay.isSomeassertEqual "Rename workspace floating command prefix" (some "ws rename ") s8d.floatingInputCommandassertEqual "New workspace opens floating input" true s8b.floatingOverlay.isSomeassertEqual "New workspace floating command prefix" (some "ws new ") s8b.floatingInputCommandlet s0 := { ViE.initialState with windowHeight := 40, windowWidth := 120 }set_option maxRecDepth 2048-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
import Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sample
#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
import Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sample
#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}
module Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show values
let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))
import Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sample
#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}
module Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show values
let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
import ViEimport Test.Utilsnamespace Test.InfoViewopen Test.Utilsdef makeTestConfig : ViE.Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}def test : IO Unit := doIO.println "Starting InfoView Test..."let s0 : ViE.EditorState :=({ ViE.initialState with message := "", floatingOverlay := none, mode := .normal, infoViewRequested := false }).updateActiveBuffer fun b =>{ b with filename := some "Main.lean" }let s0' ← ViE.Lsp.Lean.syncInfoViewWindow s0let ws0 := s0'.getCurrentWorkspaceassertEqual "InfoView hidden when not requested" 1 (ViE.Window.getWindowIds ws0.layout |>.length)let sNonLean := ({ s0 with infoViewRequested := true }).updateActiveBuffer fun b =>{ b with filename := some "note.md" }let sNonLean' ← ViE.Lsp.Lean.syncInfoViewWindow sNonLeanlet wsNonLean := sNonLean'.getCurrentWorkspaceassertEqual "InfoView hidden for non-Lean buffer" 1 (ViE.Window.getWindowIds wsNonLean.layout |>.length)let sLean := ({ s0 with infoViewRequested := true }).updateActiveBuffer fun b =>{ b with table := ViE.PieceTable.fromString "abc\n" }let sLean' ← ViE.Lsp.Lean.syncInfoViewWindow sLeanlet wsLean := sLean'.getCurrentWorkspaceassertEqual "InfoView split creates second window" 2 (ViE.Window.getWindowIds wsLean.layout |>.length)let infoBufOpt := wsLean.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)assertEqual "InfoView buffer exists" true infoBufOpt.isSomematch infoBufOpt with| none =>assertEqual "InfoView content check skipped unexpectedly" true false| some infoBuf =>let line0 := ViE.getLineFromBuffer infoBuf 0 |>.getD ""let line1 := ViE.getLineFromBuffer infoBuf 1 |>.getD ""assert "InfoView includes file line" (line0.startsWith "File: Main.lean")assertEqual "InfoView position line" "Position: 1:1 Byte: 0" line1let cfg := makeTestConfiglet sMoved ← ViE.update cfg sLean' (ViE.Key.char 'l')let wsMoved := sMoved.getCurrentWorkspacelet movedInfoBufOpt := wsMoved.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)assertEqual "InfoView buffer exists after cursor move" true movedInfoBufOpt.isSomematch movedInfoBufOpt with| none =>assertEqual "InfoView cursor move check skipped unexpectedly" true false| some infoBuf =>let line1 := ViE.getLineFromBuffer infoBuf 1 |>.getD ""assertEqual "InfoView position updates on cursor move" "Position: 1:2 Byte: 1" line1let sOff := { sLean' with infoViewRequested := false }let sOff' ← ViE.Lsp.Lean.syncInfoViewWindow sOfflet wsOff := sOff'.getCurrentWorkspaceassertEqual "InfoView off closes split window" 1 (ViE.Window.getWindowIds wsOff.layout |>.length)assertEqual "InfoView off removes buffer" true ((wsOff.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)).isNone)IO.println "InfoView Test passed!"end Test.InfoViewlet sInfoOnly := ViE.Window.closeActiveWindow sLean'let sInfoOnly' ← ViE.Lsp.Lean.syncInfoViewWindow sInfoOnlylet wsInfoOnly := sInfoOnly'.getCurrentWorkspaceassertEqual "InfoView-only fallback requests quit" true sInfoOnly'.shouldQuitassertEqual "InfoView-only fallback removes InfoView buffer" true ((wsInfoOnly.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)).isNone)-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Movementimport ViE.State.Editimport ViE.Key.Mapimport ViE.Typesimport ViE.Buffer.Contentimport ViE.Configimport ViE.Command.Implnamespace Test.Keybindsopen ViEdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}def keys (s : String) : List Key :=s.toList.map fun c =>if c == '\n' then Key.enter else Key.char cdef runKeys (s : EditorState) (ks : List Key) : IO EditorState := dolet config := makeTestConfigks.foldlM (fun s k => ViE.update config s k) sdef findEntryIndex (entries : List FileEntry) (name : String) : Option Nat :=let rec loop (rest : List FileEntry) (idx : Nat) : Option Nat :=match rest with| [] => none| e :: tail =>if e.name == name thensome idxelseloop tail (idx + 1)loop entries 0def findEntryIndexByPath (entries : List FileEntry) (path : String) : Option Nat :=let rec loop (rest : List FileEntry) (idx : Nat) : Option Nat :=match rest with| [] => none| e :: tail =>if e.path == path thensome idxelseloop tail (idx + 1)loop entries 0def testMotions : IO Unit := doIO.println " Testing Motions..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "line1\nline2\nline3" ++ [Key.esc] ++ keys "gg0")-- "line1\nline2\nline3" cursor at (0,0)-- l, hlet s_l ← runKeys s1 [Key.char 'l']let s_lh ← runKeys s_l [Key.char 'h']assertCursor "h moves left" s_lh 0 0-- j, klet s_j ← runKeys s1 [Key.char 'j']assertCursor "j moves down" s_j 1 0let s_jk ← runKeys s_j [Key.char 'k']assertCursor "k moves up" s_jk 0 0-- 0, $let s_end ← runKeys s1 [Key.char '$']assertCursor "$ moves to end" s_end 0 4 -- '1' is at col 4let s_start ← runKeys s_end [Key.char '0']assertCursor "0 moves to start" s_start 0 0-- w, b, elet s_text ← runKeys s0 ([Key.char 'i'] ++ keys "word1 word2" ++ [Key.esc] ++ [Key.char '0'])let s_w ← runKeys s_text [Key.char 'w']assertCursor "w moves to next word" s_w 0 6let s_e ← runKeys s_text [Key.char 'e']assertCursor "e moves to end of word" s_e 0 4let s_we ← runKeys s_w [Key.char 'e']assertCursor "we moves to end of next word" s_we 0 10let s_web ← runKeys s_we [Key.char 'b']assertCursor "b moves to start of word" s_web 0 6-- gg, Glet s_G ← runKeys s1 [Key.char 'G']assertCursor "G moves to last line" s_G 2 0let s_gg ← runKeys s_G [Key.char 'g', Key.char 'g']assertCursor "gg moves to first line" s_gg 0 0-- | (jump to column)let s_pipe ← runKeys s1 [Key.char '3', Key.char '|']assertCursor "| jumps to column" s_pipe 0 2 -- 1-indexed count 3 -> col 2def testEditing : IO Unit := doIO.println " Testing Editing..."let s0 := ViE.initialState-- i, a, Alet s_i ← runKeys s0 [Key.char 'i', Key.char 'x', Key.esc]assertBuffer "i inserts" s_i "x"let s_a ← runKeys s_i [Key.char 'a', Key.char 'y', Key.esc]assertBuffer "a appends" s_a "xy"let s_A ← runKeys s_a [Key.char '0', Key.char 'A', Key.char 'z', Key.esc]assertBuffer "A appends at end" s_A "xyz"let s_tab_insert ← runKeys s0 [Key.char 'i', Key.char '\t']assertCursor "Tab advances cursor to next tab stop in insert mode" s_tab_insert 0 4let s_tab_backspace ← runKeys s0 [Key.char 'i', Key.char '\t', Key.backspace]assertBuffer "Tab backspace deletes tab in insert mode" s_tab_backspace ""assertCursor "Tab backspace restores cursor column" s_tab_backspace 0 0let s_ctrlV ← runKeys s0 [Key.ctrl 'v']assertEqual "Ctrl-v enters visual block mode" Mode.visualBlock s_ctrlV.modelet s_q ← runKeys s0 [Key.char 'q']assertEqual "q does not enter command mode" Mode.normal s_q.modelet s_V ← runKeys s0 [Key.char 'V']assertEqual "V enters visual line mode" Mode.visualLine s_V.modelet s_tab ← runKeys s0 [Key.char 'i', Key.char '\t', Key.esc]assertBuffer "Tab inserts in insert mode" s_tab "\t"assertCursor "Esc after tab returns to tab char in normal mode" s_tab 0 0let s0_tab8 := { s0 with config := { s0.config with tabStop := 8 } }let s_tab8 ← runKeys s0_tab8 [Key.char 'i', Key.char '\t']assertCursor "Tab follows custom tabStop in insert mode" s_tab8 0 8-- o, Olet s_o ← runKeys s_i [Key.char 'o', Key.char 'y', Key.esc]assertBuffer "o inserts line below" s_o "x\ny"let s_O ← runKeys s_o [Key.char 'k', Key.char 'O', Key.char 'z', Key.esc]assertBuffer "O inserts line above" s_O "z\nx\ny"-- xlet s_x ← runKeys s_i [Key.char 'x']assertBuffer "x deletes char" s_x ""match s_x.clipboard with| some reg =>assertEqual "x register kind" RegisterKind.charwise reg.kindassertEqual "x register text" "x" reg.text| none => assertEqual "x register set" true falsedef testOperators : IO Unit := doIO.println " Testing Operators..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "hello world" ++ [Key.esc] ++ [Key.char '0'])-- dwlet s_dw ← runKeys s1 [Key.char 'd', Key.char 'w']assertBuffer "dw deletes word" s_dw "world"match s_dw.clipboard with| some reg =>assertEqual "dw register kind" RegisterKind.charwise reg.kindassertEqual "dw register text" "hello " reg.text| none => assertEqual "dw register set" true false-- cwlet s_cw ← runKeys s1 [Key.char 'c', Key.char 'w', Key.char 'h', Key.char 'i', Key.esc]assertBuffer "cw changes word" s_cw "hi world"-- ddlet s_lines ← runKeys s0 ([Key.char 'i'] ++ keys "line1\nline2\nline3" ++ [Key.esc] ++ keys "ggj")let s_dd ← runKeys s_lines [Key.char 'd', Key.char 'd']assertBuffer "dd deletes current line" s_dd "line1\nline3"match s_dd.clipboard with| some reg =>assertEqual "dd register kind" RegisterKind.linewise reg.kindassertEqual "dd register text" "line2\n" reg.text| none => assertEqual "dd register set" true false-- yy, p, Plet s_yy ← runKeys s_lines [Key.char 'g', Key.char 'g', Key.char 'y', Key.char 'y']let s_p ← runKeys s_yy [Key.char 'G', Key.char 'p']assertBuffer "p pastes below" s_p "line1\nline2\nline3\nline1\n"let s_P ← runKeys s_yy [Key.char 'g', Key.char 'g', Key.char 'P']assertBuffer "P pastes above" s_P "line1\nline1\nline2\nline3"let s_indent ← runKeys s0 ([Key.char 'i'] ++ keys " indented\nx" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char 'y', Key.char 'y', Key.char 'j', Key.char 'p'])assertCursor "linewise paste moves to first non-blank" s_indent 2 2-- paste should not overwrite registerlet s_reg0 ← runKeys s0 ([Key.char 'i'] ++ keys "one\ntwo" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char 'y', Key.char 'y'])let regBefore := s_reg0.clipboardlet s_regP ← runKeys s_reg0 [Key.char 'j', Key.char 'p']match (regBefore, s_regP.clipboard) with| (some before, some after) =>assertEqual "paste keeps register kind" before.kind after.kindassertEqual "paste keeps register text" before.text after.text| _ => assertEqual "paste keeps register" true false-- charwise y/p from normal modelet s_char0 ← runKeys s0 ([Key.char 'i'] ++ keys "hello" ++ [Key.esc] ++ [Key.char '0'])let s_charY ← runKeys s_char0 [Key.char 'v', Key.char 'l', Key.char 'y']let s_charP ← runKeys s_charY [Key.char '$', Key.char 'p']assertBuffer "charwise paste in normal mode" s_charP "hellohe"assertCursor "charwise paste cursor at end" s_charP 0 6let s_charP2 ← runKeys s_charY [Key.char '0', Key.char 'P']assertBuffer "charwise P pastes before cursor" s_charP2 "hehello"assertCursor "charwise P cursor at end" s_charP2 0 1def testVisual : IO Unit := doIO.println " Testing Visual Mode..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "highlight me" ++ [Key.esc] ++ [Key.char '0'])-- v, dlet s_v ← runKeys s1 [Key.char 'v', Key.char 'l', Key.char 'd']assertBuffer "visual d deletes selection" s_v "ghlight me"match s_v.clipboard with| some reg =>assertEqual "visual d register kind" RegisterKind.charwise reg.kindassertEqual "visual d register text" "hi" reg.text| none => assertEqual "visual d register set" true false-- v, y, plet s_vy ← runKeys s1 [Key.char 'v', Key.char 'e', Key.char 'y']let s_vyp ← runKeys s_vy [Key.char '$', Key.char 'p']assertBuffer "visual y yanks selection" s_vyp "highlight mehighlight"let s_char0 ← runKeys s0 ([Key.char 'i'] ++ keys "abc" ++ [Key.esc] ++ [Key.char '0'])let s_charY ← runKeys s_char0 [Key.char 'l', Key.char 'v', Key.char 'y']let s_charP ← runKeys s_charY [Key.char '$', Key.char 'p']assertBuffer "charwise paste appends" s_charP "abcb"assertCursor "charwise paste cursor at end" s_charP 0 3let s_charP2 ← runKeys s_charY [Key.char '0', Key.char 'P']assertBuffer "charwise P inserts before" s_charP2 "babc"assertCursor "charwise P cursor at end" s_charP2 0 0-- visual block yank/paste cursor positionlet s_blk0 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\nefgh" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char '0'])let s_blkP ← runKeys s_blkY [Key.char 'g', Key.char 'g', Key.char '0', Key.char 'p']assertBuffer "visual block y/p inserts block" s_blkP "abcbcd\nefgfgh"assertCursor "visual block paste cursor at block start" s_blkP 0 1let s_blkP2 ← runKeys s_blkY [Key.char 'g', Key.char 'g', Key.char '0', Key.char 'P']assertBuffer "visual block P inserts block" s_blkP2 "bcabcd\nfgefgh"assertCursor "visual block P cursor at block start" s_blkP2 0 0let s_blkD0 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\nefgh" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char '0'])assertBuffer "visual block d deletes block" s_blkD "ad\neh"match s_blkD.clipboard with| some reg =>assertEqual "visual block d register kind" RegisterKind.blockwise reg.kindassertEqual "visual block d register text" "bc\nfg" reg.text| none => assertEqual "visual block d register set" true false-- linewise yank/paste (via yy + P)let s_line0 ← runKeys s0 ([Key.char 'i'] ++ keys "aa\nbb\ncc" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g'])let s_lineY ← runKeys s_line0 [Key.char 'y', Key.char 'y']let s_lineP ← runKeys s_lineY [Key.char 'j', Key.char 'P']assertBuffer "linewise paste above keeps lines" s_lineP "aa\naa\nbb\ncc"let s_lineP2 ← runKeys s_lineY [Key.char 'j', Key.char 'p']assertBuffer "linewise paste below keeps lines" s_lineP2 "aa\nbb\naa\ncc"def testCounted : IO Unit := doIO.println " Testing Counted Actions..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "a b c d e" ++ [Key.esc] ++ [Key.char '0'])-- 3wlet s_3w ← runKeys s1 [Key.char '3', Key.char 'w']assertCursor "3w moves 3 words" s_3w 0 6-- 2jlet s_lines ← runKeys s0 ([Key.char 'i'] ++ keys "1\n2\n3\n4" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g'])let s_2j ← runKeys s_lines [Key.char '2', Key.char 'j']assertCursor "2j moves 2 lines down" s_2j 2 0def testVimCompatMotions : IO Unit := doIO.println " Testing Vim Compatibility Motions..."let s0 := { ViE.initialState with windowHeight := 12, windowWidth := 80 }let lines := String.intercalate "\n" ((List.range 30).map (fun i => s!" line{i}")) ++ "\n"let s1 ← runKeys s0 ([Key.char 'i'] ++ keys lines ++ [Key.esc] ++ keys "gg0")let sCd ← runKeys s1 [Key.ctrl 'd']assertCursor "Ctrl-d scrolls half page down" sCd 5 0let sCu ← runKeys sCd [Key.ctrl 'u']assertCursor "Ctrl-u scrolls half page up" sCu 0 0let sCf ← runKeys s1 [Key.ctrl 'f']assertCursor "Ctrl-f scrolls one page down" sCf 10 0let sCb ← runKeys sCf [Key.ctrl 'b']assertCursor "Ctrl-b scrolls one page up" sCb 0 0let sCe ← runKeys s1 [Key.ctrl 'e']let (ceRow, _) := sCe.getScrollassertEqual "Ctrl-e scrolls window down by one line" 1 ceRow.vallet sCy ← runKeys sCe [Key.ctrl 'y']let (cyRow, _) := sCy.getScrollassertEqual "Ctrl-y scrolls window up by one line" 0 cyRow.vallet sH ← runKeys sCf [Key.char 'H']assertCursor "H moves to top line of screen" sH 10 2let sM ← runKeys sCf [Key.char 'M']assertCursor "M moves to middle line of screen" sM 15 2let sL ← runKeys sCf [Key.char 'L']assertCursor "L moves to bottom line of screen" sL 19 2let sF0 ← runKeys s0 ([Key.char 'i'] ++ keys "abcaXcaYca" ++ [Key.esc] ++ [Key.char '0'])let sF1 ← runKeys sF0 [Key.char 'f', Key.char 'a']assertCursor "fa finds next character" sF1 0 3let sF2 ← runKeys sF1 [Key.char ';']assertCursor "; repeats last f motion" sF2 0 6let sF3 ← runKeys sF2 [Key.char ',']assertCursor ", reverses last f motion" sF3 0 3let sT1 ← runKeys sF3 [Key.char 't', Key.char 'a']assertCursor "ta moves before target" sT1 0 5let sT2 ← runKeys sT1 [Key.char ';']assertCursor "; repeats last t motion" sT2 0 8def testWorkgroupSwitch : IO Unit := doIO.println " Testing Workgroup Switching..."let s0 := ViE.initialStatelet s1 ← runKeys s0 [Key.alt '3']assertEqual "Alt-3 switches workgroup" 3 s1.currentGrouplet s2 ← runKeys s1 [Key.alt '0']assertEqual "Alt-0 switches workgroup" 0 s2.currentGroupdef testCommandGlobal : IO Unit := doIO.println " Testing Command Global..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "a\nfoo1\nb\nfoo2\n" ++ [Key.esc] ++ keys "gg0")let s2 ← runKeys s1 ([Key.char ':'] ++ keys "g/foo/ d" ++ [Key.enter])assertBuffer ":g/pat/ d deletes matching lines" s2 "a\nb\n"let s3 := ViE.initialStatelet s4 ← runKeys s3 ([Key.char 'i'] ++ keys "foo foo\nbar foo\nfoo bar\n" ++ [Key.esc] ++ keys "gg0")let s5 ← runKeys s4 ([Key.char ':'] ++ keys "g/foo/ s/foo/xxx/" ++ [Key.enter])assertBuffer ":g/pat/ s replaces first match per line" s5 "xxx foo\nbar xxx\nxxx bar\n"let s6 := ViE.initialStatelet s7 ← runKeys s6 ([Key.char 'i'] ++ keys "keep1\nfoo\nkeep2\n" ++ [Key.esc] ++ keys "gg0")let s8 ← runKeys s7 ([Key.char ':'] ++ keys "v/foo/ d" ++ [Key.enter])assertBuffer ":v/pat/ d deletes non-matching lines" s8 "foo\n"def testCommandBloom : IO Unit := doIO.println " Testing Command Bloom..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "hello bloom\nbloom hello\n" ++ [Key.esc] ++ keys "gg0")let s2 ← runKeys s1 ([Key.char ':'] ++ keys "bloom /bloom" ++ [Key.enter])assertCursor ":bloom moves to first match" s2 0 6let s3 ← runKeys s2 ([Key.char ':'] ++ keys "bloom /nomatch" ++ [Key.enter])assertEqual ":bloom not found message" "Pattern not found: nomatch" s3.messagedef testUiCommands : IO Unit := doIO.println " Testing UI Commands..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char ':'] ++ keys "float hello" ++ [Key.enter])assertEqual ":float shows overlay" true s1.floatingOverlay.isSomelet s2 ← runKeys s1 [Key.esc]assertEqual "Esc closes overlay" false s2.floatingOverlay.isSomelet s3 ← runKeys s0 ([Key.char ':'] ++ keys "float alpha\\nbeta" ++ [Key.enter])match s3.floatingOverlay with| some overlay =>assertEqual ":float parses newline escape (line count)" 2 overlay.lines.sizeassertEqual ":float parses newline escape (line 1)" "alpha" overlay.lines[0]!assertEqual ":float parses newline escape (line 2)" "beta" overlay.lines[1]!| none =>assertEqual ":float newline overlay exists" true falselet s4 ← runKeys s3 ([Key.char ':'] ++ keys "nofloat" ++ [Key.enter])assertEqual ":nofloat clears overlay" false s4.floatingOverlay.isSomelet s5 ← runKeys s0 ([Key.char ':'] ++ keys "redraw" ++ [Key.enter])assertEqual ":redraw sets message" "redraw" s5.messageassertEqual ":redraw marks dirty" true s5.dirtylet s6 ← runKeys s0 ([Key.char ':'] ++ keys "redraw!" ++ [Key.enter])assertEqual ":redraw! alias works" "redraw" s6.messagelet s7 ← runKeys s0 [Key.ctrl 'l']assertEqual "Ctrl-l redraw marks dirty" true s7.dirtylet s8 ← runKeys s0 ([Key.char ':'] ++ keys "float --title Note --width 32 hello world" ++ [Key.enter])match s8.floatingOverlay with| some overlay =>assertEqual ":float --title sets title" "Note" overlay.titleassertEqual ":float --width sets width" 32 overlay.maxWidthassertEqual ":float with options keeps text" "hello world" overlay.lines[0]!| none =>assertEqual ":float with options shows overlay" true falselet s9 ← runKeys s0 ([Key.char ':'] ++ keys "float --title=Panel --width=28 hi" ++ [Key.enter])match s9.floatingOverlay with| some overlay =>assertEqual ":float --title= sets title" "Panel" overlay.titleassertEqual ":float --width= sets width" 28 overlay.maxWidth| none =>assertEqual ":float with inline options shows overlay" true falselet s10 ← runKeys s0 ([Key.char ':'] ++ keys "float --width nope hi" ++ [Key.enter])assertEqual ":float invalid width message" "Invalid float width: nope" s10.messagelet s11 ← runKeys s0 ([Key.char 'i'] ++ keys "abc" ++ [Key.esc] ++ [Key.char ':'] ++ keys "float guard" ++ [Key.enter])let s12 ← runKeys s11 [Key.char 'i']assertEqual "floating overlay enters insert mode" Mode.insert s12.modelet s13 ← runKeys s12 (keys "X" ++ [Key.enter] ++ keys "Y")match s13.floatingOverlay with| some overlay =>assertEqual "floating overlay writes text" "guardX" overlay.lines[0]!assertEqual "floating overlay writes next line" "Y" overlay.lines[1]!| none =>assertEqual "floating overlay remains open while editing" true falselet s14 ← runKeys s13 [Key.esc]assertEqual "Esc exits floating overlay insert mode" Mode.normal s14.modelet s15 ← runKeys s14 [Key.enter]assertEqual "Enter closes floating overlay" false s15.floatingOverlay.isSomelet sMsg0 := { s0 with message := "Error: sample message", dirty := true }let sMsg1 ← runKeys sMsg0 [Key.enter]assertEqual "Enter closes message float" "" sMsg1.messagelet sMsg2 := { s0 with message := "Cannot write preview buffer", dirty := true }let sMsg3 ← runKeys sMsg2 [Key.esc]assertEqual "Esc closes message float" "" sMsg3.messagelet sPrompt0 ← runKeys s0 ([Key.char ':'] ++ keys "ws list" ++ [Key.enter])let sPrompt1 := sPrompt0.updateActiveView fun v => { v with cursor := { row := ⟨2⟩, col := 0 } }let sPrompt2 ← runKeys sPrompt1 [Key.enter]assertEqual "Workspace explorer New opens floating input" true sPrompt2.floatingOverlay.isSomeassertEqual "Workspace explorer New sets floating command prefix" (some "ws new ") sPrompt2.floatingInputCommandlet sPrompt3 ← runKeys sPrompt2 (keys "TmpWS" ++ [Key.enter])assertEqual "Workspace explorer floating input submits with Enter" "TmpWS" sPrompt3.getCurrentWorkspace.nameassertEqual "Workspace explorer floating input closes after submit" false sPrompt3.floatingOverlay.isSomelet sWsCmd0 ← runKeys s0 ([Key.char ':'] ++ keys "ws new" ++ [Key.enter])assertEqual ":ws new opens floating input" true sWsCmd0.floatingOverlay.isSomeassertEqual ":ws new floating command prefix" (some "ws new ") sWsCmd0.floatingInputCommandlet sWsCmd1 ← runKeys sWsCmd0 (keys "CmdWorkspace" ++ [Key.enter])assertEqual ":ws new floating input submits name" "CmdWorkspace" sWsCmd1.getCurrentWorkspace.namelet sWgCmd0 ← runKeys s0 ([Key.char ':'] ++ keys "wg new" ++ [Key.enter])assertEqual ":wg new opens floating input" true sWgCmd0.floatingOverlay.isSomeassertEqual ":wg new floating command prefix" (some "wg new ") sWgCmd0.floatingInputCommandlet sWgCmd1 ← runKeys sWgCmd0 (keys "CmdGroup" ++ [Key.enter])assertEqual ":wg new floating input submits name" "CmdGroup" sWgCmd1.getCurrentWorkgroup.namelet stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-explorer-create-{stamp}"IO.FS.createDirAll tmpRootlet sExp0 := { s0 with windowHeight := 30, windowWidth := 100 }let sExp1 ← runKeys sExp0 ([Key.char ':'] ++ keys s!"ex list {tmpRoot}" ++ [Key.enter])let explorerOpt := sExp1.explorers.find? (fun (id, _) => id == sExp1.getActiveBuffer.id)match explorerOpt with| none =>assertEqual "Explorer create test has active explorer" true false| some (_, explorer) =>let newFileIdx := (findEntryIndex explorer.entries "[New File]").getD 0let sExp2 := sExp1.updateActiveView fun v => { v with cursor := { row := ⟨2 + newFileIdx⟩, col := 0 } }let sExp3 ← runKeys sExp2 [Key.enter]assertEqual "Explorer [New File] opens floating input" true sExp3.floatingOverlay.isSomeassertEqual "Explorer [New File] sets command prefix" (some s!"mkfile {tmpRoot}/") sExp3.floatingInputCommandlet sExp4 ← runKeys sExp3 (keys "alpha.txt" ++ [Key.enter])let fileCreated ← (System.FilePath.mk s!"{tmpRoot}/alpha.txt").pathExistsassertEqual "Explorer [New File] creates file" true fileCreatedlet explorerOpt2 := sExp4.explorers.find? (fun (id, _) => id == sExp4.getActiveBuffer.id)match explorerOpt2 with| none =>assertEqual "Explorer refreshed after file create" true false| some (_, explorer2) =>assertEqual "Explorer list includes created file" true (findEntryIndex explorer2.entries "alpha.txt").isSomelet newDirIdx := (findEntryIndex explorer.entries "[New Directory]").getD 1let sExp5 := sExp4.updateActiveView fun v => { v with cursor := { row := ⟨2 + newDirIdx⟩, col := 0 } }let sExp6 ← runKeys sExp5 [Key.enter]assertEqual "Explorer [New Directory] opens floating input" true sExp6.floatingOverlay.isSomeassertEqual "Explorer [New Directory] sets command prefix" (some s!"mkdir {tmpRoot}/") sExp6.floatingInputCommandlet sExp7 ← runKeys sExp6 (keys "subdir" ++ [Key.enter])let createdDirPath := System.FilePath.mk s!"{tmpRoot}/subdir"let dirExists ← createdDirPath.pathExistslet dirIsDir ← if dirExists then createdDirPath.isDir else pure falseassertEqual "Explorer [New Directory] creates directory" true dirIsDirlet explorerOpt3 := sExp7.explorers.find? (fun (id, _) => id == sExp7.getActiveBuffer.id)match explorerOpt3 with| none =>assertEqual "Explorer refreshed after directory create" true false| some (_, explorer3) =>assertEqual "Explorer list includes created directory" true (findEntryIndex explorer3.entries "subdir").isSomelet s16 ← runKeys s0 ([Key.char ':'] ++ keys "vs" ++ [Key.enter])let ws16 := s16.getCurrentWorkspaceassertEqual "split creates second window" true (ws16.nextWindowId >= 2)let s17 ← runKeys s16 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let ws17 := s17.getCurrentWorkspaceassertEqual ":floatwin on marks active window floating" true (ws17.isFloatingWindow ws17.activeWindowId)let s18 ← runKeys s17 ([Key.char ':'] ++ keys "floatwin off" ++ [Key.enter])let ws18 := s18.getCurrentWorkspaceassertEqual ":floatwin off clears floating flag" false (ws18.isFloatingWindow ws18.activeWindowId)let s19 ← runKeys s18 ([Key.char ':'] ++ keys "floatwin" ++ [Key.enter])let ws19 := s19.getCurrentWorkspaceassertEqual ":floatwin toggles floating flag" true (ws19.isFloatingWindow ws19.activeWindowId)let sFloatMove0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatMove1 ← runKeys sFloatMove0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let floatMoveId := sFloatMove1.getCurrentWorkspace.activeWindowIdlet beforeFloatMove := sFloatMove1.getFloatingWindowBounds floatMoveIdlet sFloatMove2 ← runKeys sFloatMove1 [Key.alt 'L']let afterFloatMove := sFloatMove2.getFloatingWindowBounds floatMoveIdmatch beforeFloatMove, afterFloatMove with| some (_, left0, _, _), some (_, left1, _, _) =>assertEqual "Alt-Shift-l moves floating window right" (left0 + 1) left1| _, _ =>assertEqual "Alt-Shift-l moves floating window right" true falselet sFloatMoveGrp0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatMoveGrp1 ← runKeys sFloatMoveGrp0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatMoveGrp2 ← runKeys sFloatMoveGrp1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let moveGrpIds := sFloatMoveGrp2.getCurrentWorkspace.getFloatingWindowIds.toListlet beforeGrp := moveGrpIds.filterMap (fun wid => (sFloatMoveGrp2.getFloatingWindowBounds wid).map (fun b => (wid, b)))let sFloatMoveGrp3 ← runKeys sFloatMoveGrp2 [Key.alt 'J']let afterGrp := moveGrpIds.filterMap (fun wid => (sFloatMoveGrp3.getFloatingWindowBounds wid).map (fun b => (wid, b)))let movedAllDown :=moveGrpIds.all (fun wid =>match beforeGrp.find? (fun (id, _) => id == wid), afterGrp.find? (fun (id, _) => id == wid) with| some (_, (top0, _, _, _)), some (_, (top1, _, _, _)) => top1 == top0 + 1| _, _ => false)assertEqual "Alt-Shift-j moves all panes in active floating subtree down" true movedAllDownlet sFloatVs0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatVs1 ← runKeys sFloatVs0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatVs2 ← runKeys sFloatVs1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let wsFloatVs2 := sFloatVs2.getCurrentWorkspacelet floatingIdsVs := wsFloatVs2.getFloatingWindowIds.toListassertEqual "vsplit in floating window keeps new pane floating" true (floatingIdsVs.length >= 2)match floatingIdsVs with| a :: b :: _ =>let sideBySide :=match sFloatVs2.getFloatingWindowBounds a, sFloatVs2.getFloatingWindowBounds b with| some (t1, l1, h1, w1), some (t2, l2, h2, w2) =>t1 == t2 && h1 == h2 && ((l1 + w1 == l2) || (l2 + w2 == l1))| _, _ => falseassertEqual "vsplit in floating window keeps pair side-by-side" true sideBySide| _ =>assertEqual "vsplit in floating window keeps pair side-by-side" true falselet sFloatSp0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatSp1 ← runKeys sFloatSp0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatSp2 ← runKeys sFloatSp1 ([Key.char ':'] ++ keys "split" ++ [Key.enter])let wsFloatSp2 := sFloatSp2.getCurrentWorkspacelet floatingIdsSp := wsFloatSp2.getFloatingWindowIds.toListassertEqual "split in floating window keeps new pane floating" true (floatingIdsSp.length >= 2)match floatingIdsSp with| a :: b :: _ =>let stacked :=match sFloatSp2.getFloatingWindowBounds a, sFloatSp2.getFloatingWindowBounds b with| some (t1, l1, h1, w1), some (t2, l2, h2, w2) =>l1 == l2 && w1 == w2 && ((t1 + h1 == t2) || (t2 + h2 == t1))| _, _ => falseassertEqual "split in floating window keeps pair stacked" true stacked| _ =>assertEqual "split in floating window keeps pair stacked" true falselet sFloatMix0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatMix1 ← runKeys sFloatMix0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatMix2 ← runKeys sFloatMix1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let sFloatMix3 ← runKeys sFloatMix2 ([Key.char ':'] ++ keys "split" ++ [Key.enter])let wsFloatMix3 := sFloatMix3.getCurrentWorkspacelet floatingIdsMix := wsFloatMix3.getFloatingWindowIds.toListassertEqual "vsplit then split in floating window keeps three panes floating" 3 floatingIdsMix.lengthlet boundsMix := floatingIdsMix.filterMap (fun wid => sFloatMix3.getFloatingWindowBounds wid)assertEqual "vsplit then split in floating window resolves bounds for all panes" 3 boundsMix.lengthlet overlaps (a b : Nat × Nat × Nat × Nat) : Bool :=let (ta, la, ha, wa) := alet (tb, lb, hb, wb) := bla < lb + wb && lb < la + wa && ta < tb + hb && tb < ta + halet rec hasOverlap (xs : List (Nat × Nat × Nat × Nat)) : Bool :=match xs with| [] => false| x :: rest => rest.any (fun y => overlaps x y) || hasOverlap restassertEqual "vsplit then split in floating window panes do not overlap" false (hasOverlap boundsMix)let sFloatDeep0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatDeep1 ← runKeys sFloatDeep0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatDeep2 ← runKeys sFloatDeep1 ([Key.char ':'] ++ keys "vs" ++ [Key.enter])let sFloatDeep3 ← runKeys sFloatDeep2 ([Key.char ':'] ++ keys "hs" ++ [Key.enter])let sFloatDeep4 ← runKeys sFloatDeep3 ([Key.char ':'] ++ keys "vs" ++ [Key.enter])let wsFloatDeep4 := sFloatDeep4.getCurrentWorkspacelet floatingIdsDeep := wsFloatDeep4.getFloatingWindowIds.toListassertEqual "vs -> hs -> vs in floating window creates four panes" 4 floatingIdsDeep.lengthlet boundsDeep := floatingIdsDeep.filterMap (fun wid => sFloatDeep4.getFloatingWindowBounds wid)assertEqual "vs -> hs -> vs in floating window resolves bounds for all panes" 4 boundsDeep.lengthassertEqual "vs -> hs -> vs in floating window panes do not overlap" false (hasOverlap boundsDeep)let sideBySideTouch (a b : Nat × Nat × Nat × Nat) : Bool :=let (t1, l1, h1, w1) := alet (t2, l2, h2, w2) := b((l1 + w1 == l2) || (l2 + w2 == l1)) && (t1 < t2 + h2 && t2 < t1 + h1)let stackedTouch (a b : Nat × Nat × Nat × Nat) : Bool :=let (t1, l1, h1, w1) := alet (t2, l2, h2, w2) := b((t1 + h1 == t2) || (t2 + h2 == t1)) && (l1 < l2 + w2 && l2 < l1 + w1)let rec hasPairWith(pred : (Nat × Nat × Nat × Nat) → (Nat × Nat × Nat × Nat) → Bool)(xs : List (Nat × Nat × Nat × Nat)) : Bool :=match xs with| [] => false| x :: rest => rest.any (fun y => pred x y) || hasPairWith pred restassertEqual "vs -> hs -> vs in floating window keeps horizontal adjacency" true (hasPairWith sideBySideTouch boundsDeep)assertEqual "vs -> hs -> vs in floating window keeps vertical adjacency" true (hasPairWith stackedTouch boundsDeep)let longLine := "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"let longText := String.intercalate "\n" [longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine] ++ "\n"let s20 := { s0 with windowHeight := 14, windowWidth := 40 }let s21 ← runKeys s20 ([Key.char 'i'] ++ keys longText ++ [Key.esc] ++ keys "gg0")let s22 ← runKeys s21 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let s23 ← runKeys s22 [Key.char '1', Key.char '0', Key.char 'j']let (sRow23, _) := s23.getScrollassertEqual "floating window vertical scroll follows cursor" true (sRow23.val > 0)let s24 ← runKeys s23 [Key.char '3', Key.char '5', Key.char 'l']let (_, sCol24) := s24.getScrollassertEqual "floating window horizontal scroll follows cursor" true (sCol24.val > 0)let s25 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\n" ++ [Key.esc] ++ keys "gg0")let s26 ← runKeys s25 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let s27 ← runKeys s26 [Key.char 'v', Key.char 'l', Key.char 'd']assertBuffer "visual mode edits active floating window buffer" s27 "cd\n"let s28 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\nefgh\n" ++ [Key.esc] ++ keys "gg0")let s29 ← runKeys s28 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])assertBuffer "visual block edits active floating window buffer" s30 "ad\neh\n"def testBufferExplorerCommand : IO Unit := doIO.println " Testing Buffer Explorer..."let s0 := ViE.initialStatelet stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-buffer-explorer-{stamp}"IO.FS.createDirAll tmpRootlet bufAPath := s!"{tmpRoot}/bufA.txt"let bufBPath := s!"{tmpRoot}/bufB.txt"IO.FS.writeFile bufAPath "alpha\n"IO.FS.writeFile bufBPath "beta\n"let sBuf0 := { s0 with windowHeight := 30, windowWidth := 100 }let sBuf1 ← runKeys sBuf0 ([Key.char ':'] ++ keys s!"e {bufAPath}" ++ [Key.enter])let sBuf2 ← runKeys sBuf1 ([Key.char ':'] ++ keys s!"e {bufBPath}" ++ [Key.enter])let targetBufIdOpt := sBuf2.getCurrentWorkspace.buffers.find? (fun b => b.filename == some bufAPath) |>.map (fun b => b.id)assertEqual "Buffer explorer target buffer exists" true targetBufIdOpt.isSomelet targetBufId := targetBufIdOpt.getD 0let sBuf3 ← runKeys sBuf2 ([Key.char ':'] ++ keys "buf list" ++ [Key.enter])assertEqual ":buf list opens buffer explorer" true ((sBuf3.getActiveBuffer.filename.getD "").startsWith "explorer://buffers")let explorerBufId := sBuf3.getActiveBuffer.idlet explorerBufOpt := sBuf3.explorers.find? (fun (id, _) => id == explorerBufId)match explorerBufOpt with| none =>assertEqual "Buffer explorer registered" true false| some (_, explorerBuf) =>let targetPath := s!"buffer://{targetBufId}"let targetIdxOpt := findEntryIndexByPath explorerBuf.entries targetPathassertEqual "Buffer explorer contains target buffer entry" true targetIdxOpt.isSomelet targetIdx := targetIdxOpt.getD 0let sBuf4 := sBuf3.updateActiveView fun v => { v with cursor := { row := ⟨2 + targetIdx⟩, col := 0 } }let sBuf5 ← runKeys sBuf4 [Key.enter]assertEqual "Buffer explorer Enter switches active buffer" (some bufAPath) sBuf5.getActiveBuffer.filenameassertEqual "Buffer explorer closes after selection" false (sBuf5.explorers.any (fun (id, _) => id == explorerBufId))let sBufCompat ← runKeys sBuf2 ([Key.char ':'] ++ keys "buffers" ++ [Key.enter])assertEqual ":buffers alias opens buffer explorer" true ((sBufCompat.getActiveBuffer.filename.getD "").startsWith "explorer://buffers")let sBufAlias ← runKeys sBuf2 ([Key.char ':'] ++ keys "ls" ++ [Key.enter])assertEqual ":ls alias opens buffer explorer" true ((sBufAlias.getActiveBuffer.filename.getD "").startsWith "explorer://buffers")def testExplorerCommandAliases : IO Unit := doIO.println " Testing Explorer Command Names..."let s0 := ViE.initialStatelet stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-ex-alias-{stamp}"IO.FS.createDirAll tmpRootIO.FS.writeFile s!"{tmpRoot}/x.txt" "x\n"let s1 ← runKeys s0 ([Key.char ':'] ++ keys s!"ex list {tmpRoot}" ++ [Key.enter])assertEqual ":ex list opens file explorer" true ((s1.getActiveBuffer.filename.getD "").startsWith "explorer://")let s2 ← runKeys s0 ([Key.char ':'] ++ keys s!"ee {tmpRoot}" ++ [Key.enter])assertEqual ":ee alias opens file explorer" true ((s2.getActiveBuffer.filename.getD "").startsWith "explorer://")let s3 ← runKeys s0 ([Key.char ':'] ++ keys "wgex" ++ [Key.enter])assertEqual ":wgex alias opens workgroup explorer" (some "explorer://workgroups") s3.getActiveBuffer.filenamelet s1 ← runKeys s0 [Key.char 'c']assertBuffer "completion popup keeps showing on char insert" s1 "abc"assertEqual "completion popup remains visible after char insert" true s1.completionPopup.isSomelet s2 ← runKeys s1 [Key.enter]assertBuffer "Enter inserts newline instead of accepting completion" s2 "abc\n"assertCursor "Enter moves cursor to next line head with completion popup" s2 1 0assertEqual "Enter closes completion popup" true s2.completionPopup.isNonedef testExplorerOpenUsesFocusedWindow : IO Unit := doIO.println " Testing Explorer Open Target Window..."let s0 := { ViE.initialState with windowHeight := 30, windowWidth := 100 }let stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-ex-target-{stamp}"IO.FS.createDirAll tmpRootlet leftPath := s!"{tmpRoot}/left.txt"let rightPath := s!"{tmpRoot}/right.txt"let targetPath := s!"{tmpRoot}/target.txt"IO.FS.writeFile leftPath "left\n"IO.FS.writeFile rightPath "right\n"IO.FS.writeFile targetPath "target\n"let s1 ← runKeys s0 ([Key.char ':'] ++ keys s!"e {leftPath}" ++ [Key.enter])let s2 ← runKeys s1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let rightWinId := s2.getCurrentWorkspace.activeWindowIdlet s3 ← runKeys s2 ([Key.char ':'] ++ keys s!"e {rightPath}" ++ [Key.enter])let s4 ← runKeys s3 ([Key.char ':'] ++ keys "wincmd w" ++ [Key.enter])let targetWinId := s4.getCurrentWorkspace.activeWindowIdassertEqual "wincmd w changes active target window" true (targetWinId != rightWinId)let s5 ← runKeys s4 ([Key.char ':'] ++ keys s!"ex list {tmpRoot}" ++ [Key.enter])let explorerBufId := s5.getActiveBuffer.idlet explorerOpt := s5.explorers.find? (fun (id, _) => id == explorerBufId)match explorerOpt with| none =>assertEqual "File explorer registered" true false| some (_, explorer) =>let targetIdxOpt := findEntryIndex explorer.entries "target.txt"assertEqual "Explorer contains target file" true targetIdxOpt.isSomelet targetIdx := targetIdxOpt.getD 0let s6 := s5.updateActiveView fun v => { v with cursor := { row := ⟨2 + targetIdx⟩, col := 0 } }let s7 ← runKeys s6 [Key.enter]assertEqual "Explorer Enter uses focused window from before open" targetWinId s7.getCurrentWorkspace.activeWindowIdassertEqual "Selected file opens in focused window" (some targetPath) s7.getActiveBuffer.filenamelet ws7 := s7.getCurrentWorkspacematch ws7.layout.findView rightWinId with| none =>assertEqual "Other split window still exists" true false| some rightView =>let rightBufName := ws7.buffers.find? (fun b => b.id == rightView.bufferId) |>.bind (fun b => b.filename)assertEqual "Non-focused split keeps prior buffer" (some rightPath) rightBufNamedef testLeanCompletionPopupBehavior : IO Unit := doIO.println " Testing Lean Completion Popup..."let popup : CompletionPopup := {items := #[{ label := "foo", insertText := "foo" },{ label := "foobar", insertText := "foobar" }]selected := 0anchorRow := 0anchorCol := 2}let s0 : EditorState :=({ ViE.initialState with mode := .insert, completionPopup := some popup }).updateActiveBuffer fun b =>{ b with filename := some "Main.lean", table := ViE.PieceTable.fromString "ab" }let s0 := s0.setCursor { row := ⟨0⟩, col := ⟨2⟩ }let s30 ← runKeys s29 [Key.char 'l', Key.ctrl 'v', Key.char 'l', Key.char 'j', Key.char 'd']let s6 ← runKeys s2 [Key.char 'v', Key.char 'l', Key.char 'd']assertBuffer "custom tabStop: visual delete uses configured offsets" s6 "\to\n"assertCursor "custom tabStop: visual delete keeps cursor aligned" s6 0 8let s9 := ViE.initialStatelet s10 ← runKeys s9 ([Key.char 'i'] ++ keys "a\nfoo1\nb\nfoo2\nc" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char 'j', Key.char 'j', Key.char 'j', Key.char '0'])assertCursor ":g/pat/ d precondition cursor on deletable line" s10 3 0let s11 ← runKeys s10 ([Key.char ':'] ++ keys "g/foo/ d" ++ [Key.enter])assertBuffer ":g/pat/ d keeps expected text when cursor line is deleted" s11 "a\nb\nc"assertCursor ":g/pat/ d clamps cursor when current line is deleted" s11 2 0def testCursorDriftCustomTabStop : IO Unit := doIO.println " Testing Cursor Drift (custom tabStop)..."let cfg8 := { ViE.defaultConfig with tabStop := 8 }let s0 := { ViE.initialState with config := cfg8 }let s1 ← runKeys s0 ([Key.char 'i', Key.char '\t'] ++ keys "foo\n" ++ [Key.esc])let s2 ← runKeys s1 [Key.char 'g', Key.char 'g', Key.char '0', Key.char 'l']assertCursor "custom tabStop: l moves to first char after tab" s2 0 8let s3 ← runKeys s2 ([Key.char ':'] ++ keys "s/foo/bar/" ++ [Key.enter])assertBuffer "custom tabStop: :s updates current line" s3 "\tbar\n"let s4 ← runKeys s3 [Key.char 'u']assertBuffer "custom tabStop: undo restores text after :s" s4 "\tfoo\n"assertCursor "custom tabStop: undo restores cursor col after :s" s4 0 8let s5 ← runKeys s4 [Key.ctrl 'r']assertBuffer "custom tabStop: redo reapplies :s" s5 "\tbar\n"assertCursor "custom tabStop: redo restores cursor col after :s" s5 0 8def testSearch : IO Unit := doIO.println " Testing Search..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "hello world\nhello again\n" ++ [Key.esc])let s2 ← runKeys s1 ([Key.char '/'] ++ keys "hello" ++ [Key.enter])assertCursor "/hello finds first match" s2 0 0let s3 ← runKeys s2 [Key.char 'n']assertCursor "n finds next match" s3 1 0let s4 ← runKeys s3 [Key.char 'N']assertCursor "N finds previous match" s4 0 0let s5 ← runKeys s4 ([Key.char '?'] ++ keys "world" ++ [Key.enter])assertCursor "?world searches backward" s5 0 6def testCommandSubstitute : IO Unit := doIO.println " Testing Command Substitute..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "foo bar\nfoo baz\nbar foo\n" ++ [Key.esc] ++ keys "gg0")let s2 ← runKeys s1 ([Key.char ':'] ++ keys "s/foo/xxx/" ++ [Key.enter])assertBuffer ":s replaces first match on current line" s2 "xxx bar\nfoo baz\nbar foo\n"let s3 := ViE.initialStatelet s4 ← runKeys s3 ([Key.char 'i'] ++ keys "foo foo\nfoo foo\n" ++ [Key.esc] ++ keys "gg0")let s5 ← runKeys s4 ([Key.char ':'] ++ keys "%s/foo/yyy/" ++ [Key.enter])assertBuffer ":%s replaces first match per line" s5 "yyy foo\nyyy foo\n"let s6 := ViE.initialStatelet s7 ← runKeys s6 ([Key.char 'i'] ++ keys "foo bar\nfoo baz\nbar foo\n" ++ [Key.esc] ++ keys "gg0")let s8 ← runKeys s7 ([Key.char ':'] ++ keys "%s/foo/yyy/g" ++ [Key.enter])assertBuffer ":%s with g replaces all matches" s8 "yyy bar\nyyy baz\nbar yyy\n"let s2Prompt ← runKeys s2 [Key.char '/']let promptCleared :=match s2Prompt.searchState with| none => true| some _ => falseassertEqual "Starting new search prompt clears old search highlight state" true promptClearedlet s2e ← runKeys s2 [Key.enter]assertCursor "Enter after search jumps to next match" s2e 1 0assertBuffer "Enter after search does not insert newline" s2e "hello world\nhello again\n"def test : IO Unit := doIO.println "Starting Expanded Keybind Tests..."testMotionstestEditingtestOperatorstestVisualtestCountedtestWorkgroupSwitchtestSearchtestCommandSubstitutetestCommandGlobaltestCommandBloomtestUiCommandstestBufferExplorerCommandtestExplorerCommandAliasestestExplorerOpenUsesFocusedWindowtestLeanCompletionPopupBehaviortestCursorDriftCustomTabStopIO.println "All Expanded Keybind Tests passed!"end Test.KeybindstestVimCompatMotionslet sPct0 ← runKeys s0 ([Key.char 'i'] ++ keys "(a[b]c)" ++ [Key.esc] ++ [Key.char '0'])let sPct1 ← runKeys sPct0 [Key.char '%']assertCursor "% jumps to matching bracket" sPct1 0 6let sPct2 ← runKeys sPct1 [Key.ctrl 'o']assertCursor "Ctrl-o jumps back in jump list" sPct2 0 0let sPct3 ← runKeys sPct2 [Key.char '\t']assertCursor "Ctrl-i (Tab) jumps forward in jump list" sPct3 0 6let sStar0 := ViE.initialStatelet sStar1 ← runKeys sStar0 ([Key.char 'i'] ++ keys "foo bar\nfoo baz\nbar foo\n" ++ [Key.esc] ++ keys "gg0")let sStar2 ← runKeys sStar1 [Key.char '*']assertCursor "* searches next word under cursor" sStar2 1 0let sStar3 ← runKeys sStar2 [Key.char '#']assertCursor "# searches previous word under cursor" sStar3 0 0let s_blkD ← runKeys s_blkD0 [Key.char 'l', Key.ctrl 'v', Key.char 'l', Key.char 'j', Key.char 'd']let s_blkY ← runKeys s_blk0 [Key.char 'l', Key.ctrl 'v', Key.char 'l', Key.char 'j', Key.char 'y']let s_linewise0 ← runKeys s0 ([Key.char 'i'] ++ keys "aa\nbb\ncc" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g'])let s_linewiseD ← runKeys s_linewise0 [Key.char 'V', Key.char 'j', Key.char 'd']assertBuffer "visual line d deletes full lines" s_linewiseD "cc"match s_linewiseD.clipboard with| some reg =>assertEqual "visual line d register kind" RegisterKind.linewise reg.kindassertEqual "visual line d register text" "aa\nbb\n" reg.text| none => assertEqual "visual line d register set" true falselet s_linewiseY ← runKeys s_linewise0 [Key.char 'V', Key.char 'j', Key.char 'y']let s_linewiseP ← runKeys s_linewiseY [Key.char 'G', Key.char 'p']assertBuffer "visual line y/p pastes linewise" s_linewiseP "aa\nbb\ncc\naa\nbb\n"assertCursor "l moves right" s_l 0 1open Test.Utilsimport Test.Utils-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.Configimport ViE.Command.Implimport ViE.Key.Mapimport ViE.State.Editimport ViE.State.Movementnamespace Test.Modeopen 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}")-- Helper to construct a full Configdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}-- Run a sequence of keysdef runKeys (startState : EditorState) (keys : List Key) : IO EditorState := dolet config := makeTestConfiglet mut s := startStatefor k in keys dos ← ViE.update config s kreturn sdef test : IO Unit := doIO.println "Starting Mode Test..."let s := ViE.initialState-- Scenario: Insert 'abc' then Esc.-- Initial: (0,0)-- 'i' -> Insert Mode-- 'a' -> "a", (0,1)-- 'b' -> "ab", (0,2)-- 'c' -> "abc", (0,3)-- Esc -> Normal Mode.-- Expected Vim behavior: Cursor should move left to (0,2) 'c'.-- If empty line "insert a then esc", "a" (0,1) -> (0,0).let keys := [Key.char 'i', Key.char 'a', Key.char 'b', Key.char 'c', Key.esc]let sEnd ← runKeys s keys-- Check Modeif sEnd.mode != Mode.normal thenIO.println s!"[FAIL] Mode mismatch. Expected Normal, got {sEnd.mode}"assert "Mode is Normal" falseelseIO.println "[PASS] Mode is Normal"-- Check Textlet text := getLineFromBuffer sEnd.getActiveBuffer 0 |>.getD ""if text != "abc" thenIO.println s!"[FAIL] Text mismatch. Expected 'abc', got '{text}'"assert "Text is 'abc'" falseelseIO.println "[PASS] Text is 'abc'"-- Check Cursor-- Expect (0, 2)let cursor := sEnd.getCursorif cursor.col.val != 2 thenIO.println s!"[FAIL] Cursor mismatch. Expected (0, 2), got ({cursor.row.val}, {cursor.col.val})"assert "Cursor moved left on Esc" falseelseIO.println "[PASS] Cursor moved left on Esc"IO.println "ModeTest passed!"end Test.Modeprivate theorem visualLineProofWitness :(ViE.initialState.startVisualLineMode).mode = .visualLine := bysimpa using Proof.startVisualLineMode_setsMode ViE.initialStateimport Proof.Visual-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.Configimport ViE.Command.Implimport ViE.Key.Mapimport ViE.State.Editimport ViE.UInamespace ViE.Benchmarkopen ViE/-- Mock Config for Benchmarking -/let mut s := ViE.initialStatefor i in [0:iterations] dos := s.insertChar 'a'if i % 100 == 0 thens := s.commitEdits := s.moveCursorLefts := s.insertChar 'b's := s.moveCursorRights := s.yankCurrentLines := s.pasteBelows := ← ViE.Command.cmdWg ["new", s!"BenchGroup {i}"] ss := s.insertChar 'w'for _ in [0:iterations/200] dos := ← ViE.Command.cmdWg ["close"] sfor _ in [0:iterations/500] dos := s.insertChar 'v's := ViE.Window.cycleWindow ss := ViE.Window.closeActiveWindow ss := s.undofor _ in [0:iterations/10] dos := s.redodef availableCases : List String :=/-- Run benchmark cases. -/def runBenchmark (opts : BenchOptions) : IO Unit := dolet buildLeafBits := opts.buildLeafBits.getD ViE.defaultConfig.searchBloomBuildLeafBitslet config := makeBenchConfig (some buildLeafBits)let cacheMax := config.settings.searchBloomCacheMaxlet cases := if opts.cases.isEmpty then availableCases else opts.casesIO.println s!"Starting benchmark: iter={opts.iterations}, render={opts.render}"if opts.warmup > 0 thenIO.println s!"Warmup: {opts.warmup} iterations"for _ in [0:opts.warmup] dolet mut s := ViE.initialStates := s.insertChar 'a'let _ ← pure sfor c in cases domatch c with| "insert" => timeCase "insert" opts.iterations (benchInsert opts.iterations)| "edit" => timeCase "edit" opts.iterations (benchEditMix opts.iterations)| "clipboard" => timeCase "clipboard" opts.iterations (benchClipboard opts.iterations)| "workgroups" => timeCase "workgroups" opts.iterations (benchWorkgroups opts.iterations)| "windows" => timeCase "windows" opts.iterations (benchWindows opts.iterations)| "undo" => timeCase "undo/redo" opts.iterations (benchUndoRedo opts.iterations)| "search-bloom" =>timeCase "search-bloom" opts.iterations (benchSearch opts.iterations true opts.textLines opts.lineLen buildLeafBits cacheMax)| "search-linear" =>timeCase "search-linear" opts.iterations (benchSearch opts.iterations false opts.textLines opts.lineLen buildLeafBits cacheMax)| "render" =>if opts.render thentimeCase "render" opts.iterations (benchRender opts.iterations)elseIO.println "[bench] render skipped (--no-render)"| other =>IO.println s!"[bench] Unknown case: {other}"| "load-1mb" =>let text := buildLargeText (1024 * 1024)timeCase "load-1mb" opts.iterations (for _ in [0:opts.iterations] dolet pt := PieceTable.fromString text buildLeafBitsif pt.tree.stats.bytes == 0 then IO.println "Zero bytes?" -- force usage)| "load-10mb" =>let text := buildLargeText (10 * 1024 * 1024)timeCase "load-10mb" opts.iterations (for _ in [0:opts.iterations] dolet pt := PieceTable.fromString text buildLeafBitsif pt.tree.stats.bytes == 0 then IO.println "Zero bytes?")| "load-100mb" =>let text := buildLargeText (100 * 1024 * 1024)timeCase "load-100mb" opts.iterations (for _ in [0:opts.iterations] dolet pt := PieceTable.fromString text buildLeafBitsif pt.tree.stats.bytes == 0 then IO.println "Zero bytes?")| "insert-mid-1mb" => timeCase "insert-mid-1mb" opts.iterations (benchInsertMidLarge1MB opts.iterations buildLeafBits)| "insert-mid-10mb" => timeCase "insert-mid-10mb" opts.iterations (benchInsertMidLarge10MB opts.iterations buildLeafBits)| "split-1mb" => timeCase "split-1mb" opts.iterations (benchSplitLarge1MB opts.iterations buildLeafBits)| "split-10mb" => timeCase "split-10mb" opts.iterations (benchSplitLarge10MB opts.iterations buildLeafBits)| "getbytes-1mb" => timeCase "getbytes-1mb" opts.iterations (benchGetBytesLarge1MB opts.iterations buildLeafBits)| "getbytes-10mb" => timeCase "getbytes-10mb" opts.iterations (benchGetBytesLarge10MB opts.iterations buildLeafBits)[ "insert", "edit", "clipboard", "workgroups", "windows", "undo", "search-bloom", "search-linear", "render","load-1mb", "load-10mb", "load-100mb","insert-mid-1mb", "insert-mid-10mb","split-1mb", "split-10mb","getbytes-1mb", "getbytes-10mb" ]IO.println "Benchmark completed."end ViE.Benchmark/-- CLI entrypoint. -/def main (args : List String) : IO Unit := dolet opts := ViE.Benchmark.parseArgs argsif opts.listOnly thenIO.println "Available cases:"IO.println (String.intercalate ", " ViE.Benchmark.availableCases)elseViE.Benchmark.runBenchmark opts/-- Case: Insert at middle of large file (1MB). -/def benchInsertMidLarge1MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0-- Consume split results so optimizer cannot erase this loop.for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Insert at middle of large file (10MB). -/def benchInsertMidLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0-- Consume split results so optimizer cannot erase this loop.for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Split operation on large file (1MB). -/def benchSplitLarge1MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Split operation on large file (10MB). -/def benchSplitLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: GetBytes operation on large file (1MB). -/def benchGetBytesLarge1MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet chunkSize := 1024 -- 1KB chunkslet mut checksum := 0for i in [0:iterations] dolet offset := (i * chunkSize) % pt.tree.stats.bytes.toNatlet bytes := PieceTree.getBytes pt.tree offset chunkSize ptchecksum := checksum + bytes.sizeif checksum == 0 thenIO.println "Zero bytes?"/-- Case: GetBytes operation on large file (10MB). -/def benchGetBytesLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet chunkSize := 1024 -- 1KB chunkslet mut checksum := 0for i in [0:iterations] dolet offset := (i * chunkSize) % pt.tree.stats.bytes.toNatlet bytes := PieceTree.getBytes pt.tree offset chunkSize ptchecksum := checksum + bytes.sizeif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Search workload (PieceTable). -/def benchSearch (iterations : Nat) (useBloom : Bool) (lines lineLen : Nat) (buildLeafBits : Bool) (cacheMax : Nat) : IO Unit := dolet needle := "needle"let text := buildSearchText lines lineLen needlelet pt := PieceTable.fromString text buildLeafBitslet pattern := needle.toUTF8let mut offset := 0let mut cache : Lean.RBMap Nat ByteArray compare := Lean.RBMap.emptylet mut order : Array Nat := #[]for _ in [0:iterations] dolet (res, cache', order') := PieceTree.searchNext pt.tree pt pattern offset searchChunkSize useBloom cache order cacheMaxcache := cache'order := order'offset := match res with| some r => r + 1| none => 0/-- Case: Render workload. -/def benchRender (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor _ in [0:iterations/20] dos := s.insertChar 'a'let _ ← ViE.UI.render s/-- Case: Load large file (100MB). -/def benchLoadLarge100MB (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (100 * 1024 * 1024) -- 100MBlet _ := PieceTable.fromString text buildLeafBitspure ()/-- Generate large text content of specified size (in bytes). -/def buildLargeText (sizeBytes : Nat) : String :=let lineLen := 80let line := String.ofList (List.replicate lineLen 'a') ++ "\n"let lineBytes := line.utf8ByteSizelet numLines := sizeBytes / lineBytesString.intercalate "" (List.replicate numLines line)/-- Case: Load large file (1MB). -/def benchLoadLarge1MB (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024) -- 1MBlet _ := PieceTable.fromString text buildLeafBitspure ()/-- Case: Load large file (10MB). -/def benchLoadLarge10MB (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024) -- 10MBlet _ := PieceTable.fromString text buildLeafBitspure ()/-- Case: Undo/Redo stress. -/def benchUndoRedo (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor i in [0:iterations] dos := s.insertChar 'a'if i % 50 == 0 thens := s.commitEditfor _ in [0:iterations/10] dofor _ in [0:iterations/200] dos := ViE.Window.splitWindow s trues := ViE.Window.splitWindow s false/-- Case: Window splits/cycles. -/def benchWindows (iterations : Nat) : IO Unit := dolet mut s := ViE.initialState/-- Case: Workgroup churn. -/def benchWorkgroups (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor i in [0:iterations/200] do/-- Case: Clipboard (yank/paste). -/def benchClipboard (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStates := s.insertChar 'x'for _ in [0:iterations/50] do/-- Case: Mixed small edits/movements. -/def benchEditMix (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor _ in [0:iterations] dos := s.insertChar 'a'/-- Case: Large insert workload (EditorState). -/def benchInsert (iterations : Nat) : IO Unit := dodef makeBenchConfig (buildLeafBits : Option Bool := none) : Config :=let base := ViE.defaultConfiglet settings :=match buildLeafBits with| some v => { base with searchBloomBuildLeafBits := v }| none => base{settings := settingscommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}structure BenchOptions whereiterations : Nat := 1000render : Bool := truecases : List String := []textLines : Nat := 200lineLen : Nat := 80warmup : Nat := 0buildLeafBits : Option Bool := nonelistOnly : Bool := falsedef parseArgs (args : List String) : BenchOptions :=let rec loop (opts : BenchOptions) (args : List String) : BenchOptions :=match args with| [] => opts| "--no-render" :: rest => loop { opts with render := false } rest| "--case" :: name :: rest => loop { opts with cases := opts.cases ++ [name] } rest| "--lines" :: n :: rest =>match n.toNat? with| some v => loop { opts with textLines := v } rest| none => loop opts rest| "--line-len" :: n :: rest =>match n.toNat? with| some v => loop { opts with lineLen := v } rest| none => loop opts rest| "--warmup" :: n :: rest =>match n.toNat? with| some v => loop { opts with warmup := v } rest| none => loop opts rest| "--bloom-leaf-bits" :: rest => loop { opts with buildLeafBits := some true } rest| "--no-bloom-leaf-bits" :: rest => loop { opts with buildLeafBits := some false } rest| "--list" :: rest => loop { opts with listOnly := true } rest| arg :: rest =>match arg.toNat? with| some v => loop { opts with iterations := v } rest| none => loop opts restloop {} argsdef timeCase (label : String) (iterations : Nat) (f : IO Unit) : IO Unit := dolet t0 ← IO.monoMsNowflet t1 ← IO.monoMsNowlet ms := t1 - t0let opsPerSec := if ms == 0 then 0 else (iterations * 1000) / msIO.println s!"[bench] {label}: {ms} ms ({opsPerSec} ops/s)"/-- Build a simple multi-line ASCII buffer for search benchmarks. -/def buildText (lines : Nat) (lineLen : Nat) : String :=let line := String.ofList (List.replicate lineLen 'a')String.intercalate "\n" (List.replicate lines line)/-- Insert a needle in the middle of text for search benchmarks. -/def buildSearchText (lines : Nat) (lineLen : Nat) (needle : String) : String :=let baseLine := String.ofList (List.replicate lineLen 'a')let mid := lines / 2let leftLen := lineLen / 2let rightLen := if lineLen > leftLen + needle.length then lineLen - leftLen - needle.length else 0let needleLine :=(String.ofList (List.replicate leftLen 'a')) ++needle ++(String.ofList (List.replicate rightLen 'a'))let linesArr := Id.run dolet mut arr := Array.replicate lines baseLineif mid < arr.size thenarr := arr.set! mid needleLinereturn arrString.intercalate "\n" linesArr.toListimport ViE.State.Searchimport ViE.Data.PieceTable.Tree-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.Configimport ViE.Command.Implimport ViE.Key.Mapimport ViE.State.Editnamespace Test.CursorReproductionopen 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}")-- Helper to construct a full Configdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}-- Run a sequence of keysdef runKeys (startState : EditorState) (keys : List Key) : IO EditorState := dolet config := makeTestConfiglet mut s := startStatefor k in keys dos ← ViE.update config s kreturn sdef test : IO Unit := doIO.println "Starting Cursor Reproduction Test..."let s := ViE.initialState-- Scenario: Insert, Undo, Insertlet s1 := s.insertChar 'a'let s1 := s1.commitEdit -- Force separate undo grouplet s2 := s1.insertChar 'b'-- Text: 'ab', Cursor: (0, 2)let s3 := s2.undo-- Text: 'a', Cursor should be (0, 1)let cursor := s3.getCursorif cursor.col.val != 1 thenIO.println s!"[FAIL] Undo cursor mismatch. Expected 1, got {cursor.col.val}"assert "Undo cursor" falseelseIO.println "[PASS] Undo cursor correct"let s4 := s3.insertChar 'c'-- Text: 'ac'let text := getLineFromBuffer s4.getActiveBuffer 0 |>.getD ""if text != "ac" thenIO.println s!"[FAIL] Insert after undo failed. Expected 'ac', got '{text}'"assert "Insert after undo" falseelseIO.println "[PASS] Insert after undo works"-- Scenario: Paste, Undo, Paste (checking grouping too, but focusing on cursor)-- Resetlet s := ViE.initialStatelet s := s.insertChar 'x'let s := s.yankCurrentLine -- Yank 'x\n'let _s := s.pasteBelow -- Paste 'x\n' -> 'x\nx\n' ??-- PasteBelow pastes line.-- Scenario: x commandlet s := ViE.initialStatelet s := s.insertChar 'h'let s := s.insertChar 'e'let s := s.insertChar 'y'-- "hey"let s := (s.moveCursorLeft).moveCursorLeft -- At 'h' (0,0)? No: 3 -> 2 -> 1. 'e'-- "hey" cursor at 2 ('y'). Left -> 1 ('e'). Left -> 0 ('h').-- Actually moveCursorLeft from (0,3) -> (0,2) 'y'.-- Let's use setCursorlet s := s.setCursor (Point.make 0 1) -- 'e'let s := s.deleteCharAfterCursor -- Should delete 'e' -> "hy"let text := getLineFromBuffer s.getActiveBuffer 0 |>.getD ""if text != "hy" thenIO.println s!"[FAIL] 'x' command (deleteCharAfterCursor) failed. Expected 'hy', got '{text}'"assert "x command" falseelseIO.println "[PASS] 'x' command works"IO.println "Cursor Reproduction Test Finished"end Test.CursorReproduction-- Scenario: Insert mode transition + single-char delete keeps line count and cursor in sync.let s := ViE.initialStatelet s ← runKeys s [Key.char 'i', Key.enter]let c1 := s.getCursorassert "Enter in insert creates second line" (s.getActiveBuffer.lineCount == 2)assert "Enter in insert moves cursor to next line" (c1.row.val == 1 && c1.col.val == 0)let s ← runKeys s [Key.esc]let c2 := s.getCursorassert "Esc from insert keeps valid cursor row" (c2.row.val < s.getActiveBuffer.lineCount)assert "Esc from insert keeps row/col at second line head" (c2.row.val == 1 && c2.col.val == 0)-- Re-enter insert and delete one char (the previous newline) via Backspace.let s ← runKeys s [Key.char 'i', Key.backspace]let c3 := s.getCursorassert "Backspace at BOL joins lines" (s.getActiveBuffer.lineCount == 1)assert "Backspace at BOL moves cursor to joined line end" (c3.row.val == 0 && c3.col.val == 0)-- In normal mode x at empty line is a no-op and must keep cursor valid.let s ← runKeys s [Key.esc, Key.char 'x']let c4 := s.getCursorassert "x on empty line keeps line count" (s.getActiveBuffer.lineCount == 1)assert "x on empty line keeps cursor in range" (c4.row.val < s.getActiveBuffer.lineCount)-- Scenario: Wide character insert moves cursor by display widthlet s := ViE.initialStatelet wide := Char.ofNat 0x3042 -- Hiragana A (wide)let s := s.insertChar widelet cursor := s.getCursorif cursor.col.val != 2 thenIO.println s!"[FAIL] Wide char cursor mismatch. Expected 2, got {cursor.col.val}"assert "Wide char cursor" falseelseIO.println "[PASS] Wide char cursor correct"-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.State.Configimport ViE.State.Layoutimport ViE.Data.PieceTableimport Test.Utilsopen ViEnamespace Test.MissingEolopen Test.Utilsdef testMissingEol : IO Unit := doIO.println "testMissingEol..."let s := ViE.initialStatelet s := s.updateActiveBuffer fun buffer =>{ buffer with table := PieceTable.fromString "abc\n", dirty := true }let buf := s.getActiveBufferassert "missingEol false with trailing newline" (!buf.missingEol)let len := buf.table.lengthlet s := s.updateActiveBuffer fun buffer =>{ buffer with table := buffer.table.delete (len - 1) 1 (len - 1), dirty := true }let buf2 := s.getActiveBufferassert "missingEol true after deleting newline" buf2.missingEollet s := ViE.initialStatelet s := s.updateActiveBuffer fun buffer =>{ buffer with table := PieceTable.fromString "abc", dirty := true }let buf3 := s.getActiveBufferassert "missingEol true without newline" buf3.missingEollet len2 := buf3.table.lengthlet s := s.updateActiveBuffer fun buffer =>{ buffer with table := buffer.table.insert len2 "\n" len2, dirty := true }let buf4 := s.getActiveBufferassert "missingEol false after adding newline" (!buf4.missingEol)def test : IO Unit := doIO.println "Starting MissingEol Test..."testMissingEolIO.println "MissingEol Test passed!"end Test.MissingEol-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.Data.PieceTablenamespace Test.PasteReproductiondef test : IO Unit := doIO.println "--- Test 1: Paste below last line (no trailing newline) ---"let pt := ViE.PieceTable.fromString "line 1"IO.println s!"Initial: [{pt.toString}]"-- Yank line 1 (manually simulating EditorState.yankCurrentLine)let line1 := pt.getLine 0 |>.getD ""let yanked := if line1.endsWith "\n" then line1 else line1 ++ "\n"IO.println s!"Yanked: [{yanked}]"-- Paste below line 0 (simulating EditorState.pasteBelow)let (off, text) := match pt.getOffsetFromPoint 1 0 with| some o => (o, yanked)| none =>let len := pt.lengthif len > 0 thenif !pt.endsWithNewline then (len, "\n" ++ yanked) else (len, yanked)else (0, yanked)let pt2 := pt.insert off text offIO.println s!"After paste:\n[{pt2.toString}]"IO.println s!"Line count: {pt2.lineCount}"for i in [:pt2.lineCount] doIO.println s!"Line {i}: [{pt2.getLine i |>.getD "NONE"}]"IO.println "\n--- Test 2: Paste below last line (with trailing newline) ---"let ptB := ViE.PieceTable.fromString "line 1\n"IO.println s!"Initial: [{ptB.toString}]"let line1B := ptB.getLine 0 |>.getD ""let yankedB := if line1B.endsWith "\n" then line1B else line1B ++ "\n"let (offB, textB) := match ptB.getOffsetFromPoint 1 0 with| some o => (o, yankedB)| none =>let len := ptB.lengthif len > 0 thenif !ptB.endsWithNewline then (len, "\n" ++ yankedB) else (len, yankedB)else (0, yankedB)let ptB2 := ptB.insert offB textB offBIO.println s!"After paste:\n[{ptB2.toString}]"IO.println s!"Line count: {ptB2.lineCount}"for i in [:ptB2.lineCount] doIO.println s!"Line {i}: [{ptB2.getLine i |>.getD "NONE"}]"-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.UI.Searchimport Test.Utilsopen Test.Utilsnamespace Test.SyntaxHighlightprivate def firstRangeOf (line : String) (pat : String) : Option (Nat × Nat) :=(ViE.UI.findAllMatchesBytes line.toUTF8 pat.toUTF8)[0]?def testLean : IO Unit := dolet line := "def x := 42 -- comment"let (defS, defE) := (firstRangeOf line "def").getD (0, 0)let (numS, numE) := (firstRangeOf line "42").getD (0, 0)let (comS, comE) := (firstRangeOf line "-- comment").getD (0, 0)def testMarkdown : IO Unit := dolet line := "Use `code` and [link](url)"let (codeS, codeE) := (firstRangeOf line "`code`").getD (0, 0)let (linkS, linkE) := (firstRangeOf line "[link](url)").getD (0, 0)def test : IO Unit := doIO.println "Starting SyntaxHighlight Test..."testLeantestMarkdownIO.println "SyntaxHighlight Test passed!"end Test.SyntaxHighlightassertEqual "Markdown code style" (Bliku.Tui.Syntax.defaultPalette.faceFor .code)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans codeS codeE)assertEqual "Markdown link style" (Bliku.Tui.Syntax.defaultPalette.faceFor .link)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans linkS linkE)let spans := Bliku.Tui.Syntax.highlightLine (some "README.md") lineassertEqual "Lean keyword style" (Bliku.Tui.Syntax.defaultPalette.faceFor .keyword)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans defS defE)assertEqual "Lean number style" (Bliku.Tui.Syntax.defaultPalette.faceFor .numberLiteral)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans numS numE)assertEqual "Lean comment style" (Bliku.Tui.Syntax.defaultPalette.faceFor .comment)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans comS comE)let spans := Bliku.Tui.Syntax.highlightLine (some "Main.lean") lineimport Bliku.Tui.Syntax-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.Data.PieceTableimport Test.Utilsfor i in [0:iterations] dopt := pt.insert pt.tree.length s!"line {i}\n" pt.tree.lengthdef report (pt : PieceTable) (iterations : Nat) : IO Unit := dolet tree := pt.treelet h := PieceTree.height treelet nodes := countNodes treelet leaves := countLeaves treelet maxLeaf := maxLeafPieces treelet maxChildren := maxInternalChildren treeIO.println s!"iterations={iterations}"IO.println s!"bytes={PieceTree.length tree} lines={PieceTree.lineBreaks tree} height={h}"IO.println s!"nodes={nodes} leaves={leaves} maxLeafPieces={maxLeaf} maxChildren={maxChildren}"match tree withif iterations > 0 thenlet start := ← IO.monoMsNowlet _ := pt.getLineRange (iterations - 1)let stop := ← IO.monoMsNowIO.println s!"getLineRange(last)={stop - start}ms"def test : IO Unit := doIO.println "Starting TreeStats Test..."let iterations := 3000let pt := buildWorkload iterationslet t := pt.treelet bytes := PieceTree.length tlet textBytes := pt.toString.toUTF8.sizelet isNonEmpty :=match t with| .empty => false| _ => trueassertEqual "tree length equals rendered bytes" textBytes bytesassert "tree is non-empty" isNonEmptyassert "leaf piece count stays within node capacity" (maxLeafPieces t <= NodeCapacity)assert "internal children stay within node capacity" (maxInternalChildren t <= NodeCapacity)assert "line range for last inserted line exists" ((pt.getLineRange (iterations - 1)).isSome)IO.println "TreeStats Test passed!"def main (args : List String) : IO Unit := dolet iterations := parseIterations argslet pt := buildWorkload iterationsreport pt iterationsend Test.TreeStatsassert "tree height is positive for non-empty tree" (PieceTree.height t >= 1)| .empty => IO.println "root=empty"| .leaf ps _ _ => IO.println s!"root=leaf pieces={ps.size}"| .internal cs _ _ _ => IO.println s!"root=internal children={cs.size}"return ptdef parseIterations (args : List String) : Nat :=let args :=match args with| "--" :: rest => rest| _ => argsmatch args with| a :: _ =>match a.toNat? with| some n => n| none => 5000| [] => 5000def countLeavesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => countLeaves t + countLeavesList restendmutualdef maxLeafPieces (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf ps _ _ => ps.sizedef maxLeafPiecesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => max (maxLeafPieces t) (maxLeafPiecesList rest)endmutualdef maxInternalChildren (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 0max cs.size (maxInternalChildrenList cs.toList)def maxInternalChildrenList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => max (maxInternalChildren t) (maxInternalChildrenList rest)end| .internal cs _ _ _ =>| .internal cs _ _ _ => maxLeafPiecesList cs.toListmutualdef countLeaves (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ _ => countLeavesList cs.toListdef countNodesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => countNodes t + countNodesList restendmutualdef countNodes (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ _ => 1 + countNodesList cs.toListopen ViEopen Test.Utilsdef buildWorkload (iterations : Nat) : PieceTable := Id.run dolet mut pt := PieceTable.fromString "Initial content\n"namespace Test.TreeStats-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.Buffer.Contentimport ViE.Typesimport ViE.Data.PieceTableopen ViEnamespace Test.Bufferdef 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 := doIO.println "Starting Buffer Test..."-- 1. Test fromString and getLinelet text := "Hello\nWorld"let pt := PieceTable.fromString textlet buf : FileBuffer := { initialFileBuffer with table := pt }-- Check linesmatch getLineFromBuffer buf 0 with| some l => assert "Line 0 is Hello" (l == "Hello")| none => assert "Line 0 missing" falsematch getLineFromBuffer buf 1 with| some l => assert "Line 1 is World" (l == "World")| none => assert "Line 1 missing" falsematch getLineFromBuffer buf 2 with| some _ => assert "Line 2 should be missing" false| none => assert "Line 2 correctly missing" true-- 2. Test FileBuffer <-> TextBuffer conversionlet tb : TextBuffer := #[ #['A', 'B'], #['C'] ] -- "AB\nC"let buf2 := ViE.Buffer.fileBufferFromTextBuffer 1 (some "test.txt") tbassert "Buffer created from TextBuffer id" (buf2.id == 1)assert "Buffer created from TextBuffer filename" (buf2.filename == some "test.txt")let tb2 := ViE.Buffer.fileBufferToTextBuffer buf2assert "Roundtrip TextBuffer size" (tb2.size == 2)if tb2.size == 2 thenlet l0 := tb2[0]!let l1 := tb2[1]!assert "Roundtrip Line 0" (String.ofList l0.toList == "AB")assert "Roundtrip Line 1" (String.ofList l1.toList == "C")IO.println "BufferTest passed!"end Test.Buffer-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.Typesimport ViE.State.Configimport ViE.Configimport ViE.Key.Mapimport ViE.Command.Implopen ViEnamespace Test.Integration-- Helper to construct a full Configdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}-- Run a sequence of keysdef runKeys (startState : EditorState) (keys : List Key) : IO EditorState := dolet config := makeTestConfiglet mut s := startStatefor k in keys dos ← ViE.update config s kreturn s-- Helper: Convert string to list of char keysdef keys (s : String) : List Key :=s.toList.map Key.chardef test : IO Unit := doIO.println "Starting Integration Test..."-- Test 1: Typing "abc" -> Undo -> Insert "d"-- Expected: "abc" -> "" -> "d"-- Bug Report: After Undo, Insert mode might fail or buffer might be corruptedlet s0 := ViE.initialState-- 1. Insert "abc"-- i a b c Esclet input1 := [Key.char 'i'] ++ keys "abc" ++ [Key.esc]let s1 ← runKeys s0 input1-- 2. Undo-- ulet s2 ← runKeys s1 [Key.char 'u']-- 3. Insert "d"-- i d Esclet s3 ← runKeys s2 [Key.char 'i', Key.char 'd', Key.esc]IO.println "IntegrationTest passed!"end Test.IntegrationassertBuffer "Text after re-insert d" s3 "d"assertCursor "Cursor after insert 'd' and Esc should be (0,0)" s3 0 0assertBuffer "Text after undo" s2 ""assertCursor "Cursor after undo should be (0,0)" s2 0 0assertBuffer "Text after insert abc" s1 "abc"open Test.Utilsimport Test.Utils-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
import ViE.Typesopen ViEnamespace Test.Layoutdef 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 := doIO.println "Starting Layout Test..."-- 1. Construct Layoutslet view1 : ViewState := { bufferId := 1, cursor := {row:=0, col:=0}, scrollRow:=0, scrollCol:=0 }let win1 := Layout.window 1 view1let view2 : ViewState := { bufferId := 2, cursor := {row:=0, col:=0}, scrollRow:=0, scrollCol:=0 }let win2 := Layout.window 2 view2-- 2. Test Split Constructionlet hsplit := Layout.hsplit win1 win2 0.5match hsplit withassert "HSplit match" trueassert "HSplit ratio" (ratio == 0.5)| _ => assert "HSplit construction failed" falselet vsplit := Layout.vsplit hsplit win1 0.3match vsplit withassert "VSplit match" trueassert "VSplit ratio" (ratio == 0.3)match t with| .hsplit _ _ _ => assert "VSplit top is HSplit" true| _ => assert "VSplit top incorrect" false| _ => assert "VSplit construction failed" falseIO.println "LayoutTest passed!"end Test.Layout| .vsplit t _ ratio =>| .hsplit _ _ ratio =>-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Data.PieceTablenamespace Test.Undodef 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 := doIO.println "Starting Undo/Redo Test..."-- 1. Create initial bufferlet initialText := "Hello, World!"let pt0 := PieceTable.fromString initialTextassert "Initial text correct" (pt0.toString == initialText)assert "Initial undo stack empty" (pt0.undoStack.isEmpty)-- 2. Insert " New" at end-- "Hello, World! New"let pt1 := pt0.insert 13 " New" 13assert "Text after insert" (pt1.toString == "Hello, World! New")assert "Undo stack has 1 item" (pt1.undoStack.length == 1)-- 3. Delete ", World"-- "Hello! New"-- "Hello, World!" -> offset 5, delete 7 (", World")let pt2 := pt1.delete 5 7 5assert "Text after delete" (pt2.toString == "Hello! New")assert "Undo stack has 2 items" (pt2.undoStack.length == 2)-- 4. Undo Modify (Delete)-- Should revert to "Hello, World! New"let (pt3, _) := pt2.undo 0assert "Text after undo delete" (pt3.toString == "Hello, World! New")assert "Undo stack has 1 item" (pt3.undoStack.length == 1)assert "Redo stack has 1 item" (pt3.redoStack.length == 1)-- 5. Undo Insert-- Should revert to "Hello, World!"let (pt4, _) := pt3.undo 0assert "Text after undo insert" (pt4.toString == "Hello, World!")assert "Undo stack has 0 items" (pt4.undoStack.isEmpty)assert "Redo stack has 2 items" (pt4.redoStack.length == 2)-- 6. Redo Insert-- Should go to "Hello, World! New"let (pt5, _) := pt4.redo 0assert "Text after redo insert" (pt5.toString == "Hello, World! New")assert "Undo stack has 1 item" (pt5.undoStack.length == 1)assert "Redo stack has 1 item" (pt5.redoStack.length == 1)-- 7. Redo Delete-- Should go to "Hello! New"let (pt6, _) := pt5.redo 0assert "Text after redo delete" (pt6.toString == "Hello! New")assert "Undo stack has 2 items" (pt6.undoStack.length == 2)assert "Redo stack empty" (pt6.redoStack.isEmpty)-- 8. Test Redo clearing on new editlet (pt7, _) := pt6.undo 0 -- "Hello, World! New"-- Insert " Again" at end -> "Hello, World! New Again"let pt8 := pt7.insert 17 " Again" 17assert "Text after new insert" (pt8.toString == "Hello, World! New Again")assert "Redo stack cleared" (pt8.redoStack.isEmpty)-- 9. Test Optimization (Grouping)-- Force break merge chain from previous step to ensure " 1" starts a new grouplet pt8_clean := { pt8 with lastInsert := none }let pt9 := pt8_clean.insert 23 " 1" 23 -- "Hello, World! New Again 1"let pt10 := pt9.insert 25 "2" 25 -- "Hello, World! New Again 12"let pt11 := pt10.insert 26 "3" 26 -- "Hello, World! New Again 123"assert "Text after group insert" (pt11.toString == "Hello, World! New Again 123")-- Should have only 1 new undo item for " 123" because they overlap-- pt8 had some undo stack. pt9 added 1. pt10 merged. pt11 merged.-- So pt11.undoStack.length should be pt8.undoStack.length + 1assert "Undo stack grouped" (pt11.undoStack.length == pt8.undoStack.length + 1)let (pt12, _) := pt11.undo 0assert "Undo removes group" (pt12.toString == "Hello, World! New Again")-- 10. Test Limit-- Force a small limitlet ptLimit := { pt12 with undoLimit := 2 }let ptL1 := (ptLimit.insert 0 "A" 0) -- Stack: 1let ptL2 := (ptL1.insert 0 "B" 0) -- Stack: 2-- Previous insert was "A" at 0. LastInsert = (0 + 1, addOffset + 1).-- Current insert at 0. 0 != 1. So NOT contiguous. Correct.let ptL3 := (ptL2.insert 0 "C" 0) -- Stack: 3 -> capped to 2assert "Undo limit respected" (ptL3.undoStack.length == 2)-- The oldest undo (for "A") should be dropped. The remaining undos are "C" -> "B", and "B" -> "A".-- So undoing twice should start with "CBA..." -> "BA..." -> "A..."let (u1, _) := ptL3.undo 0let (u2, _) := u1.undo 0assert "Oldest undo dropped" (u2.toString == "AHello, World! New Again")-- 11. Test Paste Undo Grouping (Reproduction)-- Scenario: Paste "P1" then Paste "P2". They should NOT merge if we signal a break,-- OR if we consider pastes as distinct operations that shouldn't auto-merge like typing.-- Currently PieceTable merges ANY contiguous insert.let ptBase := PieceTable.fromString ""let ptP1 := ptBase.insert 0 "P1" 0 -- Simulate Paste 1-- Simulate EditorState.paste calling commit before next insertlet ptP1_committed := ptP1.commitlet ptP2 := ptP1_committed.insert 2 "P2" 2 -- Simulate Paste 2 (contiguous)assert "Text is P1P2" (ptP2.toString == "P1P2")-- Expectation: 2 undo items (one for P1, one for P2).-- Bug: they merge into 1 item because offset 2 == lastInsert end.if ptP2.undoStack.length == 1 thenIO.println "[FAIL] Paste operations merged (expected 2 undo items)"assert "Paste should not merge" falseelseIO.println "[PASS] Paste operations distinct (2 undo items)"IO.println "TestUndo passed!"end Test.Undoopen ViE
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import 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 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)
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Appimport ViE.Checkpointimport ViE.State.Configimport Test.Utilsnamespace Test.Checkpointopen Test.Utilsopen ViEdef testLoadSessionInvalid : IO Unit := doIO.println "Starting Checkpoint Parse Invalid Test..."IO.FS.writeFile ViE.Checkpoint.sessionFile "--ACTIVE--\nnot-a-number\n"let loaded <- ViE.Checkpoint.loadSessionassertEqual "Invalid checkpoint parses as none" none loadeddef testLoadSessionValid : IO Unit := doIO.println "Starting Checkpoint Parse Valid Test..."let content :="/tmp/file-a.txt\n" ++"0 0\n" ++"/tmp/file-b.txt\n" ++"3 4\n" ++"--ACTIVE--\n" ++"1\n"IO.FS.writeFile ViE.Checkpoint.sessionFile contentlet loaded <- ViE.Checkpoint.loadSessionlet expected : Option (List String × Nat × List (Nat × Nat)) :=some (["/tmp/file-a.txt", "/tmp/file-b.txt"], 1, [(0, 0), (3, 4)])assertEqual "Valid checkpoint parsing" expected loadeddef testBuildRestoredWorkspace : IO Unit := doIO.println "Starting Restored Workspace Build Test..."let stamp <- IO.monoMsNowlet root := s!"/tmp/vie-checkpoint-{stamp}"IO.FS.createDirAll rootlet f1 := s!"{root}/a.txt"let f2 := s!"{root}/b.txt"IO.FS.writeFile f1 "alpha\nbeta\n"IO.FS.writeFile f2 "line0\nline1\nline2\n"let ws <- ViE.buildRestoredWorkspace ViE.defaultConfig (some root) [f1, f2] 1 [(0, 0), (1, 2)]assertEqual "Restored workspace buffer count" 2 ws.buffers.lengthassertEqual "Restored workspace nextBufferId" 2 ws.nextBufferIdlet active := ws.layout.findView ws.activeWindowId |>.getD ViE.initialViewassertEqual "Restored active buffer id" 1 active.bufferIdassertEqual "Restored cursor row" 1 active.cursor.row.valassertEqual "Restored cursor col" 2 active.cursor.col.valmatch ws.buffers with| b1 :: b2 :: _ =>assertEqual "Restored first file path" (some f1) b1.filenameassertEqual "Restored second file path" (some f2) b2.filename| _ =>throw (IO.userError "Expected two restored buffers")def testBuildRestoredWorkspaceCustomTabStop : IO Unit := doIO.println "Starting Restored Workspace Custom TabStop Test..."let stamp <- IO.monoMsNowlet root := s!"/tmp/vie-checkpoint-tab-{stamp}"IO.FS.createDirAll rootlet f1 := s!"{root}/tab.txt"IO.FS.writeFile f1 "\talpha\n"let settings := { ViE.defaultConfig with tabStop := 8 }let ws <- ViE.buildRestoredWorkspace settings (some root) [f1] 0 [(0, 8)]let active := ws.layout.findView ws.activeWindowId |>.getD ViE.initialViewassertEqual "Restored custom tabStop cursor row" 0 active.cursor.row.valassertEqual "Restored custom tabStop cursor col" 8 active.cursor.col.vallet s0 := ({ ViE.initialState with config := ViE.defaultConfig }).updateCurrentWorkspace (fun _ => ws)let s1 := s0.updateCurrentWorkspace fun ws =>{ ws with layout := ws.layout.updateView ws.activeWindowId (fun v => { v with bufferId := 0 }) }let s2 ← ViE.Buffer.ensureActiveBufferLoaded s1let activeLoaded := s2.getActiveBufferassertEqual "Lazy buffer loads on activation" true activeLoaded.loadedassertEqual "Lazy loaded buffer keeps file path" (some f1) activeLoaded.filenamedef test : IO Unit := dotestLoadSessionInvalidtestLoadSessionValidtestBuildRestoredWorkspacetestBuildRestoredWorkspaceCustomTabStopend Test.CheckpointassertEqual "Restored inactive buffer is lazy" false b1.loadedassertEqual "Restored active buffer is loaded" true b2.loadedimport ViE.Buffer.Manager
import ViE.Data.PieceTableimport Test.Utilsopen ViEnamespace Test.PieceTable.Searchopen Test.Utilsdef makeBoundaryTable : PieceTable :=let n := ViE.NodeCapacity + 1let startIdx := n / 2 - 1let chars := Id.run dolet mut cs := Array.replicate n 'x'cs := cs.set! startIdx 'a'cs := cs.set! (startIdx + 1) 'b'cs := cs.set! (startIdx + 2) 'c'return cslet s := String.ofList chars.toListlet bytes := s.toUTF8let pieces := (Array.range n).map fun i =>{ source := BufferSource.originallet tmp : PieceTable := {original := bytesaddBuffers := #[]tree := PieceTree.emptyundoStack := []redoStack := []undoStackCount := 0redoStackCount := 0undoLimit := 100lastInsert := none}let tree := PieceTree.fromPieces pieces tmp{ tmp with tree := tree }def testCrossLeafSearch : IO Unit := doIO.println "testCrossLeafSearch..."let pt := makeBoundaryTablelet n := ViE.NodeCapacity + 1let startIdx := n / 2 - 1let pattern := "abc".toUTF8let (r1, _, _) := PieceTree.searchNext pt.tree pt pattern 0 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchNext bloom across leaf" (some startIdx) r1let total := pt.tree.lengthlet (r2, _, _) := PieceTree.searchPrev pt.tree pt pattern total 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchPrev bloom across leaf" (some startIdx) r2def testCrossLeafSearchNearBoundary : IO Unit := doIO.println "testCrossLeafSearchNearBoundary..."let pt := makeBoundaryTablelet n := ViE.NodeCapacity + 1let startIdx := n / 2 - 1let pattern := "abc".toUTF8let startOffset := startIdx - 1let (r1, _, _) := PieceTree.searchNext pt.tree pt pattern startOffset 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchNext from near boundary" (some startIdx) r1let startExclusive := startIdx + pattern.sizelet (r2, _, _) := PieceTree.searchPrev pt.tree pt pattern startExclusive 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchPrev to near boundary" (some startIdx) r2def test : IO Unit := doIO.println "Starting PieceTable Search Test..."testCrossLeafSearchtestCrossLeafSearchNearBoundaryIO.println "PieceTable Search Test passed!"end Test.PieceTable.Searchstart := i.toUInt64length := (1 : UInt64)lineBreaks := (0 : UInt64)charCount := (1 : UInt64) }private theorem twoWayBoundaryWitness :ViE.PieceTree.twoWaySearch "abc".toUTF8 ByteArray.empty 1 = some 1 := bysimp using Proof.PieceTable.Search.twoWaySearch_empty "abc".toUTF8 1import Proof.PieceTable.Search.TwoWay
import ViE.Data.PieceTableimport Test.Utilsopen ViEnamespace Test.PieceTable.Appendedopen Test.Utilsdef testAppends : IO Unit := doIO.println "testAppends..."let pt := PieceTable.fromString "Line1\n"-- Append Line 2let pt1 := pt.insert pt.length "Line2\n" pt.lengthassertEqual "Append 1" "Line1\nLine2\n" pt1.toString-- Append Line 3let pt2 := pt1.insert pt1.length "Line3" pt1.lengthassertEqual "Append 2" "Line1\nLine2\nLine3" pt2.toStringdef test : IO Unit := doIO.println "Starting PieceTable Appended Test..."testAppendsIO.println "PieceTable Appended Test passed!"end Test.PieceTable.Appended
import ViE.Data.PieceTableimport Test.Utilsopen ViEnamespace Test.PieceTable.Basicopen Test.Utilsdef testInsert : IO Unit := doIO.println "testInsert..."let pt := PieceTable.fromString "Hello"-- Insert at endlet pt1 := pt.insert 5 " World" 5assertEqual "Insert at end" "Hello World" pt1.toString-- Insert at middlelet pt2 := pt1.insert 5 "," 5assertEqual "Insert at middle" "Hello, World" pt2.toString-- Insert at startlet pt3 := pt2.insert 0 "Say: " 0assertEqual "Insert at start" "Say: Hello, World" pt3.toStringdef testDelete : IO Unit := doIO.println "testDelete..."let pt := PieceTable.fromString "Hello, World!"-- Delete "World" (offset 7, len 5)let pt1 := pt.delete 7 5 7assertEqual "Delete middle" "Hello, !" pt1.toString-- Delete startlet pt2 := pt1.delete 0 7 0assertEqual "Delete start" "!" pt2.toString-- Delete endlet pt3 := pt2.delete 0 1 0assertEqual "Delete end" "" pt3.toStringdef test : IO Unit := doIO.println "Starting PieceTable Basic Test..."testInserttestDeleteIO.println "PieceTable Basic Test passed!"end Test.PieceTable.Basic
import ViE.Data.PieceTableimport Test.Utilsopen ViEnamespace Test.PieceTable.Stressopen Test.Utils/-- Naive string manipulation for verification -/def naiveInsert (s : String) (idx : Nat) (content : String) : String :=let lst := s.toListlet pre := lst.take idxlet post := lst.drop idxString.ofList (pre ++ content.toList ++ post)def naiveDelete (s : String) (idx : Nat) (len : Nat) : String :=let lst := s.toListlet pre := lst.take idxlet post := lst.drop (idx + len)String.ofList (pre ++ post)def testConsistency : IO Unit := doIO.println "testConsistency (Small Stress)..."let mut pt := PieceTable.fromString ""let mut mirror := ""-- 1. Insert Hellopt := pt.insert 0 "Hello" 0mirror := naiveInsert mirror 0 "Hello"assertEqual "Step 1" mirror pt.toString-- 2. Insert Worldpt := pt.insert 5 " World" 5mirror := naiveInsert mirror 5 " World"assertEqual "Step 2" mirror pt.toString-- 3. Delete space at 5-- "Hello World" -> "HelloWorld"pt := pt.delete 5 1 5mirror := naiveDelete mirror 5 1assertEqual "Step 3" mirror pt.toString-- 4. Insert comma at 5-- "HelloWorld" -> "Hello,World"pt := pt.insert 5 "," 5mirror := naiveInsert mirror 5 ","assertEqual "Step 4" mirror pt.toStringdef test : IO Unit := doIO.println "Starting PieceTable Stress Test..."testConsistencyIO.println "PieceTable Stress Test passed!"end Test.PieceTable.StresstestLengthAndLineCountConsistencytestYankPasteClearingdef testLengthAndLineCountConsistency : IO Unit := doIO.println "testLengthAndLineCountConsistency..."let mut pt := PieceTable.fromString ""for i in [0:1200] dolet len := pt.lengthlet pos := if len == 0 then 0 else (i * 17 + 3) % (len + 1)if i % 5 == 0 thenlet txt := if i % 2 == 0 then "\n" else s!"x{i}"pt := pt.insert pos txt poselse if i % 7 == 0 thenlet delLen := if len == 0 then 0 else min ((i % 4) + 1) (len - (pos % len))pt := pt.delete (if len == 0 then 0 else pos % len) delLen 0elselet txt := if i % 3 == 0 then "a\n" else "b"pt := pt.insert pos txt poslet rendered := pt.toStringlet renderedBytes := rendered.toUTF8.sizeif renderedBytes != pt.length thenthrow <| IO.userError s!"Length mismatch at step {i}: tree={pt.length} rendered={renderedBytes}"let expectedLines := expectedLineCountFromText renderedif pt.lineCount != expectedLines thenthrow <| IO.userError s!"Line count mismatch at step {i}: tree={pt.lineCount} expected={expectedLines}"let h := PieceTree.height pt.treeif h > 64 thenthrow <| IO.userError s!"Tree height unexpectedly large at step {i}: height={h}"IO.println "[PASS] length/line count remain consistent under mixed edits"def testYankPasteClearing : IO Unit := doIO.println "testYankPasteClearing (yy+p reproduction)..."-- Simulate: insert 25 'i' chars + newline, then yy+p 4000 timeslet initLine := String.ofList (List.replicate 25 'i') ++ "\n"let lineBytes := initLine.toUTF8.size -- 26 byteslet mut pt := PieceTable.fromString initLinelet mut cursorLine : Nat := 0for step in [0:4000] do-- 1. yy: read current line content via getLineRange + getBytes (like real editor)let range := PieceTree.getLineRange pt.tree cursorLine ptmatch range with| none =>throw <| IO.userError s!"[YankPaste] getLineRange returned none at step {step}, line {cursorLine}"| some (off, len) =>let yankedBytes := PieceTree.getBytes pt.tree off len ptif yankedBytes.size == 0 thenthrow <| IO.userError s!"[YankPaste] Empty yank at step {step}, line {cursorLine}, off={off} len={len}"-- 2. p: paste below current line.-- Insert after end of current line (off + len + 1 for newline)let insertOffset := off + len + 1let insertOffset := min insertOffset pt.lengthlet yankStr := (String.fromUTF8! yankedBytes) ++ "\n"pt := pt.insert insertOffset yankStr insertOffset-- 3. Move cursor to pasted linecursorLine := cursorLine + 1-- 4. Verify tree lengthlet expectedLen := lineBytes * (step + 2)let actualLen := pt.lengthif actualLen != expectedLen thenthrow <| IO.userError s!"[YankPaste] Length mismatch at step {step}: expected={expectedLen} actual={actualLen}"-- 5. Full consistency check every 500 stepsif step % 500 == 0 thenlet rendered := pt.toString.toUTF8.sizeif rendered != expectedLen thenthrow <| IO.userError s!"[YankPaste] Rendered mismatch at step {step}: expected={expectedLen} rendered={rendered}"IO.println s!" step {step}: length={actualLen} lines={PieceTree.lineBreaks pt.tree} height={PieceTree.height pt.tree} OK"IO.println "[PASS] yank+paste 4000 iterations with getLineRange consistent"def newlineCount (s : String) : Nat :=s.foldl (fun n c => if c == '\n' then n + 1 else n) 0def expectedLineCountFromText (s : String) : Nat :=let breaks := newlineCount sbreaks + 1
import ViE.Data.PieceTableimport Test.Utilsopen ViEnamespace Test.PieceTable.UndoRedoopen Test.Utilsdef testUndoRedo : IO Unit := doIO.println "testUndoRedo..."let pt := PieceTable.fromString ""-- Insert "A" at 0let pt1 := pt.insert 0 "A" 0-- Insert "B" at 0 (Prepend) -> "BA"let pt2 := pt1.insert 0 "B" 0assertEqual "Initial state" "BA" pt2.toString-- Undo B (restores to "A")let (pt3, _) := pt2.undo 0assertEqual "After Undo 1" "A" pt3.toString-- Undo Alet (pt4, _) := pt3.undo 0assertEqual "After Undo 2" "" pt4.toString-- Redo Alet (pt5, _) := pt4.redo 0assertEqual "After Redo 1" "A" pt5.toString-- Redo Blet (pt6, _) := pt5.redo 0assertEqual "After Redo 2" "BA" pt6.toStringdef testComplexUndo : IO Unit := doIO.println "testComplexUndo..."let pt := PieceTable.fromString "Base"let pt1 := pt.insert 4 "1" 4 -- Base1let pt1 := pt1.commit -- Prevent mergelet pt2 := pt1.insert 5 "2" 5 -- Base12let pt3 := pt2.delete 5 1 5 -- Base1let pt4 := pt3.insert 5 "3" 5 -- Base13assertEqual "State before undo" "Base13" pt4.toString-- Undo insert "3" -> Base1let (u1, _) := pt4.undo 0assertEqual "Undo 3" "Base1" u1.toString-- Undo delete "2" -> Base12let (u2, _) := u1.undo 0assertEqual "Undo delete 2" "Base12" u2.toString-- Undo insert "2" -> Base1let (u3, _) := u2.undo 0assertEqual "Undo insert 2" "Base1" u3.toString-- Undo insert "1" -> Baselet (u4, _) := u3.undo 0assertEqual "Undo insert 1" "Base" u4.toStringdef test : IO Unit := doIO.println "Starting PieceTable Undo/Redo Test..."testUndoRedotestComplexUndoIO.println "PieceTable Undo/Redo Test passed!"end Test.PieceTable.UndoRedo
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.UIimport ViE.Configimport Test.Utilsnamespace Test.SearchHighlightopen Test.Utilsopen ViEdef test : IO Unit := doIO.println "Starting Search Highlight Test..."let hitRanges : Array (Nat × Nat) := #[(0, 4), (10, 14), (20, 24)]assertEqual "Active match picks cursor candidate" (some (10, 14)) (ViE.UI.activeMatchRange hitRanges (some 11))assertEqual "No active match when cursor is outside all matches" none (ViE.UI.activeMatchRange hitRanges (some 5))assertEqual "No active match when cursor is none" none (ViE.UI.activeMatchRange hitRanges none)let active := ViE.UI.activeMatchRange hitRanges (some 11)let activeHead :=match active with| some m => ViE.UI.overlapsByteRange m 0 1| none => falselet activeBody :=match active with| some m => ViE.UI.overlapsByteRange m 11 12| none => falseassertEqual "Active candidate does not color other matches" false activeHeadassertEqual "Active candidate colors only its own range" true activeBodylet s0 := ViE.startOrUpdateSearch ViE.initialState "abc" .forward falselet (m1, s1) := ViE.UI.getLineSearchMatches s0 s0.getActiveBuffer.id 0 "abc abc"assertEqual "Line search cache computes matches" true (m1.size > 0)let cachedBeforeSwitch :=match s1.searchState with| some ss => ss.lineMatches.contains 0| none => falseassertEqual "Line search cache populated" true cachedBeforeSwitchlet s2 := ViE.UI.ensureSearchLineCacheForBuffer s1 9999let cacheClearedOnSwitch :=match s2.searchState with| some ss => ss.lineMatches.isEmpty && ss.lineCacheBufferId == some 9999| none => falseassertEqual "Line search cache resets on buffer switch" true cacheClearedOnSwitchlet t0 := 1000let (p0, k0) := ViE.parseKey ViE.initialState '\x1b' t0let (p1, k1) := ViE.parseKey p0 '[' (t0 + 1)let (p2, k2) := ViE.parseKey p1 '1' (t0 + 2)let (p3, k3) := ViE.parseKey p2 ';' (t0 + 3)let (p4, k4) := ViE.parseKey p3 '2' (t0 + 4)let (p5, k5) := ViE.parseKey p4 'X' (t0 + 5)assertEqual "Unknown CSI emits no keys while pending" ([] : List Key) (k0 ++ k1 ++ k2 ++ k3 ++ k4)assertEqual "Unknown CSI final emits Key.unknown" [Key.unknown 'X'] k5assertEqual "Unknown CSI clears pending state" "" p5.inputState.pendingKeyslet sInc0 := ViE.startOrUpdateSearch ViE.initialState "abc" .forward falselet sInc1 := {sInc0 withmode := .searchForwardinputState := { sInc0.inputState with commandBuffer := "" }}let sInc2 := ViE.runIncrementalSearch sInc1let incCleared :=match sInc2.searchState with| none => true| some _ => falseassertEqual "Incremental search with empty pattern clears highlight state" true incClearedlet chars : List Char := ['\x1b', '[', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3']let (pLong, emitted) := chars.foldl(fun (acc : EditorState × List Key) ch =>let (st, out) := acclet (st', keys) := ViE.parseKey st ch (t0 + 20)(st', out ++ keys))(ViE.initialState, [])assertEqual "Overlong unknown CSI flushes emitted keys" true (!emitted.isEmpty)assertEqual "Overlong unknown CSI clears pending state" "" pLong.inputState.pendingKeyslet (_, tabKeys) := ViE.parseKey ViE.initialState '\x09' (t0 + 30)assertEqual "Tab parses as char tab" [Key.char '\t'] tabKeysend Test.SearchHighlight
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Typesimport ViE.Buffer.Contentimport ViE.State.Layoutimport ViE.State.Movementnamespace Test.Utils/-- Assert condition is true, throw error if false -/def 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}")/-- Assert equality with debug output -/def assertEqual [Repr α] [BEq α] (msg : String) (expected actual : α) : IO Unit := doif expected == actual thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"IO.println s!" Expected: {repr expected}"IO.println s!" Actual: {repr actual}"throw (IO.userError s!"Assertion failed: {msg}")/-- Assert EditorState buffer content matches expected string -/def assertBuffer (msg : String) (est : ViE.EditorState) (expected : String) : IO Unit := dolet actual := est.getActiveBuffer.table.toStringif actual == expected thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"IO.println s!" Expected: '{expected}'"IO.println s!" Actual: '{actual}'"throw (IO.userError s!"Buffer content assertion failed: {msg}")/-- Assert EditorState cursor position matches expected row/col -/def assertCursor (msg : String) (est : ViE.EditorState) (r c : Nat) : IO Unit := dolet cursor := est.getCursorif cursor.row.val == r && cursor.col.val == c thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"IO.println s!" Expected: ({r}, {c})"IO.println s!" Actual: ({cursor.row.val}, {cursor.col.val})"throw (IO.userError s!"Cursor assertion failed: {msg}")end Test.Utils
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViEimport Test.Utilsnamespace Test.Lspopen Test.Utilsdef test : IO Unit := doIO.println "Starting LSP Test..."let leanBuf := { ViE.initialBuffer with filename := some "Main.lean" }let txtBuf := { ViE.initialBuffer with filename := some "notes.txt" }assertEqual "isLeanBuffer true for .lean" true (ViE.Lsp.Lean.isLeanBuffer leanBuf)assertEqual "isLeanBuffer false for non-.lean" false (ViE.Lsp.Lean.isLeanBuffer txtBuf)let uriAbs := ViE.Lsp.Lean.fileUri "/tmp/a b#c%.lean"assertEqual "fileUri percent-encodes reserved bytes" "file:///tmp/a%20b%23c%25.lean" uriAbslet uriRel := ViE.Lsp.Lean.fileUri "tmp/test.lean"assertEqual "fileUri normalizes relative path with leading slash" "file:///tmp/test.lean" uriRellet okResp : Lean.Json := Lean.Json.mkObj [("id", (12 : Lean.Json)), ("result", Lean.Json.null)]let errResp : Lean.Json := Lean.Json.mkObj [("id", (13 : Lean.Json)), ("error", Lean.Json.mkObj [("code", (1 : Lean.Json))])]let notif : Lean.Json := Lean.Json.mkObj [("method", .str "textDocument/publishDiagnostics"), ("params", Lean.Json.mkObj [])]assertEqual "responseId? parses result response id" (some 12) (ViE.Lsp.Lean.responseId? okResp)assertEqual "responseId? parses error response id" (some 13) (ViE.Lsp.Lean.responseId? errResp)assertEqual "responseId? is none for notifications" (none : Option Nat) (ViE.Lsp.Lean.responseId? notif)assertEqual "isResponseForRequest true for same id" true (ViE.Lsp.Lean.isResponseForRequest okResp 12)assertEqual "isResponseForRequest false for different id" false (ViE.Lsp.Lean.isResponseForRequest okResp 99)let s0 := ViE.initialStatelet sStatus ← ViE.Lsp.Lean.cmdLsp ["status"] s0assertEqual "lsp status reports stopped when no session" "LSP: stopped" sStatus.messagelet sStop ← ViE.Lsp.Lean.cmdLsp ["stop"] s0assertEqual "lsp stop reports not running when no session" "LSP is not running" sStop.messageIO.println "LSP Test passed!"end Test.Lsplet uriWs ← ViE.Lsp.Lean.fileUriWithWorkspace (some "/tmp/ws") "dir/a b.lean"assertEqual "fileUriWithWorkspace resolves relative path from workspace root" "file:///tmp/ws/dir/a%20b.lean" uriWslet uriWsAbs ← ViE.Lsp.Lean.fileUriWithWorkspace (some "/tmp/ws") "/tmp/alt/x.lean"assertEqual "fileUriWithWorkspace keeps absolute path priority" "file:///tmp/alt/x.lean" uriWsAbs
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))
module Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show values
#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}
import Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sample
let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))let rec fib n =if n <= 1 then nelse fib (n - 1) + fib (n - 2)let () =let values = List.init 16 fib inPrintf.printf "fib(0..15) = %s\n" (String.concat ", " (List.map string_of_int values))
module Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show valuesmodule Main wherefib :: Int -> Intfib 0 = 0fib 1 = 1fib n = fib (n - 1) + fib (n - 2)main :: IO ()main = dolet values = map fib [0..15]putStrLn $ "fib(0..15) = " ++ show values
#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}
import Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sample
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}#include <stdio.h>#include <stdlib.h>static int is_even(int n) {return n % 2 == 0;}int main(void) {int sum = 0;for (int i = 0; i < 100; i++) {if (is_even(i)) sum += i;}printf("sum of even numbers < 100: %d\n", sum);return 0;}
import Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sampleimport Stdnamespace Sampledef fib : Nat → Nat| 0 => 0| 1 => 1| n + 2 => fib (n + 1) + fib ndef isEven : Nat → Bool| 0 => true| 1 => false| n + 2 => isEven ndef main : IO Unit := dolet values := (List.range 20).map fibIO.println s!"fib(0..19) = {values}"IO.println s!"isEven 42 = {isEven 42}"end Sample
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)(define (fib n)(if (< n 2)n(+ (fib (- n 1)) (fib (- n 2)))))(display "fib(0..10) = ")(display (map fib (iota 11)))(newline)
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididuntut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitationullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor inreprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteursint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.State.Layoutimport ViE.Workspaceimport ViE.Command.Implimport ViE.Appimport ViE.Buffer.Contentimport ViE.Basicimport ViETest.Utilsset_option maxRecDepth 2048namespace ViETest.Workspaceopen ViETest.Utilsopen ViEdef addWorkspace (state : EditorState) (ws : WorkspaceState) : EditorState :=state.updateCurrentWorkgroup fun wg =>{ wg with workspaces := wg.workspaces.push ws }def switchWorkspace (state : EditorState) (idx : Nat) : EditorState :=state.updateCurrentWorkgroup fun wg =>if idx < wg.workspaces.size then{ wg with currentWorkspace := idx }elsewgdef test : IO Unit := doIO.println "Starting Workspace ViETest..."let s0 := { ViE.initialState with windowHeight := 40, windowWidth := 120 }let wg0 := s0.getCurrentWorkgroupassertEqual "Initial workgroups size" 10 s0.workgroups.sizeassertEqual "Initial workgroup count" 1 wg0.workspaces.sizeassertEqual "Initial workspace index" 0 wg0.currentWorkspaceassertEqual "Initial workspace name" "ViE" s0.getCurrentWorkspace.namelet ws1 := makeWorkspaceState "Project A" (some "/tmp/project-a") 10let s1 := addWorkspace s0 ws1let wg1 := s1.getCurrentWorkgroupassertEqual "Workgroup has 2 workspaces" 2 wg1.workspaces.sizelet s2 := switchWorkspace s1 1assertEqual "Switched to workspace 1" "Project A" s2.getCurrentWorkspace.nameassertEqual "Active buffer comes from workspace 1" 10 (s2.getActiveBuffer.id)let resolved := s2.getCurrentWorkspace.resolvePath "file.txt"assertEqual "ResolvePath uses workspace root" "/tmp/project-a/file.txt" resolvedlet resolvedAbs := s2.getCurrentWorkspace.resolvePath "/abs/file.txt"assertEqual "ResolvePath keeps absolute path" "/abs/file.txt" resolvedAbsIO.println "Starting Workspace Command ViETest..."let s3 ← ViE.Command.cmdWs ["new", "Project B", "/tmp/project-b"] s2assertEqual "Workspace created and switched" "Project B" s3.getCurrentWorkspace.nameassertEqual "Workspace rootPath set" (some "/tmp/project-b") s3.getCurrentWorkspace.rootPathassertEqual "Active buffer id is new workspace buffer" 11 s3.getActiveBuffer.idlet s4 ← ViE.Command.cmdWs ["rename", "Project B2"] s3assertEqual "Workspace rename applied" "Project B2" s4.getCurrentWorkspace.namelet s5 ← ViE.Command.cmdWs ["new", "Project C"] s4assertEqual "Workspace C created" "Project C" s5.getCurrentWorkspace.namelet s6 ← ViE.Command.cmdWs ["prev"] s5assertEqual "Workspace prev switches back" "Project B2" s6.getCurrentWorkspace.namelet s7 ← ViE.Command.cmdWs ["0"] s6assertEqual "Workspace switch by index" "ViE" s7.getCurrentWorkspace.namelet s8 ← ViE.Command.cmdWs ["list"] s7assertEqual "Workspace list opens explorer buffer" (some "explorer://workgroup") s8.getActiveBuffer.filenamelet ws8 := s8.getCurrentWorkspaceassertEqual "Workspace explorer opens in regular window" false (ws8.isFloatingWindow ws8.activeWindowId)let wsListText := s8.getActiveBuffer.table.toStringassertEqual "Workspace list contains New Workspace entry" true (wsListText.contains "[New Workspace]")assertEqual "Workspace list contains Rename Workspace entry" true (wsListText.contains "[Rename Workspace]")assertEqual "Workspace list contains Project B2" true (wsListText.contains "Project B2")assertEqual "Workspace list marks current workspace" true (wsListText.contains "*")let s8a := s8.updateActiveView fun v => { v with cursor := {row := ⟨2⟩, col := 0} }let s8b ← ViE.Feature.handleExplorerEnter s8aassertEqual "New workspace opens floating input" true s8b.floatingOverlay.isSomeassertEqual "New workspace floating command prefix" (some "ws new ") s8b.floatingInputCommandlet s8c := s8.updateActiveView fun v => { v with cursor := {row := ⟨3⟩, col := 0} }let s8d ← ViE.Feature.handleExplorerEnter s8cassertEqual "Rename workspace opens floating input" true s8d.floatingOverlay.isSomeassertEqual "Rename workspace floating command prefix" (some "ws rename ") s8d.floatingInputCommandlet s8e ← ViE.Command.cmdWorkspace ["rename", ""] s8assertEqual "Rename workspace rejects empty" "Workspace name cannot be empty" s8e.messagelet s8f ← ViE.Command.cmdWorkspace ["rename", "Project B2"] s8assertEqual "Rename workspace rejects duplicate" true (s8f.message.contains "already exists")let s9 ← ViE.Command.cmdWs ["open", "/tmp/project-d"] s8assertEqual "Workspace open uses path name" "project-d" s9.getCurrentWorkspace.nameassertEqual "Workspace open rootPath set" (some "/tmp/project-d") s9.getCurrentWorkspace.rootPathlet s10 ← ViE.Command.cmdWs ["open", "--name", "Project E", "/tmp/project-e"] s9assertEqual "Workspace open --name prefix" "Project E" s10.getCurrentWorkspace.nameassertEqual "Workspace open --name prefix rootPath" (some "/tmp/project-e") s10.getCurrentWorkspace.rootPathlet s11 ← ViE.Command.cmdWs ["open", "/tmp/project-f", "--name", "Project F"] s10assertEqual "Workspace open --name suffix" "Project F" s11.getCurrentWorkspace.nameassertEqual "Workspace open --name suffix rootPath" (some "/tmp/project-f") s11.getCurrentWorkspace.rootPathIO.println "Starting Workspace Relative Path Resolution ViETest..."let stamp ← IO.monoMsNowlet relBase := s!"/tmp/vie-ws-rel-{stamp}"IO.FS.createDirAll (System.FilePath.mk relBase)let sRel0 := s11.updateCurrentWorkspace fun ws =>{ ws with rootPath := some relBase, name := "RelBase" }let sRel1 ← ViE.Command.cmdCd ["child"] sRel0assertEqual "cd relative resolves from workspace root" (some s!"{relBase}/child") sRel1.getCurrentWorkspace.rootPathlet sRel2 ← ViE.Command.cmdWs ["open", "nested"] sRel0assertEqual "ws open relative resolves from workspace root" (some s!"{relBase}/nested") sRel2.getCurrentWorkspace.rootPathassertEqual "ws open relative uses basename from absolute path" "nested" sRel2.getCurrentWorkspace.namelet sRel3 ← ViE.Command.cmdWs ["new", "RelChild", "child2"] sRel0assertEqual "ws new relative resolves from workspace root" (some s!"{relBase}/child2") sRel3.getCurrentWorkspace.rootPathlet s12 ← ViE.Command.cmdWs ["close"] s11assertEqual "Workspace close switches to previous" "Project E" s12.getCurrentWorkspace.nameIO.println "Starting Workspace Explorer Preview ViETest..."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.getCurrentWorkspaceassertEqual "Workspace explorer preview window is regular" false (wsPrev.isFloatingWindow previewWinId)let pairSideBySide :=match (ViE.Window.getAllWindowBounds wsPrev.layout (if s12c.windowHeight > 0 then s12c.windowHeight - 1 else 0) s12c.windowWidth).find? (fun (id, _, _, _, _) => id == wsPrev.activeWindowId),(ViE.Window.getAllWindowBounds wsPrev.layout (if s12c.windowHeight > 0 then s12c.windowHeight - 1 else 0) s12c.windowWidth).find? (fun (id, _, _, _, _) => id == previewWinId) with| some (_, et, el, eh, ew), some (_, pt, pl, ph, pw) =>et == pt && eh == ph && ((el + ew <= pl) || (pl + pw <= el))| _, _ => falseassertEqual "Workspace explorer/preview pair is side-by-side" true pairSideBySidelet 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")IO.println "Starting Workspace Restore ViETest..."let bufA : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 0 none #[stringToLine "WS-A"]let bufB : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 10 none #[stringToLine "WS-B"]let wsA : WorkspaceState := {name := "WS-A"rootPath := nonebuffers := [bufA]nextBufferId := 1layout := .window 0 { initialView with bufferId := 0 }activeWindowId := 0nextWindowId := 1}let wsB : WorkspaceState := {name := "WS-B"rootPath := nonebuffers := [bufB]nextBufferId := 11layout := .window 0 { initialView with bufferId := 10 }activeWindowId := 0nextWindowId := 1}let s12a := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsB], currentWorkspace := 0 }let s12b ← ViE.Feature.openWorkspaceExplorer s12alet s12c := s12b.updateActiveView fun v => { v with cursor := {row := ⟨5⟩, col := 0} }let s12d ← ViE.Feature.handleExplorerEnter s12cassertEqual "Workspace selection switches current workspace" "WS-B" s12d.getCurrentWorkspace.nameassertEqual "Workspace selection restores active buffer" "WS-B" s12d.getActiveBuffer.table.toStringIO.println "Starting Workspace Restore Layout ViETest..."let bufC : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 20 none #[stringToLine "WS-C1"]let bufD : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 21 none #[stringToLine "WS-C2"]let layoutC : Layout :=.hsplit(.window 0 { initialView with bufferId := 20 })(.window 1 { initialView with bufferId := 21, cursor := ⟨⟨0⟩, ⟨2⟩⟩ })0.3let wsC : WorkspaceState := {name := "WS-C"rootPath := nonebuffers := [bufC, bufD]nextBufferId := 22layout := layoutCactiveWindowId := 1nextWindowId := 2}let s12e := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsC], currentWorkspace := 0 }let s12f ← ViE.Feature.openWorkspaceExplorer s12elet s12g := s12f.updateActiveView fun v => { v with cursor := {row := ⟨5⟩, col := 0} }let s12h ← ViE.Feature.handleExplorerEnter s12gassertEqual "Workspace layout restore (active buffer)" "WS-C2" s12h.getActiveBuffer.table.toStringlet ratio := match s12h.getCurrentWorkspace.layout with| .hsplit _ _ r => r| _ => 0.0assertEqual "Workspace layout restore (ratio)" 0.3 ratiolet cursor := s12h.getCursorassertEqual "Workspace layout restore (cursor row)" 0 cursor.row.valassertEqual "Workspace layout restore (cursor col)" 2 cursor.col.valIO.println "Starting Workspace Restore VSplit ViETest..."let bufE : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 30 none #[stringToLine "WS-D1"]let bufF : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 31 none #[stringToLine "WS-D2"]let layoutD : Layout :=.vsplit(.window 0 { initialView with bufferId := 30, cursor := ⟨⟨1⟩, ⟨0⟩⟩, scrollRow := ⟨1⟩, scrollCol := ⟨2⟩ })(.window 1 { initialView with bufferId := 31, cursor := ⟨⟨2⟩, ⟨3⟩⟩, scrollRow := ⟨0⟩, scrollCol := ⟨1⟩ })0.7let wsD : WorkspaceState := {name := "WS-D"rootPath := nonebuffers := [bufE, bufF]nextBufferId := 32layout := layoutDactiveWindowId := 1nextWindowId := 2}let s12i := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsC, wsD], currentWorkspace := 0 }let s12j ← ViE.Feature.openWorkspaceExplorer s12ilet s12k := s12j.updateActiveView fun v => { v with cursor := {row := ⟨6⟩, col := 0} }let s12l ← ViE.Feature.handleExplorerEnter s12kassertEqual "Workspace vsplit restore (active buffer)" "WS-D2" s12l.getActiveBuffer.table.toStringlet ratio2 := match s12l.getCurrentWorkspace.layout with| .vsplit _ _ r => r| _ => 0.0assertEqual "Workspace vsplit restore (ratio)" 0.7 ratio2let cursor2 := s12l.getCursorassertEqual "Workspace vsplit restore (cursor row)" 2 cursor2.row.valassertEqual "Workspace vsplit restore (cursor col)" 3 cursor2.col.vallet (sRow, sCol) := s12l.getScrollassertEqual "Workspace vsplit restore (scroll row)" 0 sRow.valassertEqual "Workspace vsplit restore (scroll col)" 1 sCol.valIO.println "Starting Workspace Multi-View Restore ViETest..."let bufG : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 40 none #[stringToLine "WS-E1"]let bufH : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 41 none #[stringToLine "WS-E2"]let bufI : FileBuffer := ViE.Buffer.fileBufferFromTextBuffer 42 none #[stringToLine "WS-E3"]let layoutE : Layout :=.hsplit(.vsplit(.window 0 { initialView with bufferId := 40, cursor := ⟨⟨0⟩, ⟨1⟩⟩, scrollRow := ⟨0⟩, scrollCol := ⟨0⟩ })(.window 1 { initialView with bufferId := 41, cursor := ⟨⟨2⟩, ⟨2⟩⟩, scrollRow := ⟨1⟩, scrollCol := ⟨1⟩ })0.4)(.window 2 { initialView with bufferId := 42, cursor := ⟨⟨3⟩, ⟨0⟩⟩, scrollRow := ⟨2⟩, scrollCol := ⟨2⟩ })0.6let wsE : WorkspaceState := {name := "WS-E"rootPath := nonebuffers := [bufG, bufH, bufI]nextBufferId := 43layout := layoutEactiveWindowId := 1nextWindowId := 3}let s12m := s12.updateCurrentWorkgroup fun wg =>{ wg with workspaces := #[wsA, wsC, wsD, wsE], currentWorkspace := 0 }let s12n ← ViE.Feature.openWorkspaceExplorer s12mlet s12o := s12n.updateActiveView fun v => { v with cursor := {row := ⟨7⟩, col := 0} }let s12p ← ViE.Feature.handleExplorerEnter s12oassertEqual "Multi-view restore (active buffer)" "WS-E2" s12p.getActiveBuffer.table.toStringlet (sr2, sc2) := s12p.getScrollassertEqual "Multi-view restore (scroll row)" 1 sr2.valassertEqual "Multi-view restore (scroll col)" 1 sc2.vallet cursor3 := s12p.getCursorassertEqual "Multi-view restore (cursor row)" 2 cursor3.row.valassertEqual "Multi-view restore (cursor col)" 2 cursor3.col.valassertEqual "Multi-view restore (layout hsplit ratio)" 0.6 (match s12p.getCurrentWorkspace.layout with | .hsplit _ _ r => r | _ => 0.0)let subRatio := match s12p.getCurrentWorkspace.layout with| .hsplit left _ _ =>match left with| .vsplit _ _ r => r| _ => 0.0| _ => 0.0assertEqual "Multi-view restore (layout vsplit ratio)" 0.4 subRatioIO.println "Starting Workspace Startup Target ViETest..."let base := "ViETest/test_paths"let absBase ← ViE.resolveAbsolutePath none baselet dirOnly := s!"{base}/dir0"let absDirOnly ← ViE.resolveAbsolutePath none dirOnlylet (wsDirOnly, fileDirOnly) ← ViE.resolveStartupTarget (some dirOnly)assertEqual "Directory arg sets workspace" (some absDirOnly) wsDirOnlyassertEqual "Directory arg has no file" none fileDirOnlylet fileNested := s!"{base}/dir0/dir1/dir2/file0.txt"let absFileNested ← ViE.resolveAbsolutePath none fileNestedlet nestedParent := match (System.FilePath.mk absFileNested).parent with| some p => p.toString| none => "/"let (wsFileNested, fileFileNested) ← ViE.resolveStartupTarget (some fileNested)assertEqual "File arg sets workspace to parent dir" (some nestedParent) wsFileNestedassertEqual "File arg keeps filename" (some absFileNested) fileFileNestedlet fileTop := s!"{base}/file0.txt"let absFileTop ← ViE.resolveAbsolutePath none fileToplet (wsFileTop, fileFileTop) ← ViE.resolveStartupTarget (some fileTop)assertEqual "Top-level file sets workspace to base" (some absBase) wsFileTopassertEqual "Top-level file keeps filename" (some absFileTop) fileFileToplet newNested := s!"{base}/newdir/newfile.txt"let absNewNested ← ViE.resolveAbsolutePath none newNestedlet newNestedParent := match (System.FilePath.mk absNewNested).parent with| some p => p.toString| none => "/"let (wsNewNested, fileNewNested) ← ViE.resolveStartupTarget (some newNested)assertEqual "Nonexistent file sets workspace to parent dir" (some newNestedParent) wsNewNestedassertEqual "Nonexistent file keeps absolute filename" (some absNewNested) fileNewNestedIO.println "Starting Explorer Path Resolution ViETest..."let absTest ← ViE.resolveAbsolutePath none "ViETest"let s13 := s12.updateCurrentWorkspace fun ws =>{ ws with rootPath := some absTest, name := "Test" }let s14 ← ViE.Feature.openExplorer s13 "."assertEqual "Explorer opens absolute workspace root from dot" (some s!"explorer://{absTest}") s14.getActiveBuffer.filenamelet s15 ← ViE.Feature.openExplorer s13 "test_paths"assertEqual "Explorer resolves relative path from absolute workspace root" (some s!"explorer://{absTest}/test_paths") s15.getActiveBuffer.filenameIO.println "WorkspaceTest passed!"end ViETest.Workspace
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.State.Layoutimport ViE.Command.Explorerimport ViE.State.Configimport ViE.Command.Implimport ViETest.Utilsnamespace ViETest.WorkgroupExploreropen ViETest.Utilsopen ViEdef addWorkspace (state : EditorState) (ws : WorkspaceState) : EditorState :=state.updateCurrentWorkgroup fun wg =>{ wg with workspaces := wg.workspaces.push ws }def addWorkgroup (state : EditorState) (name : String) : EditorState :=let nextBufId := state.workgroups.foldl (fun m g =>g.workspaces.foldl (fun m2 ws => max m2 ws.nextBufferId) m) 0let newWg := createEmptyWorkgroup name nextBufId{ state with workgroups := state.workgroups.push newWg }def setCursorRow (state : EditorState) (row : Nat) : EditorState :=state.updateActiveView fun v => { v with cursor := { v.cursor with row := ⟨row⟩ } }def test : IO Unit := doIO.println "Starting Workgroup Explorer ViETest..."let s0 := { ViE.initialState with windowHeight := 40, windowWidth := 120 }let ws1 := makeWorkspaceState "Project A" (some "/tmp/project-a") 10let ws2 := makeWorkspaceState "Project B" (some "/tmp/project-b") 20let s1 := addWorkspace (addWorkspace s0 ws1) ws2let s1 := addWorkgroup s1 "Group B"let s1 := addWorkgroup s1 "Group C"let s2 ← ViE.Command.cmdWg ["list"] s1assertEqual "Explorer buffer is active" (some "explorer://workgroups") s2.getActiveBuffer.filenamelet ws2 := s2.getCurrentWorkspaceassertEqual "Workgroup explorer opens in regular window" false (ws2.isFloatingWindow ws2.activeWindowId)let bufText := s2.getActiveBuffer.table.toStringassertEqual "Explorer contains Workgroup header" true (bufText.contains "Workgroup Explorer")assertEqual "Explorer contains New Workgroup entry" true (bufText.contains "[New Workgroup]")assertEqual "Explorer contains Rename Workgroup entry" true (bufText.contains "[Rename Workgroup]")assertEqual "Explorer contains Group 0" true (bufText.contains "0")assertEqual "Explorer contains Group B" true (bufText.contains "Group B")assertEqual "Explorer marks current workgroup" true (bufText.contains "*")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.getCurrentWorkspaceassertEqual "Workgroup explorer preview window is regular" false (wsPrev.isFloatingWindow previewWinId)let pairSideBySide :=match (ViE.Window.getAllWindowBounds wsPrev.layout (if s2.windowHeight > 0 then s2.windowHeight - 1 else 0) s2.windowWidth).find? (fun (id, _, _, _, _) => id == wsPrev.activeWindowId),(ViE.Window.getAllWindowBounds wsPrev.layout (if s2.windowHeight > 0 then s2.windowHeight - 1 else 0) s2.windowWidth).find? (fun (id, _, _, _, _) => id == previewWinId) with| some (_, et, el, eh, ew), some (_, pt, pl, ph, pw) =>et == pt && eh == ph && ((el + ew <= pl) || (pl + pw <= el))| _, _ => falseassertEqual "Workgroup explorer/preview pair is side-by-side" true pairSideBySidelet 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")let s3 := setCursorRow s2 2let s4 ← ViE.Feature.handleExplorerEnter s3assertEqual "New workgroup opens floating input" true s4.floatingOverlay.isSomeassertEqual "New workgroup floating command prefix" (some "wg new ") s4.floatingInputCommandlet s3b := setCursorRow s2 3let s4b ← ViE.Feature.handleExplorerEnter s3bassertEqual "Rename workgroup opens floating input" true s4b.floatingOverlay.isSomeassertEqual "Rename workgroup floating command prefix" (some "wg rename ") s4b.floatingInputCommandlet s4c ← ViE.Command.cmdWg ["rename", ""] s2assertEqual "Rename workgroup rejects empty" "Workgroup name cannot be empty" s4c.messagelet s4d ← ViE.Command.cmdWg ["rename", "Group B"] s2assertEqual "Rename workgroup rejects duplicate" true (s4d.message.contains "already exists")let s5 ← ViE.Command.cmdWg ["list"] s1let s6 := setCursorRow s5 5let s7 ← ViE.Feature.handleExplorerEnter s6assertEqual "Switched to workgroup index 1" 1 s7.currentGroupassertEqual "Switch restores workgroup layout" true (s7.getActiveBuffer.filename != some "explorer://workgroup")IO.println "WorkgroupExplorerTest passed!"end ViETest.WorkgroupExplorer
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Typesimport ViE.Buffer.Contentimport ViE.State.Layoutimport ViE.State.Movementnamespace ViETest.Utils/-- Assert condition is true, throw error if false -/def 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}")/-- Assert equality with debug output -/def assertEqual [Repr α] [BEq α] (msg : String) (expected actual : α) : IO Unit := doif expected == actual thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"IO.println s!" Expected: {repr expected}"IO.println s!" Actual: {repr actual}"throw (IO.userError s!"Assertion failed: {msg}")/-- Assert EditorState buffer content matches expected string -/def assertBuffer (msg : String) (est : ViE.EditorState) (expected : String) : IO Unit := dolet actual := est.getActiveBuffer.table.toStringif actual == expected thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"IO.println s!" Expected: '{expected}'"IO.println s!" Actual: '{actual}'"throw (IO.userError s!"Buffer content assertion failed: {msg}")/-- Assert EditorState cursor position matches expected row/col -/def assertCursor (msg : String) (est : ViE.EditorState) (r c : Nat) : IO Unit := dolet cursor := est.getCursorif cursor.row.val == r && cursor.col.val == c thenIO.println s!"[PASS] {msg}"elseIO.println s!"[FAIL] {msg}"IO.println s!" Expected: ({r}, {c})"IO.println s!" Actual: ({cursor.row.val}, {cursor.col.val})"throw (IO.userError s!"Cursor assertion failed: {msg}")end ViETest.Utils
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Data.PieceTableopen ViEnamespace ViETest.Undodef 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 := doIO.println "Starting Undo/Redo ViETest..."-- 1. Create initial bufferlet initialText := "Hello, World!"let pt0 := PieceTable.fromString initialTextassert "Initial text correct" (pt0.toString == initialText)assert "Initial undo stack empty" (pt0.undoStack.isEmpty)-- 2. Insert " New" at end-- "Hello, World! New"let pt1 := pt0.insert 13 " New" 13assert "Text after insert" (pt1.toString == "Hello, World! New")assert "Undo stack has 1 item" (pt1.undoStack.length == 1)-- 3. Delete ", World"-- "Hello! New"-- "Hello, World!" -> offset 5, delete 7 (", World")let pt2 := pt1.delete 5 7 5assert "Text after delete" (pt2.toString == "Hello! New")assert "Undo stack has 2 items" (pt2.undoStack.length == 2)-- 4. Undo Modify (Delete)-- Should revert to "Hello, World! New"let (pt3, _) := pt2.undo 0assert "Text after undo delete" (pt3.toString == "Hello, World! New")assert "Undo stack has 1 item" (pt3.undoStack.length == 1)assert "Redo stack has 1 item" (pt3.redoStack.length == 1)-- 5. Undo Insert-- Should revert to "Hello, World!"let (pt4, _) := pt3.undo 0assert "Text after undo insert" (pt4.toString == "Hello, World!")assert "Undo stack has 0 items" (pt4.undoStack.isEmpty)assert "Redo stack has 2 items" (pt4.redoStack.length == 2)-- 6. Redo Insert-- Should go to "Hello, World! New"let (pt5, _) := pt4.redo 0assert "Text after redo insert" (pt5.toString == "Hello, World! New")assert "Undo stack has 1 item" (pt5.undoStack.length == 1)assert "Redo stack has 1 item" (pt5.redoStack.length == 1)-- 7. Redo Delete-- Should go to "Hello! New"let (pt6, _) := pt5.redo 0assert "Text after redo delete" (pt6.toString == "Hello! New")assert "Undo stack has 2 items" (pt6.undoStack.length == 2)assert "Redo stack empty" (pt6.redoStack.isEmpty)-- 8. Test Redo clearing on new editlet (pt7, _) := pt6.undo 0 -- "Hello, World! New"-- Insert " Again" at end -> "Hello, World! New Again"let pt8 := pt7.insert 17 " Again" 17assert "Text after new insert" (pt8.toString == "Hello, World! New Again")assert "Redo stack cleared" (pt8.redoStack.isEmpty)-- 9. Test Optimization (Grouping)-- Force break merge chain from previous step to ensure " 1" starts a new grouplet pt8_clean := { pt8 with lastInsert := none }let pt9 := pt8_clean.insert 23 " 1" 23 -- "Hello, World! New Again 1"let pt10 := pt9.insert 25 "2" 25 -- "Hello, World! New Again 12"let pt11 := pt10.insert 26 "3" 26 -- "Hello, World! New Again 123"assert "Text after group insert" (pt11.toString == "Hello, World! New Again 123")-- Should have only 1 new undo item for " 123" because they overlap-- pt8 had some undo stack. pt9 added 1. pt10 merged. pt11 merged.-- So pt11.undoStack.length should be pt8.undoStack.length + 1assert "Undo stack grouped" (pt11.undoStack.length == pt8.undoStack.length + 1)let (pt12, _) := pt11.undo 0assert "Undo removes group" (pt12.toString == "Hello, World! New Again")-- 10. Test Limit-- Force a small limitlet ptLimit := { pt12 with undoLimit := 2 }let ptL1 := (ptLimit.insert 0 "A" 0) -- Stack: 1let ptL2 := (ptL1.insert 0 "B" 0) -- Stack: 2-- Previous insert was "A" at 0. LastInsert = (0 + 1, addOffset + 1).-- Current insert at 0. 0 != 1. So NOT contiguous. Correct.let ptL3 := (ptL2.insert 0 "C" 0) -- Stack: 3 -> capped to 2assert "Undo limit respected" (ptL3.undoStack.length == 2)-- The oldest undo (for "A") should be dropped. The remaining undos are "C" -> "B", and "B" -> "A".-- So undoing twice should start with "CBA..." -> "BA..." -> "A..."let (u1, _) := ptL3.undo 0let (u2, _) := u1.undo 0assert "Oldest undo dropped" (u2.toString == "AHello, World! New Again")-- 11. Test Paste Undo Grouping (Reproduction)-- Scenario: Paste "P1" then Paste "P2". They should NOT merge if we signal a break,-- OR if we consider pastes as distinct operations that shouldn't auto-merge like typing.-- Currently PieceTable merges ANY contiguous insert.let ptBase := PieceTable.fromString ""let ptP1 := ptBase.insert 0 "P1" 0 -- Simulate Paste 1-- Simulate EditorState.paste calling commit before next insertlet ptP1_committed := ptP1.commitlet ptP2 := ptP1_committed.insert 2 "P2" 2 -- Simulate Paste 2 (contiguous)assert "Text is P1P2" (ptP2.toString == "P1P2")-- Expectation: 2 undo items (one for P1, one for P2).-- Bug: they merge into 1 item because offset 2 == lastInsert end.if ptP2.undoStack.length == 1 thenIO.println "[FAIL] Paste operations merged (expected 2 undo items)"assert "Paste should not merge" falseelseIO.println "[PASS] Paste operations distinct (2 undo items)"IO.println "TestUndo passed!"end ViETest.Undo
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViETest.TreeStatsdef main (args : List String) : IO Unit :=ViETest.TreeStats.main args
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Data.PieceTableimport ViETest.Utilsnamespace ViETest.TreeStatsopen ViEopen ViETest.Utilsdef buildWorkload (iterations : Nat) : PieceTable := Id.run dolet mut pt := PieceTable.fromString "Initial content\n"for i in [0:iterations] dopt := pt.insert pt.tree.length s!"line {i}\n" pt.tree.lengthreturn ptmutualdef countNodes (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ _ => 1 + countNodesList cs.toListdef countNodesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => countNodes t + countNodesList restendmutualdef countLeaves (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ _ => countLeavesList cs.toListdef countLeavesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => countLeaves t + countLeavesList restendmutualdef maxLeafPieces (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf ps _ _ => ps.size| .internal cs _ _ _ => maxLeafPiecesList cs.toListdef maxLeafPiecesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => max (maxLeafPieces t) (maxLeafPiecesList rest)endmutualdef maxInternalChildren (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 0| .internal cs _ _ _ =>max cs.size (maxInternalChildrenList cs.toList)def maxInternalChildrenList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => max (maxInternalChildren t) (maxInternalChildrenList rest)enddef parseIterations (args : List String) : Nat :=let args :=match args with| "--" :: rest => rest| _ => argsmatch args with| a :: _ =>match a.toNat? with| some n => n| none => 5000| [] => 5000def report (pt : PieceTable) (iterations : Nat) : IO Unit := dolet tree := pt.treelet h := PieceTree.height treelet nodes := countNodes treelet leaves := countLeaves treelet maxLeaf := maxLeafPieces treelet maxChildren := maxInternalChildren treeIO.println s!"iterations={iterations}"IO.println s!"bytes={PieceTree.length tree} lines={PieceTree.lineBreaks tree} height={h}"IO.println s!"nodes={nodes} leaves={leaves} maxLeafPieces={maxLeaf} maxChildren={maxChildren}"match tree with| .empty => IO.println "root=empty"| .leaf ps _ _ => IO.println s!"root=leaf pieces={ps.size}"| .internal cs _ _ _ => IO.println s!"root=internal children={cs.size}"if iterations > 0 thenlet start := ← IO.monoMsNowlet _ := pt.getLineRange (iterations - 1)let stop := ← IO.monoMsNowIO.println s!"getLineRange(last)={stop - start}ms"def test : IO Unit := doIO.println "Starting TreeStats ViETest..."let iterations := 3000let pt := buildWorkload iterationslet t := pt.treelet bytes := PieceTree.length tlet textBytes := pt.toString.toUTF8.sizelet isNonEmpty :=match t with| .empty => false| _ => trueassertEqual "tree length equals rendered bytes" textBytes bytesassert "tree is non-empty" isNonEmptyassert "tree height is positive for non-empty tree" (PieceTree.height t >= 1)assert "leaf piece count stays within node capacity" (maxLeafPieces t <= NodeCapacity)assert "internal children stay within node capacity" (maxInternalChildren t <= NodeCapacity)assert "line range for last inserted line exists" ((pt.getLineRange (iterations - 1)).isSome)IO.println "TreeStats Test passed!"def main (args : List String) : IO Unit := dolet iterations := parseIterations argslet pt := buildWorkload iterationsreport pt iterationsend ViETest.TreeStats
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.UI.Searchimport ViETest.Utilsimport Bliku.Tui.Syntaxopen ViETest.Utilsnamespace ViETest.SyntaxHighlightprivate def firstRangeOf (line : String) (pat : String) : Option (Nat × Nat) :=(ViE.UI.findAllMatchesBytes line.toUTF8 pat.toUTF8)[0]?def testLean : IO Unit := dolet line := "def x := 42 -- comment"let spans := Bliku.Tui.Syntax.highlightLine (some "Main.lean") linelet (defS, defE) := (firstRangeOf line "def").getD (0, 0)let (numS, numE) := (firstRangeOf line "42").getD (0, 0)let (comS, comE) := (firstRangeOf line "-- comment").getD (0, 0)assertEqual "Lean keyword style" (Bliku.Tui.Syntax.defaultPalette.faceFor .keyword)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans defS defE)assertEqual "Lean number style" (Bliku.Tui.Syntax.defaultPalette.faceFor .numberLiteral)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans numS numE)assertEqual "Lean comment style" (Bliku.Tui.Syntax.defaultPalette.faceFor .comment)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans comS comE)def testMarkdown : IO Unit := dolet line := "Use `code` and [link](url)"let spans := Bliku.Tui.Syntax.highlightLine (some "README.md") linelet (codeS, codeE) := (firstRangeOf line "`code`").getD (0, 0)let (linkS, linkE) := (firstRangeOf line "[link](url)").getD (0, 0)assertEqual "Markdown code style" (Bliku.Tui.Syntax.defaultPalette.faceFor .code)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans codeS codeE)assertEqual "Markdown link style" (Bliku.Tui.Syntax.defaultPalette.faceFor .link)(Bliku.Tui.Syntax.faceForByteRange Bliku.Tui.Syntax.defaultPalette spans linkS linkE)def test : IO Unit := doIO.println "Starting SyntaxHighlight ViETest..."testLeantestMarkdownIO.println "SyntaxHighlight Test passed!"end ViETest.SyntaxHighlight
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.UIimport ViE.Configimport ViETest.Utilsnamespace ViETest.SearchHighlightopen ViETest.Utilsopen ViEdef test : IO Unit := doIO.println "Starting Search Highlight ViETest..."let hitRanges : Array (Nat × Nat) := #[(0, 4), (10, 14), (20, 24)]assertEqual "Active match picks cursor candidate" (some (10, 14)) (ViE.UI.activeMatchRange hitRanges (some 11))assertEqual "No active match when cursor is outside all matches" none (ViE.UI.activeMatchRange hitRanges (some 5))assertEqual "No active match when cursor is none" none (ViE.UI.activeMatchRange hitRanges none)let active := ViE.UI.activeMatchRange hitRanges (some 11)let activeHead :=match active with| some m => ViE.UI.overlapsByteRange m 0 1| none => falselet activeBody :=match active with| some m => ViE.UI.overlapsByteRange m 11 12| none => falseassertEqual "Active candidate does not color other matches" false activeHeadassertEqual "Active candidate colors only its own range" true activeBodylet s0 := ViE.startOrUpdateSearch ViE.initialState "abc" .forward falselet (m1, s1) := ViE.UI.getLineSearchMatches s0 s0.getActiveBuffer.id 0 "abc abc"assertEqual "Line search cache computes matches" true (m1.size > 0)let cachedBeforeSwitch :=match s1.searchState with| some ss => ss.lineMatches.contains 0| none => falseassertEqual "Line search cache populated" true cachedBeforeSwitchlet s2 := ViE.UI.ensureSearchLineCacheForBuffer s1 9999let cacheClearedOnSwitch :=match s2.searchState with| some ss => ss.lineMatches.isEmpty && ss.lineCacheBufferId == some 9999| none => falseassertEqual "Line search cache resets on buffer switch" true cacheClearedOnSwitchlet sInc0 := ViE.startOrUpdateSearch ViE.initialState "abc" .forward falselet sInc1 := {sInc0 withmode := .searchForwardinputState := { sInc0.inputState with commandBuffer := "" }}let sInc2 := ViE.runIncrementalSearch sInc1let incCleared :=match sInc2.searchState with| none => true| some _ => falseassertEqual "Incremental search with empty pattern clears highlight state" true incClearedlet t0 := 1000let (p0, k0) := ViE.parseKey ViE.initialState '\x1b' t0let (p1, k1) := ViE.parseKey p0 '[' (t0 + 1)let (p2, k2) := ViE.parseKey p1 '1' (t0 + 2)let (p3, k3) := ViE.parseKey p2 ';' (t0 + 3)let (p4, k4) := ViE.parseKey p3 '2' (t0 + 4)let (p5, k5) := ViE.parseKey p4 'X' (t0 + 5)assertEqual "Unknown CSI emits no keys while pending" ([] : List Key) (k0 ++ k1 ++ k2 ++ k3 ++ k4)assertEqual "Unknown CSI final emits Key.unknown" [Key.unknown 'X'] k5assertEqual "Unknown CSI clears pending state" "" p5.inputState.pendingKeyslet chars : List Char := ['\x1b', '[', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3']let (pLong, emitted) := chars.foldl(fun (acc : EditorState × List Key) ch =>let (st, out) := acclet (st', keys) := ViE.parseKey st ch (t0 + 20)(st', out ++ keys))(ViE.initialState, [])assertEqual "Overlong unknown CSI flushes emitted keys" true (!emitted.isEmpty)assertEqual "Overlong unknown CSI clears pending state" "" pLong.inputState.pendingKeyslet (_, tabKeys) := ViE.parseKey ViE.initialState '\x09' (t0 + 30)assertEqual "Tab parses as char tab" [Key.char '\t'] tabKeysend ViETest.SearchHighlight
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Window.Actionsimport ViE.Stateimport ViE.Typesimport ViE.Confignamespace ViETest.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 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)
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Buffer.LowIOimport ViE.State.Configimport ViE.State.Layoutimport ViETest.Utilsnamespace ViETest.PreviewDataopen ViETest.Utilsdef basePath : String := "ViETest/test_paths"def testFiles : List String := [s!"{basePath}/file0.txt",s!"{basePath}/dir0/file0.txt",s!"{basePath}/dir0/file1.txt",s!"{basePath}/dir0/file2.txt",s!"{basePath}/dir0/file3.txt",s!"{basePath}/dir0/dir0/file0.txt",s!"{basePath}/dir0/dir1/file1.txt",s!"{basePath}/dir0/dir1/file2.txt",s!"{basePath}/dir0/dir1/file3.txt",s!"{basePath}/dir0/dir1/file4.txt",s!"{basePath}/dir0/dir1/file5.txt"]def test : IO Unit := doIO.println "Starting Preview Data ViETest..."for path in testFiles dolet buf ← ViE.loadBufferByteArray pathlet content := buf.table.toStringlet byteLen := content.toUTF8.sizeassertEqual s!"{path} has content" true (content.length > 0)assertEqual s!"{path} has bytes" true (byteLen > 0)assertEqual s!"{path} has lines" true (buf.lineCount > 0)let firstLine := ViE.getLineFromBuffer buf 0 |>.getD ""assertEqual s!"{path} has first line" true (firstLine.length > 0)IO.println "PreviewDataTest passed!"end ViETest.PreviewData
import ViE.Data.PieceTableimport ViETest.Utilsopen ViEnamespace ViETest.PieceTable.UndoRedoopen ViETest.Utilsdef testUndoRedo : IO Unit := doIO.println "testUndoRedo..."let pt := PieceTable.fromString ""-- Insert "A" at 0let pt1 := pt.insert 0 "A" 0-- Insert "B" at 0 (Prepend) -> "BA"let pt2 := pt1.insert 0 "B" 0assertEqual "Initial state" "BA" pt2.toString-- Undo B (restores to "A")let (pt3, _) := pt2.undo 0assertEqual "After Undo 1" "A" pt3.toString-- Undo Alet (pt4, _) := pt3.undo 0assertEqual "After Undo 2" "" pt4.toString-- Redo Alet (pt5, _) := pt4.redo 0assertEqual "After Redo 1" "A" pt5.toString-- Redo Blet (pt6, _) := pt5.redo 0assertEqual "After Redo 2" "BA" pt6.toStringdef testComplexUndo : IO Unit := doIO.println "testComplexUndo..."let pt := PieceTable.fromString "Base"let pt1 := pt.insert 4 "1" 4 -- Base1let pt1 := pt1.commit -- Prevent mergelet pt2 := pt1.insert 5 "2" 5 -- Base12let pt3 := pt2.delete 5 1 5 -- Base1let pt4 := pt3.insert 5 "3" 5 -- Base13assertEqual "State before undo" "Base13" pt4.toString-- Undo insert "3" -> Base1let (u1, _) := pt4.undo 0assertEqual "Undo 3" "Base1" u1.toString-- Undo delete "2" -> Base12let (u2, _) := u1.undo 0assertEqual "Undo delete 2" "Base12" u2.toString-- Undo insert "2" -> Base1let (u3, _) := u2.undo 0assertEqual "Undo insert 2" "Base1" u3.toString-- Undo insert "1" -> Baselet (u4, _) := u3.undo 0assertEqual "Undo insert 1" "Base" u4.toStringdef test : IO Unit := doIO.println "Starting PieceTable Undo/Redo ViETest..."testUndoRedotestComplexUndoIO.println "PieceTable Undo/Redo Test passed!"end ViETest.PieceTable.UndoRedo
import ViE.Data.PieceTableimport ViETest.Utilsopen ViEnamespace ViETest.PieceTable.Stressopen ViETest.Utils/-- Naive string manipulation for verification -/def naiveInsert (s : String) (idx : Nat) (content : String) : String :=let lst := s.toListlet pre := lst.take idxlet post := lst.drop idxString.ofList (pre ++ content.toList ++ post)def naiveDelete (s : String) (idx : Nat) (len : Nat) : String :=let lst := s.toListlet pre := lst.take idxlet post := lst.drop (idx + len)String.ofList (pre ++ post)def newlineCount (s : String) : Nat :=s.foldl (fun n c => if c == '\n' then n + 1 else n) 0def expectedLineCountFromText (s : String) : Nat :=let breaks := newlineCount sbreaks + 1def testConsistency : IO Unit := doIO.println "testConsistency (Small Stress)..."let mut pt := PieceTable.fromString ""let mut mirror := ""-- 1. Insert Hellopt := pt.insert 0 "Hello" 0mirror := naiveInsert mirror 0 "Hello"assertEqual "Step 1" mirror pt.toString-- 2. Insert Worldpt := pt.insert 5 " World" 5mirror := naiveInsert mirror 5 " World"assertEqual "Step 2" mirror pt.toString-- 3. Delete space at 5-- "Hello World" -> "HelloWorld"pt := pt.delete 5 1 5mirror := naiveDelete mirror 5 1assertEqual "Step 3" mirror pt.toString-- 4. Insert comma at 5-- "HelloWorld" -> "Hello,World"pt := pt.insert 5 "," 5mirror := naiveInsert mirror 5 ","assertEqual "Step 4" mirror pt.toStringdef testLengthAndLineCountConsistency : IO Unit := doIO.println "testLengthAndLineCountConsistency..."let mut pt := PieceTable.fromString ""for i in [0:1200] dolet len := pt.lengthlet pos := if len == 0 then 0 else (i * 17 + 3) % (len + 1)if i % 5 == 0 thenlet txt := if i % 2 == 0 then "\n" else s!"x{i}"pt := pt.insert pos txt poselse if i % 7 == 0 thenlet delLen := if len == 0 then 0 else min ((i % 4) + 1) (len - (pos % len))pt := pt.delete (if len == 0 then 0 else pos % len) delLen 0elselet txt := if i % 3 == 0 then "a\n" else "b"pt := pt.insert pos txt poslet rendered := pt.toStringlet renderedBytes := rendered.toUTF8.sizeif renderedBytes != pt.length thenthrow <| IO.userError s!"Length mismatch at step {i}: tree={pt.length} rendered={renderedBytes}"let expectedLines := expectedLineCountFromText renderedif pt.lineCount != expectedLines thenthrow <| IO.userError s!"Line count mismatch at step {i}: tree={pt.lineCount} expected={expectedLines}"let h := PieceTree.height pt.treeif h > 64 thenthrow <| IO.userError s!"Tree height unexpectedly large at step {i}: height={h}"IO.println "[PASS] length/line count remain consistent under mixed edits"def testYankPasteClearing : IO Unit := doIO.println "testYankPasteClearing (yy+p reproduction)..."-- Simulate: insert 25 'i' chars + newline, then yy+p 4000 timeslet initLine := String.ofList (List.replicate 25 'i') ++ "\n"let lineBytes := initLine.toUTF8.size -- 26 byteslet mut pt := PieceTable.fromString initLinelet mut cursorLine : Nat := 0for step in [0:4000] do-- 1. yy: read current line content via getLineRange + getBytes (like real editor)let range := PieceTree.getLineRange pt.tree cursorLine ptmatch range with| none =>throw <| IO.userError s!"[YankPaste] getLineRange returned none at step {step}, line {cursorLine}"| some (off, len) =>let yankedBytes := PieceTree.getBytes pt.tree off len ptif yankedBytes.size == 0 thenthrow <| IO.userError s!"[YankPaste] Empty yank at step {step}, line {cursorLine}, off={off} len={len}"-- 2. p: paste below current line.-- Insert after end of current line (off + len + 1 for newline)let insertOffset := off + len + 1let insertOffset := min insertOffset pt.lengthlet yankStr := (String.fromUTF8! yankedBytes) ++ "\n"pt := pt.insert insertOffset yankStr insertOffset-- 3. Move cursor to pasted linecursorLine := cursorLine + 1-- 4. Verify tree lengthlet expectedLen := lineBytes * (step + 2)let actualLen := pt.lengthif actualLen != expectedLen thenthrow <| IO.userError s!"[YankPaste] Length mismatch at step {step}: expected={expectedLen} actual={actualLen}"-- 5. Full consistency check every 500 stepsif step % 500 == 0 thenlet rendered := pt.toString.toUTF8.sizeif rendered != expectedLen thenthrow <| IO.userError s!"[YankPaste] Rendered mismatch at step {step}: expected={expectedLen} rendered={rendered}"IO.println s!" step {step}: length={actualLen} lines={PieceTree.lineBreaks pt.tree} height={PieceTree.height pt.tree} OK"IO.println "[PASS] yank+paste 4000 iterations with getLineRange consistent"def test : IO Unit := doIO.println "Starting PieceTable Stress ViETest..."testConsistencytestLengthAndLineCountConsistencytestYankPasteClearingIO.println "PieceTable Stress Test passed!"end ViETest.PieceTable.Stress
import ViE.Data.PieceTableimport ViETest.Utilsimport Proof.PieceTable.Search.TwoWayopen ViEnamespace ViETest.PieceTable.Searchopen ViETest.Utilsdef makeBoundaryTable : PieceTable :=let n := ViE.NodeCapacity + 1let startIdx := n / 2 - 1let chars := Id.run dolet mut cs := Array.replicate n 'x'cs := cs.set! startIdx 'a'cs := cs.set! (startIdx + 1) 'b'cs := cs.set! (startIdx + 2) 'c'return cslet s := String.ofList chars.toListlet bytes := s.toUTF8let pieces := (Array.range n).map fun i =>{ source := BufferSource.originalstart := i.toUInt64length := (1 : UInt64)lineBreaks := (0 : UInt64)charCount := (1 : UInt64) }let tmp : PieceTable := {original := bytesaddBuffers := #[]tree := PieceTree.emptyundoStack := []redoStack := []undoStackCount := 0redoStackCount := 0undoLimit := 100lastInsert := none}let tree := PieceTree.fromPieces pieces tmp{ tmp with tree := tree }def testCrossLeafSearch : IO Unit := doIO.println "testCrossLeafSearch..."let pt := makeBoundaryTablelet n := ViE.NodeCapacity + 1let startIdx := n / 2 - 1let pattern := "abc".toUTF8let (r1, _, _) := PieceTree.searchNext pt.tree pt pattern 0 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchNext bloom across leaf" (some startIdx) r1let total := pt.tree.lengthlet (r2, _, _) := PieceTree.searchPrev pt.tree pt pattern total 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchPrev bloom across leaf" (some startIdx) r2def testCrossLeafSearchNearBoundary : IO Unit := doIO.println "testCrossLeafSearchNearBoundary..."let pt := makeBoundaryTablelet n := ViE.NodeCapacity + 1let startIdx := n / 2 - 1let pattern := "abc".toUTF8let startOffset := startIdx - 1let (r1, _, _) := PieceTree.searchNext pt.tree pt pattern startOffset 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchNext from near boundary" (some startIdx) r1let startExclusive := startIdx + pattern.sizelet (r2, _, _) := PieceTree.searchPrev pt.tree pt pattern startExclusive 64 true (Lean.RBMap.empty) #[] 16assertEqual "searchPrev to near boundary" (some startIdx) r2def test : IO Unit := doIO.println "Starting PieceTable Search ViETest..."testCrossLeafSearchtestCrossLeafSearchNearBoundaryIO.println "PieceTable Search Test passed!"end ViETest.PieceTable.Search
import ViE.Data.PieceTableimport ViETest.Utilsopen ViEnamespace ViETest.PieceTable.Basicopen ViETest.Utilsdef testInsert : IO Unit := doIO.println "testInsert..."let pt := PieceTable.fromString "Hello"-- Insert at endlet pt1 := pt.insert 5 " World" 5assertEqual "Insert at end" "Hello World" pt1.toString-- Insert at middlelet pt2 := pt1.insert 5 "," 5assertEqual "Insert at middle" "Hello, World" pt2.toString-- Insert at startlet pt3 := pt2.insert 0 "Say: " 0assertEqual "Insert at start" "Say: Hello, World" pt3.toStringdef testDelete : IO Unit := doIO.println "testDelete..."let pt := PieceTable.fromString "Hello, World!"-- Delete "World" (offset 7, len 5)let pt1 := pt.delete 7 5 7assertEqual "Delete middle" "Hello, !" pt1.toString-- Delete startlet pt2 := pt1.delete 0 7 0assertEqual "Delete start" "!" pt2.toString-- Delete endlet pt3 := pt2.delete 0 1 0assertEqual "Delete end" "" pt3.toStringdef test : IO Unit := doIO.println "Starting PieceTable Basic ViETest..."testInserttestDeleteIO.println "PieceTable Basic Test passed!"end ViETest.PieceTable.Basic
import ViE.Data.PieceTableimport ViETest.Utilsopen ViEnamespace ViETest.PieceTable.Appendedopen ViETest.Utilsdef testAppends : IO Unit := doIO.println "testAppends..."let pt := PieceTable.fromString "Line1\n"-- Append Line 2let pt1 := pt.insert pt.length "Line2\n" pt.lengthassertEqual "Append 1" "Line1\nLine2\n" pt1.toString-- Append Line 3let pt2 := pt1.insert pt1.length "Line3" pt1.lengthassertEqual "Append 2" "Line1\nLine2\nLine3" pt2.toStringdef test : IO Unit := doIO.println "Starting PieceTable Appended ViETest..."testAppendsIO.println "PieceTable Appended Test passed!"end ViETest.PieceTable.Appended
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Data.PieceTablenamespace ViETest.PasteReproductiondef test : IO Unit := doIO.println "--- Test 1: Paste below last line (no trailing newline) ---"let pt := ViE.PieceTable.fromString "line 1"IO.println s!"Initial: [{pt.toString}]"-- Yank line 1 (manually simulating EditorState.yankCurrentLine)let line1 := pt.getLine 0 |>.getD ""let yanked := if line1.endsWith "\n" then line1 else line1 ++ "\n"IO.println s!"Yanked: [{yanked}]"-- Paste below line 0 (simulating EditorState.pasteBelow)let (off, text) := match pt.getOffsetFromPoint 1 0 with| some o => (o, yanked)| none =>let len := pt.lengthif len > 0 thenif !pt.endsWithNewline then (len, "\n" ++ yanked) else (len, yanked)else (0, yanked)let pt2 := pt.insert off text offIO.println s!"After paste:\n[{pt2.toString}]"IO.println s!"Line count: {pt2.lineCount}"for i in [:pt2.lineCount] doIO.println s!"Line {i}: [{pt2.getLine i |>.getD "NONE"}]"IO.println "\n--- Test 2: Paste below last line (with trailing newline) ---"let ptB := ViE.PieceTable.fromString "line 1\n"IO.println s!"Initial: [{ptB.toString}]"let line1B := ptB.getLine 0 |>.getD ""let yankedB := if line1B.endsWith "\n" then line1B else line1B ++ "\n"let (offB, textB) := match ptB.getOffsetFromPoint 1 0 with| some o => (o, yankedB)| none =>let len := ptB.lengthif len > 0 thenif !ptB.endsWithNewline then (len, "\n" ++ yankedB) else (len, yankedB)else (0, yankedB)let ptB2 := ptB.insert offB textB offBIO.println s!"After paste:\n[{ptB2.toString}]"IO.println s!"Line count: {ptB2.lineCount}"for i in [:ptB2.lineCount] doIO.println s!"Line {i}: [{ptB2.getLine i |>.getD "NONE"}]"
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.Configimport ViE.Command.Implimport ViE.Key.Mapimport ViE.State.Editimport ViE.State.Movementimport Proof.Visualnamespace ViETest.Modeopen ViEprivate theorem visualLineProofWitness :(ViE.initialState.startVisualLineMode).mode = .visualLine := bysimpa using Proof.startVisualLineMode_setsMode ViE.initialStatedef 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}")-- Helper to construct a full Configdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}-- Run a sequence of keysdef runKeys (startState : EditorState) (keys : List Key) : IO EditorState := dolet config := makeTestConfiglet mut s := startStatefor k in keys dos ← ViE.update config s kreturn sdef test : IO Unit := doIO.println "Starting Mode ViETest..."let s := ViE.initialState-- Scenario: Insert 'abc' then Esc.-- Initial: (0,0)-- 'i' -> Insert Mode-- 'a' -> "a", (0,1)-- 'b' -> "ab", (0,2)-- 'c' -> "abc", (0,3)-- Esc -> Normal Mode.-- Expected Vim behavior: Cursor should move left to (0,2) 'c'.-- If empty line "insert a then esc", "a" (0,1) -> (0,0).let keys := [Key.char 'i', Key.char 'a', Key.char 'b', Key.char 'c', Key.esc]let sEnd ← runKeys s keys-- Check Modeif sEnd.mode != Mode.normal thenIO.println s!"[FAIL] Mode mismatch. Expected Normal, got {sEnd.mode}"assert "Mode is Normal" falseelseIO.println "[PASS] Mode is Normal"-- Check Textlet text := getLineFromBuffer sEnd.getActiveBuffer 0 |>.getD ""if text != "abc" thenIO.println s!"[FAIL] Text mismatch. Expected 'abc', got '{text}'"assert "Text is 'abc'" falseelseIO.println "[PASS] Text is 'abc'"-- Check Cursor-- Expect (0, 2)let cursor := sEnd.getCursorif cursor.col.val != 2 thenIO.println s!"[FAIL] Cursor mismatch. Expected (0, 2), got ({cursor.row.val}, {cursor.col.val})"assert "Cursor moved left on Esc" falseelseIO.println "[PASS] Cursor moved left on Esc"IO.println "ModeTest passed!"end ViETest.Mode
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.State.Layoutimport ViE.Data.PieceTableimport ViETest.Utilsopen ViEnamespace ViETest.MissingEolopen ViETest.Utilsdef testMissingEol : IO Unit := doIO.println "testMissingEol..."let s := ViE.initialStatelet s := s.updateActiveBuffer fun buffer =>{ buffer with table := PieceTable.fromString "abc\n", dirty := true }let buf := s.getActiveBufferassert "missingEol false with trailing newline" (!buf.missingEol)let len := buf.table.lengthlet s := s.updateActiveBuffer fun buffer =>{ buffer with table := buffer.table.delete (len - 1) 1 (len - 1), dirty := true }let buf2 := s.getActiveBufferassert "missingEol true after deleting newline" buf2.missingEollet s := ViE.initialStatelet s := s.updateActiveBuffer fun buffer =>{ buffer with table := PieceTable.fromString "abc", dirty := true }let buf3 := s.getActiveBufferassert "missingEol true without newline" buf3.missingEollet len2 := buf3.table.lengthlet s := s.updateActiveBuffer fun buffer =>{ buffer with table := buffer.table.insert len2 "\n" len2, dirty := true }let buf4 := s.getActiveBufferassert "missingEol false after adding newline" (!buf4.missingEol)def test : IO Unit := doIO.println "Starting MissingEol ViETest..."testMissingEolIO.println "MissingEol Test passed!"end ViETest.MissingEol
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViEimport ViETest.Utilsnamespace ViETest.Lspopen ViETest.Utilsdef test : IO Unit := doIO.println "Starting LSP ViETest..."let leanBuf := { ViE.initialBuffer with filename := some "Main.lean" }let txtBuf := { ViE.initialBuffer with filename := some "notes.txt" }assertEqual "isLeanBuffer true for .lean" true (ViE.Lsp.Lean.isLeanBuffer leanBuf)assertEqual "isLeanBuffer false for non-.lean" false (ViE.Lsp.Lean.isLeanBuffer txtBuf)let uriAbs := ViE.Lsp.Lean.fileUri "/tmp/a b#c%.lean"assertEqual "fileUri percent-encodes reserved bytes" "file:///tmp/a%20b%23c%25.lean" uriAbslet uriRel := ViE.Lsp.Lean.fileUri "tmp/test.lean"assertEqual "fileUri normalizes relative path with leading slash" "file:///tmp/test.lean" uriRellet uriWs ← ViE.Lsp.Lean.fileUriWithWorkspace (some "/tmp/ws") "dir/a b.lean"assertEqual "fileUriWithWorkspace resolves relative path from workspace root" "file:///tmp/ws/dir/a%20b.lean" uriWslet uriWsAbs ← ViE.Lsp.Lean.fileUriWithWorkspace (some "/tmp/ws") "/tmp/alt/x.lean"assertEqual "fileUriWithWorkspace keeps absolute path priority" "file:///tmp/alt/x.lean" uriWsAbslet okResp : Lean.Json := Lean.Json.mkObj [("id", (12 : Lean.Json)), ("result", Lean.Json.null)]let errResp : Lean.Json := Lean.Json.mkObj [("id", (13 : Lean.Json)), ("error", Lean.Json.mkObj [("code", (1 : Lean.Json))])]let notif : Lean.Json := Lean.Json.mkObj [("method", .str "textDocument/publishDiagnostics"), ("params", Lean.Json.mkObj [])]assertEqual "responseId? parses result response id" (some 12) (ViE.Lsp.Lean.responseId? okResp)assertEqual "responseId? parses error response id" (some 13) (ViE.Lsp.Lean.responseId? errResp)assertEqual "responseId? is none for notifications" (none : Option Nat) (ViE.Lsp.Lean.responseId? notif)assertEqual "isResponseForRequest true for same id" true (ViE.Lsp.Lean.isResponseForRequest okResp 12)assertEqual "isResponseForRequest false for different id" false (ViE.Lsp.Lean.isResponseForRequest okResp 99)let s0 := ViE.initialStatelet sStatus ← ViE.Lsp.Lean.cmdLsp ["status"] s0assertEqual "lsp status reports stopped when no session" "LSP: stopped" sStatus.messagelet sStop ← ViE.Lsp.Lean.cmdLsp ["stop"] s0assertEqual "lsp stop reports not running when no session" "LSP is not running" sStop.messageIO.println "LSP Test passed!"end ViETest.Lsp
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Typesopen ViEnamespace ViETest.Layoutdef 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 := doIO.println "Starting Layout ViETest..."-- 1. Construct Layoutslet view1 : ViewState := { bufferId := 1, cursor := {row:=0, col:=0}, scrollRow:=0, scrollCol:=0 }let win1 := Layout.window 1 view1let view2 : ViewState := { bufferId := 2, cursor := {row:=0, col:=0}, scrollRow:=0, scrollCol:=0 }let win2 := Layout.window 2 view2-- 2. Test Split Constructionlet hsplit := Layout.hsplit win1 win2 0.5match hsplit with| .hsplit _ _ ratio =>assert "HSplit match" trueassert "HSplit ratio" (ratio == 0.5)| _ => assert "HSplit construction failed" falselet vsplit := Layout.vsplit hsplit win1 0.3match vsplit with| .vsplit t _ ratio =>assert "VSplit match" trueassert "VSplit ratio" (ratio == 0.3)match t with| .hsplit _ _ _ => assert "VSplit top is HSplit" true| _ => assert "VSplit top incorrect" false| _ => assert "VSplit construction failed" falseIO.println "LayoutTest passed!"end ViETest.Layout
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Movementimport ViE.State.Editimport ViE.Key.Mapimport ViE.Typesimport ViE.Buffer.Contentimport ViE.Configimport ViE.Command.Implimport ViETest.Utilsnamespace ViETest.Keybindsopen ViEopen ViETest.Utilsdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}def keys (s : String) : List Key :=s.toList.map fun c =>if c == '\n' then Key.enter else Key.char cdef runKeys (s : EditorState) (ks : List Key) : IO EditorState := dolet config := makeTestConfigks.foldlM (fun s k => ViE.update config s k) sdef findEntryIndex (entries : List FileEntry) (name : String) : Option Nat :=let rec loop (rest : List FileEntry) (idx : Nat) : Option Nat :=match rest with| [] => none| e :: tail =>if e.name == name thensome idxelseloop tail (idx + 1)loop entries 0def findEntryIndexByPath (entries : List FileEntry) (path : String) : Option Nat :=let rec loop (rest : List FileEntry) (idx : Nat) : Option Nat :=match rest with| [] => none| e :: tail =>if e.path == path thensome idxelseloop tail (idx + 1)loop entries 0def testMotions : IO Unit := doIO.println " Testing Motions..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "line1\nline2\nline3" ++ [Key.esc] ++ keys "gg0")-- "line1\nline2\nline3" cursor at (0,0)-- l, hlet s_l ← runKeys s1 [Key.char 'l']assertCursor "l moves right" s_l 0 1let s_lh ← runKeys s_l [Key.char 'h']assertCursor "h moves left" s_lh 0 0-- j, klet s_j ← runKeys s1 [Key.char 'j']assertCursor "j moves down" s_j 1 0let s_jk ← runKeys s_j [Key.char 'k']assertCursor "k moves up" s_jk 0 0-- 0, $let s_end ← runKeys s1 [Key.char '$']assertCursor "$ moves to end" s_end 0 4 -- '1' is at col 4let s_start ← runKeys s_end [Key.char '0']assertCursor "0 moves to start" s_start 0 0-- w, b, elet s_text ← runKeys s0 ([Key.char 'i'] ++ keys "word1 word2" ++ [Key.esc] ++ [Key.char '0'])let s_w ← runKeys s_text [Key.char 'w']assertCursor "w moves to next word" s_w 0 6let s_e ← runKeys s_text [Key.char 'e']assertCursor "e moves to end of word" s_e 0 4let s_we ← runKeys s_w [Key.char 'e']assertCursor "we moves to end of next word" s_we 0 10let s_web ← runKeys s_we [Key.char 'b']assertCursor "b moves to start of word" s_web 0 6-- gg, Glet s_G ← runKeys s1 [Key.char 'G']assertCursor "G moves to last line" s_G 2 0let s_gg ← runKeys s_G [Key.char 'g', Key.char 'g']assertCursor "gg moves to first line" s_gg 0 0-- | (jump to column)let s_pipe ← runKeys s1 [Key.char '3', Key.char '|']assertCursor "| jumps to column" s_pipe 0 2 -- 1-indexed count 3 -> col 2def testEditing : IO Unit := doIO.println " Testing Editing..."let s0 := ViE.initialState-- i, a, Alet s_i ← runKeys s0 [Key.char 'i', Key.char 'x', Key.esc]assertBuffer "i inserts" s_i "x"let s_a ← runKeys s_i [Key.char 'a', Key.char 'y', Key.esc]assertBuffer "a appends" s_a "xy"let s_A ← runKeys s_a [Key.char '0', Key.char 'A', Key.char 'z', Key.esc]assertBuffer "A appends at end" s_A "xyz"let s_q ← runKeys s0 [Key.char 'q']assertEqual "q does not enter command mode" Mode.normal s_q.modelet s_V ← runKeys s0 [Key.char 'V']assertEqual "V enters visual line mode" Mode.visualLine s_V.modelet s_ctrlV ← runKeys s0 [Key.ctrl 'v']assertEqual "Ctrl-v enters visual block mode" Mode.visualBlock s_ctrlV.modelet s_tab_insert ← runKeys s0 [Key.char 'i', Key.char '\t']assertCursor "Tab advances cursor to next tab stop in insert mode" s_tab_insert 0 4let s_tab_backspace ← runKeys s0 [Key.char 'i', Key.char '\t', Key.backspace]assertBuffer "Tab backspace deletes tab in insert mode" s_tab_backspace ""assertCursor "Tab backspace restores cursor column" s_tab_backspace 0 0let s_tab ← runKeys s0 [Key.char 'i', Key.char '\t', Key.esc]assertBuffer "Tab inserts in insert mode" s_tab "\t"assertCursor "Esc after tab returns to tab char in normal mode" s_tab 0 0let s0_tab8 := { s0 with config := { s0.config with tabStop := 8 } }let s_tab8 ← runKeys s0_tab8 [Key.char 'i', Key.char '\t']assertCursor "Tab follows custom tabStop in insert mode" s_tab8 0 8-- o, Olet s_o ← runKeys s_i [Key.char 'o', Key.char 'y', Key.esc]assertBuffer "o inserts line below" s_o "x\ny"let s_O ← runKeys s_o [Key.char 'k', Key.char 'O', Key.char 'z', Key.esc]assertBuffer "O inserts line above" s_O "z\nx\ny"-- xlet s_x ← runKeys s_i [Key.char 'x']assertBuffer "x deletes char" s_x ""match s_x.clipboard with| some reg =>assertEqual "x register kind" RegisterKind.charwise reg.kindassertEqual "x register text" "x" reg.text| none => assertEqual "x register set" true falsedef testOperators : IO Unit := doIO.println " Testing Operators..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "hello world" ++ [Key.esc] ++ [Key.char '0'])-- dwlet s_dw ← runKeys s1 [Key.char 'd', Key.char 'w']assertBuffer "dw deletes word" s_dw "world"match s_dw.clipboard with| some reg =>assertEqual "dw register kind" RegisterKind.charwise reg.kindassertEqual "dw register text" "hello " reg.text| none => assertEqual "dw register set" true false-- cwlet s_cw ← runKeys s1 [Key.char 'c', Key.char 'w', Key.char 'h', Key.char 'i', Key.esc]assertBuffer "cw changes word" s_cw "hi world"-- ddlet s_lines ← runKeys s0 ([Key.char 'i'] ++ keys "line1\nline2\nline3" ++ [Key.esc] ++ keys "ggj")let s_dd ← runKeys s_lines [Key.char 'd', Key.char 'd']assertBuffer "dd deletes current line" s_dd "line1\nline3"match s_dd.clipboard with| some reg =>assertEqual "dd register kind" RegisterKind.linewise reg.kindassertEqual "dd register text" "line2\n" reg.text| none => assertEqual "dd register set" true false-- yy, p, Plet s_yy ← runKeys s_lines [Key.char 'g', Key.char 'g', Key.char 'y', Key.char 'y']let s_p ← runKeys s_yy [Key.char 'G', Key.char 'p']assertBuffer "p pastes below" s_p "line1\nline2\nline3\nline1\n"let s_P ← runKeys s_yy [Key.char 'g', Key.char 'g', Key.char 'P']assertBuffer "P pastes above" s_P "line1\nline1\nline2\nline3"let s_indent ← runKeys s0 ([Key.char 'i'] ++ keys " indented\nx" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char 'y', Key.char 'y', Key.char 'j', Key.char 'p'])assertCursor "linewise paste moves to first non-blank" s_indent 2 2-- paste should not overwrite registerlet s_reg0 ← runKeys s0 ([Key.char 'i'] ++ keys "one\ntwo" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char 'y', Key.char 'y'])let regBefore := s_reg0.clipboardlet s_regP ← runKeys s_reg0 [Key.char 'j', Key.char 'p']match (regBefore, s_regP.clipboard) with| (some before, some after) =>assertEqual "paste keeps register kind" before.kind after.kindassertEqual "paste keeps register text" before.text after.text| _ => assertEqual "paste keeps register" true false-- charwise y/p from normal modelet s_char0 ← runKeys s0 ([Key.char 'i'] ++ keys "hello" ++ [Key.esc] ++ [Key.char '0'])let s_charY ← runKeys s_char0 [Key.char 'v', Key.char 'l', Key.char 'y']let s_charP ← runKeys s_charY [Key.char '$', Key.char 'p']assertBuffer "charwise paste in normal mode" s_charP "hellohe"assertCursor "charwise paste cursor at end" s_charP 0 6let s_charP2 ← runKeys s_charY [Key.char '0', Key.char 'P']assertBuffer "charwise P pastes before cursor" s_charP2 "hehello"assertCursor "charwise P cursor at end" s_charP2 0 1def testVisual : IO Unit := doIO.println " Testing Visual Mode..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "highlight me" ++ [Key.esc] ++ [Key.char '0'])-- v, dlet s_v ← runKeys s1 [Key.char 'v', Key.char 'l', Key.char 'd']assertBuffer "visual d deletes selection" s_v "ghlight me"match s_v.clipboard with| some reg =>assertEqual "visual d register kind" RegisterKind.charwise reg.kindassertEqual "visual d register text" "hi" reg.text| none => assertEqual "visual d register set" true false-- v, y, plet s_vy ← runKeys s1 [Key.char 'v', Key.char 'e', Key.char 'y']let s_vyp ← runKeys s_vy [Key.char '$', Key.char 'p']assertBuffer "visual y yanks selection" s_vyp "highlight mehighlight"let s_linewise0 ← runKeys s0 ([Key.char 'i'] ++ keys "aa\nbb\ncc" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g'])let s_linewiseD ← runKeys s_linewise0 [Key.char 'V', Key.char 'j', Key.char 'd']assertBuffer "visual line d deletes full lines" s_linewiseD "cc"match s_linewiseD.clipboard with| some reg =>assertEqual "visual line d register kind" RegisterKind.linewise reg.kindassertEqual "visual line d register text" "aa\nbb\n" reg.text| none => assertEqual "visual line d register set" true falselet s_linewiseY ← runKeys s_linewise0 [Key.char 'V', Key.char 'j', Key.char 'y']let s_linewiseP ← runKeys s_linewiseY [Key.char 'G', Key.char 'p']assertBuffer "visual line y/p pastes linewise" s_linewiseP "aa\nbb\ncc\naa\nbb\n"let s_char0 ← runKeys s0 ([Key.char 'i'] ++ keys "abc" ++ [Key.esc] ++ [Key.char '0'])let s_charY ← runKeys s_char0 [Key.char 'l', Key.char 'v', Key.char 'y']let s_charP ← runKeys s_charY [Key.char '$', Key.char 'p']assertBuffer "charwise paste appends" s_charP "abcb"assertCursor "charwise paste cursor at end" s_charP 0 3let s_charP2 ← runKeys s_charY [Key.char '0', Key.char 'P']assertBuffer "charwise P inserts before" s_charP2 "babc"assertCursor "charwise P cursor at end" s_charP2 0 0-- visual block yank/paste cursor positionlet s_blk0 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\nefgh" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char '0'])let s_blkY ← runKeys s_blk0 [Key.char 'l', Key.ctrl 'v', Key.char 'l', Key.char 'j', Key.char 'y']let s_blkP ← runKeys s_blkY [Key.char 'g', Key.char 'g', Key.char '0', Key.char 'p']assertBuffer "visual block y/p inserts block" s_blkP "abcbcd\nefgfgh"assertCursor "visual block paste cursor at block start" s_blkP 0 1let s_blkP2 ← runKeys s_blkY [Key.char 'g', Key.char 'g', Key.char '0', Key.char 'P']assertBuffer "visual block P inserts block" s_blkP2 "bcabcd\nfgefgh"assertCursor "visual block P cursor at block start" s_blkP2 0 0let s_blkD0 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\nefgh" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char '0'])let s_blkD ← runKeys s_blkD0 [Key.char 'l', Key.ctrl 'v', Key.char 'l', Key.char 'j', Key.char 'd']assertBuffer "visual block d deletes block" s_blkD "ad\neh"match s_blkD.clipboard with| some reg =>assertEqual "visual block d register kind" RegisterKind.blockwise reg.kindassertEqual "visual block d register text" "bc\nfg" reg.text| none => assertEqual "visual block d register set" true false-- linewise yank/paste (via yy + P)let s_line0 ← runKeys s0 ([Key.char 'i'] ++ keys "aa\nbb\ncc" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g'])let s_lineY ← runKeys s_line0 [Key.char 'y', Key.char 'y']let s_lineP ← runKeys s_lineY [Key.char 'j', Key.char 'P']assertBuffer "linewise paste above keeps lines" s_lineP "aa\naa\nbb\ncc"let s_lineP2 ← runKeys s_lineY [Key.char 'j', Key.char 'p']assertBuffer "linewise paste below keeps lines" s_lineP2 "aa\nbb\naa\ncc"def testCounted : IO Unit := doIO.println " Testing Counted Actions..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "a b c d e" ++ [Key.esc] ++ [Key.char '0'])-- 3wlet s_3w ← runKeys s1 [Key.char '3', Key.char 'w']assertCursor "3w moves 3 words" s_3w 0 6-- 2jlet s_lines ← runKeys s0 ([Key.char 'i'] ++ keys "1\n2\n3\n4" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g'])let s_2j ← runKeys s_lines [Key.char '2', Key.char 'j']assertCursor "2j moves 2 lines down" s_2j 2 0def testVimCompatMotions : IO Unit := doIO.println " Testing Vim Compatibility Motions..."let s0 := { ViE.initialState with windowHeight := 12, windowWidth := 80 }let lines := String.intercalate "\n" ((List.range 30).map (fun i => s!" line{i}")) ++ "\n"let s1 ← runKeys s0 ([Key.char 'i'] ++ keys lines ++ [Key.esc] ++ keys "gg0")let sCd ← runKeys s1 [Key.ctrl 'd']assertCursor "Ctrl-d scrolls half page down" sCd 5 0let sCu ← runKeys sCd [Key.ctrl 'u']assertCursor "Ctrl-u scrolls half page up" sCu 0 0let sCf ← runKeys s1 [Key.ctrl 'f']assertCursor "Ctrl-f scrolls one page down" sCf 10 0let sCb ← runKeys sCf [Key.ctrl 'b']assertCursor "Ctrl-b scrolls one page up" sCb 0 0let sCe ← runKeys s1 [Key.ctrl 'e']let (ceRow, _) := sCe.getScrollassertEqual "Ctrl-e scrolls window down by one line" 1 ceRow.vallet sCy ← runKeys sCe [Key.ctrl 'y']let (cyRow, _) := sCy.getScrollassertEqual "Ctrl-y scrolls window up by one line" 0 cyRow.vallet sH ← runKeys sCf [Key.char 'H']assertCursor "H moves to top line of screen" sH 10 2let sM ← runKeys sCf [Key.char 'M']assertCursor "M moves to middle line of screen" sM 15 2let sL ← runKeys sCf [Key.char 'L']assertCursor "L moves to bottom line of screen" sL 19 2let sF0 ← runKeys s0 ([Key.char 'i'] ++ keys "abcaXcaYca" ++ [Key.esc] ++ [Key.char '0'])let sF1 ← runKeys sF0 [Key.char 'f', Key.char 'a']assertCursor "fa finds next character" sF1 0 3let sF2 ← runKeys sF1 [Key.char ';']assertCursor "; repeats last f motion" sF2 0 6let sF3 ← runKeys sF2 [Key.char ',']assertCursor ", reverses last f motion" sF3 0 3let sT1 ← runKeys sF3 [Key.char 't', Key.char 'a']assertCursor "ta moves before target" sT1 0 5let sT2 ← runKeys sT1 [Key.char ';']assertCursor "; repeats last t motion" sT2 0 8let sPct0 ← runKeys s0 ([Key.char 'i'] ++ keys "(a[b]c)" ++ [Key.esc] ++ [Key.char '0'])let sPct1 ← runKeys sPct0 [Key.char '%']assertCursor "% jumps to matching bracket" sPct1 0 6let sPct2 ← runKeys sPct1 [Key.ctrl 'o']assertCursor "Ctrl-o jumps back in jump list" sPct2 0 0let sPct3 ← runKeys sPct2 [Key.char '\t']assertCursor "Ctrl-i (Tab) jumps forward in jump list" sPct3 0 6let sStar0 := ViE.initialStatelet sStar1 ← runKeys sStar0 ([Key.char 'i'] ++ keys "foo bar\nfoo baz\nbar foo\n" ++ [Key.esc] ++ keys "gg0")let sStar2 ← runKeys sStar1 [Key.char '*']assertCursor "* searches next word under cursor" sStar2 1 0let sStar3 ← runKeys sStar2 [Key.char '#']assertCursor "# searches previous word under cursor" sStar3 0 0def testWorkgroupSwitch : IO Unit := doIO.println " Testing Workgroup Switching..."let s0 := ViE.initialStatelet s1 ← runKeys s0 [Key.alt '3']assertEqual "Alt-3 switches workgroup" 3 s1.currentGrouplet s2 ← runKeys s1 [Key.alt '0']assertEqual "Alt-0 switches workgroup" 0 s2.currentGroupdef testSearch : IO Unit := doIO.println " Testing Search..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "hello world\nhello again\n" ++ [Key.esc])let s2 ← runKeys s1 ([Key.char '/'] ++ keys "hello" ++ [Key.enter])assertCursor "/hello finds first match" s2 0 0let s2Prompt ← runKeys s2 [Key.char '/']let promptCleared :=match s2Prompt.searchState with| none => true| some _ => falseassertEqual "Starting new search prompt clears old search highlight state" true promptClearedlet s2e ← runKeys s2 [Key.enter]assertCursor "Enter after search jumps to next match" s2e 1 0assertBuffer "Enter after search does not insert newline" s2e "hello world\nhello again\n"let s3 ← runKeys s2 [Key.char 'n']assertCursor "n finds next match" s3 1 0let s4 ← runKeys s3 [Key.char 'N']assertCursor "N finds previous match" s4 0 0let s5 ← runKeys s4 ([Key.char '?'] ++ keys "world" ++ [Key.enter])assertCursor "?world searches backward" s5 0 6def testCommandSubstitute : IO Unit := doIO.println " Testing Command Substitute..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "foo bar\nfoo baz\nbar foo\n" ++ [Key.esc] ++ keys "gg0")let s2 ← runKeys s1 ([Key.char ':'] ++ keys "s/foo/xxx/" ++ [Key.enter])assertBuffer ":s replaces first match on current line" s2 "xxx bar\nfoo baz\nbar foo\n"let s3 := ViE.initialStatelet s4 ← runKeys s3 ([Key.char 'i'] ++ keys "foo foo\nfoo foo\n" ++ [Key.esc] ++ keys "gg0")let s5 ← runKeys s4 ([Key.char ':'] ++ keys "%s/foo/yyy/" ++ [Key.enter])assertBuffer ":%s replaces first match per line" s5 "yyy foo\nyyy foo\n"let s6 := ViE.initialStatelet s7 ← runKeys s6 ([Key.char 'i'] ++ keys "foo bar\nfoo baz\nbar foo\n" ++ [Key.esc] ++ keys "gg0")let s8 ← runKeys s7 ([Key.char ':'] ++ keys "%s/foo/yyy/g" ++ [Key.enter])assertBuffer ":%s with g replaces all matches" s8 "yyy bar\nyyy baz\nbar yyy\n"def testCommandGlobal : IO Unit := doIO.println " Testing Command Global..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "a\nfoo1\nb\nfoo2\n" ++ [Key.esc] ++ keys "gg0")let s2 ← runKeys s1 ([Key.char ':'] ++ keys "g/foo/ d" ++ [Key.enter])assertBuffer ":g/pat/ d deletes matching lines" s2 "a\nb\n"let s3 := ViE.initialStatelet s4 ← runKeys s3 ([Key.char 'i'] ++ keys "foo foo\nbar foo\nfoo bar\n" ++ [Key.esc] ++ keys "gg0")let s5 ← runKeys s4 ([Key.char ':'] ++ keys "g/foo/ s/foo/xxx/" ++ [Key.enter])assertBuffer ":g/pat/ s replaces first match per line" s5 "xxx foo\nbar xxx\nxxx bar\n"let s6 := ViE.initialStatelet s7 ← runKeys s6 ([Key.char 'i'] ++ keys "keep1\nfoo\nkeep2\n" ++ [Key.esc] ++ keys "gg0")let s8 ← runKeys s7 ([Key.char ':'] ++ keys "v/foo/ d" ++ [Key.enter])assertBuffer ":v/pat/ d deletes non-matching lines" s8 "foo\n"let s9 := ViE.initialStatelet s10 ← runKeys s9 ([Key.char 'i'] ++ keys "a\nfoo1\nb\nfoo2\nc" ++ [Key.esc] ++ [Key.char 'g', Key.char 'g', Key.char 'j', Key.char 'j', Key.char 'j', Key.char '0'])assertCursor ":g/pat/ d precondition cursor on deletable line" s10 3 0let s11 ← runKeys s10 ([Key.char ':'] ++ keys "g/foo/ d" ++ [Key.enter])assertBuffer ":g/pat/ d keeps expected text when cursor line is deleted" s11 "a\nb\nc"assertCursor ":g/pat/ d clamps cursor when current line is deleted" s11 2 0def testCursorDriftCustomTabStop : IO Unit := doIO.println " Testing Cursor Drift (custom tabStop)..."let cfg8 := { ViE.defaultConfig with tabStop := 8 }let s0 := { ViE.initialState with config := cfg8 }let s1 ← runKeys s0 ([Key.char 'i', Key.char '\t'] ++ keys "foo\n" ++ [Key.esc])let s2 ← runKeys s1 [Key.char 'g', Key.char 'g', Key.char '0', Key.char 'l']assertCursor "custom tabStop: l moves to first char after tab" s2 0 8let s3 ← runKeys s2 ([Key.char ':'] ++ keys "s/foo/bar/" ++ [Key.enter])assertBuffer "custom tabStop: :s updates current line" s3 "\tbar\n"let s4 ← runKeys s3 [Key.char 'u']assertBuffer "custom tabStop: undo restores text after :s" s4 "\tfoo\n"assertCursor "custom tabStop: undo restores cursor col after :s" s4 0 8let s5 ← runKeys s4 [Key.ctrl 'r']assertBuffer "custom tabStop: redo reapplies :s" s5 "\tbar\n"assertCursor "custom tabStop: redo restores cursor col after :s" s5 0 8let s6 ← runKeys s2 [Key.char 'v', Key.char 'l', Key.char 'd']assertBuffer "custom tabStop: visual delete uses configured offsets" s6 "\to\n"assertCursor "custom tabStop: visual delete keeps cursor aligned" s6 0 8def testCommandBloom : IO Unit := doIO.println " Testing Command Bloom..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char 'i'] ++ keys "hello bloom\nbloom hello\n" ++ [Key.esc] ++ keys "gg0")let s2 ← runKeys s1 ([Key.char ':'] ++ keys "bloom /bloom" ++ [Key.enter])assertCursor ":bloom moves to first match" s2 0 6let s3 ← runKeys s2 ([Key.char ':'] ++ keys "bloom /nomatch" ++ [Key.enter])assertEqual ":bloom not found message" "Pattern not found: nomatch" s3.messagedef testUiCommands : IO Unit := doIO.println " Testing UI Commands..."let s0 := ViE.initialStatelet s1 ← runKeys s0 ([Key.char ':'] ++ keys "float hello" ++ [Key.enter])assertEqual ":float shows overlay" true s1.floatingOverlay.isSomelet s2 ← runKeys s1 [Key.esc]assertEqual "Esc closes overlay" false s2.floatingOverlay.isSomelet s3 ← runKeys s0 ([Key.char ':'] ++ keys "float alpha\\nbeta" ++ [Key.enter])match s3.floatingOverlay with| some overlay =>assertEqual ":float parses newline escape (line count)" 2 overlay.lines.sizeassertEqual ":float parses newline escape (line 1)" "alpha" overlay.lines[0]!assertEqual ":float parses newline escape (line 2)" "beta" overlay.lines[1]!| none =>assertEqual ":float newline overlay exists" true falselet s4 ← runKeys s3 ([Key.char ':'] ++ keys "nofloat" ++ [Key.enter])assertEqual ":nofloat clears overlay" false s4.floatingOverlay.isSomelet s5 ← runKeys s0 ([Key.char ':'] ++ keys "redraw" ++ [Key.enter])assertEqual ":redraw sets message" "redraw" s5.messageassertEqual ":redraw marks dirty" true s5.dirtylet s6 ← runKeys s0 ([Key.char ':'] ++ keys "redraw!" ++ [Key.enter])assertEqual ":redraw! alias works" "redraw" s6.messagelet s7 ← runKeys s0 [Key.ctrl 'l']assertEqual "Ctrl-l redraw marks dirty" true s7.dirtylet s8 ← runKeys s0 ([Key.char ':'] ++ keys "float --title Note --width 32 hello world" ++ [Key.enter])match s8.floatingOverlay with| some overlay =>assertEqual ":float --title sets title" "Note" overlay.titleassertEqual ":float --width sets width" 32 overlay.maxWidthassertEqual ":float with options keeps text" "hello world" overlay.lines[0]!| none =>assertEqual ":float with options shows overlay" true falselet s9 ← runKeys s0 ([Key.char ':'] ++ keys "float --title=Panel --width=28 hi" ++ [Key.enter])match s9.floatingOverlay with| some overlay =>assertEqual ":float --title= sets title" "Panel" overlay.titleassertEqual ":float --width= sets width" 28 overlay.maxWidth| none =>assertEqual ":float with inline options shows overlay" true falselet s10 ← runKeys s0 ([Key.char ':'] ++ keys "float --width nope hi" ++ [Key.enter])assertEqual ":float invalid width message" "Invalid float width: nope" s10.messagelet s11 ← runKeys s0 ([Key.char 'i'] ++ keys "abc" ++ [Key.esc] ++ [Key.char ':'] ++ keys "float guard" ++ [Key.enter])let s12 ← runKeys s11 [Key.char 'i']assertEqual "floating overlay enters insert mode" Mode.insert s12.modelet s13 ← runKeys s12 (keys "X" ++ [Key.enter] ++ keys "Y")match s13.floatingOverlay with| some overlay =>assertEqual "floating overlay writes text" "guardX" overlay.lines[0]!assertEqual "floating overlay writes next line" "Y" overlay.lines[1]!| none =>assertEqual "floating overlay remains open while editing" true falselet s14 ← runKeys s13 [Key.esc]assertEqual "Esc exits floating overlay insert mode" Mode.normal s14.modelet s15 ← runKeys s14 [Key.enter]assertEqual "Enter closes floating overlay" false s15.floatingOverlay.isSomelet sMsg0 := { s0 with message := "Error: sample message", dirty := true }let sMsg1 ← runKeys sMsg0 [Key.enter]assertEqual "Enter closes message float" "" sMsg1.messagelet sMsg2 := { s0 with message := "Cannot write preview buffer", dirty := true }let sMsg3 ← runKeys sMsg2 [Key.esc]assertEqual "Esc closes message float" "" sMsg3.messagelet sPrompt0 ← runKeys s0 ([Key.char ':'] ++ keys "ws list" ++ [Key.enter])let sPrompt1 := sPrompt0.updateActiveView fun v => { v with cursor := { row := ⟨2⟩, col := 0 } }let sPrompt2 ← runKeys sPrompt1 [Key.enter]assertEqual "Workspace explorer New opens floating input" true sPrompt2.floatingOverlay.isSomeassertEqual "Workspace explorer New sets floating command prefix" (some "ws new ") sPrompt2.floatingInputCommandlet sPrompt3 ← runKeys sPrompt2 (keys "TmpWS" ++ [Key.enter])assertEqual "Workspace explorer floating input submits with Enter" "TmpWS" sPrompt3.getCurrentWorkspace.nameassertEqual "Workspace explorer floating input closes after submit" false sPrompt3.floatingOverlay.isSomelet sWsCmd0 ← runKeys s0 ([Key.char ':'] ++ keys "ws new" ++ [Key.enter])assertEqual ":ws new opens floating input" true sWsCmd0.floatingOverlay.isSomeassertEqual ":ws new floating command prefix" (some "ws new ") sWsCmd0.floatingInputCommandlet sWsCmd1 ← runKeys sWsCmd0 (keys "CmdWorkspace" ++ [Key.enter])assertEqual ":ws new floating input submits name" "CmdWorkspace" sWsCmd1.getCurrentWorkspace.namelet sWgCmd0 ← runKeys s0 ([Key.char ':'] ++ keys "wg new" ++ [Key.enter])assertEqual ":wg new opens floating input" true sWgCmd0.floatingOverlay.isSomeassertEqual ":wg new floating command prefix" (some "wg new ") sWgCmd0.floatingInputCommandlet sWgCmd1 ← runKeys sWgCmd0 (keys "CmdGroup" ++ [Key.enter])assertEqual ":wg new floating input submits name" "CmdGroup" sWgCmd1.getCurrentWorkgroup.namelet stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-explorer-create-{stamp}"IO.FS.createDirAll tmpRootlet sExp0 := { s0 with windowHeight := 30, windowWidth := 100 }let sExp1 ← runKeys sExp0 ([Key.char ':'] ++ keys s!"ex list {tmpRoot}" ++ [Key.enter])let explorerOpt := sExp1.explorers.find? (fun (id, _) => id == sExp1.getActiveBuffer.id)match explorerOpt with| none =>assertEqual "Explorer create test has active explorer" true false| some (_, explorer) =>let newFileIdx := (findEntryIndex explorer.entries "[New File]").getD 0let sExp2 := sExp1.updateActiveView fun v => { v with cursor := { row := ⟨2 + newFileIdx⟩, col := 0 } }let sExp3 ← runKeys sExp2 [Key.enter]assertEqual "Explorer [New File] opens floating input" true sExp3.floatingOverlay.isSomeassertEqual "Explorer [New File] sets command prefix" (some s!"mkfile {tmpRoot}/") sExp3.floatingInputCommandlet sExp4 ← runKeys sExp3 (keys "alpha.txt" ++ [Key.enter])let fileCreated ← (System.FilePath.mk s!"{tmpRoot}/alpha.txt").pathExistsassertEqual "Explorer [New File] creates file" true fileCreatedlet explorerOpt2 := sExp4.explorers.find? (fun (id, _) => id == sExp4.getActiveBuffer.id)match explorerOpt2 with| none =>assertEqual "Explorer refreshed after file create" true false| some (_, explorer2) =>assertEqual "Explorer list includes created file" true (findEntryIndex explorer2.entries "alpha.txt").isSomelet newDirIdx := (findEntryIndex explorer.entries "[New Directory]").getD 1let sExp5 := sExp4.updateActiveView fun v => { v with cursor := { row := ⟨2 + newDirIdx⟩, col := 0 } }let sExp6 ← runKeys sExp5 [Key.enter]assertEqual "Explorer [New Directory] opens floating input" true sExp6.floatingOverlay.isSomeassertEqual "Explorer [New Directory] sets command prefix" (some s!"mkdir {tmpRoot}/") sExp6.floatingInputCommandlet sExp7 ← runKeys sExp6 (keys "subdir" ++ [Key.enter])let createdDirPath := System.FilePath.mk s!"{tmpRoot}/subdir"let dirExists ← createdDirPath.pathExistslet dirIsDir ← if dirExists then createdDirPath.isDir else pure falseassertEqual "Explorer [New Directory] creates directory" true dirIsDirlet explorerOpt3 := sExp7.explorers.find? (fun (id, _) => id == sExp7.getActiveBuffer.id)match explorerOpt3 with| none =>assertEqual "Explorer refreshed after directory create" true false| some (_, explorer3) =>assertEqual "Explorer list includes created directory" true (findEntryIndex explorer3.entries "subdir").isSomelet s16 ← runKeys s0 ([Key.char ':'] ++ keys "vs" ++ [Key.enter])let ws16 := s16.getCurrentWorkspaceassertEqual "split creates second window" true (ws16.nextWindowId >= 2)let s17 ← runKeys s16 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let ws17 := s17.getCurrentWorkspaceassertEqual ":floatwin on marks active window floating" true (ws17.isFloatingWindow ws17.activeWindowId)let s18 ← runKeys s17 ([Key.char ':'] ++ keys "floatwin off" ++ [Key.enter])let ws18 := s18.getCurrentWorkspaceassertEqual ":floatwin off clears floating flag" false (ws18.isFloatingWindow ws18.activeWindowId)let s19 ← runKeys s18 ([Key.char ':'] ++ keys "floatwin" ++ [Key.enter])let ws19 := s19.getCurrentWorkspaceassertEqual ":floatwin toggles floating flag" true (ws19.isFloatingWindow ws19.activeWindowId)let sFloatMove0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatMove1 ← runKeys sFloatMove0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let floatMoveId := sFloatMove1.getCurrentWorkspace.activeWindowIdlet beforeFloatMove := sFloatMove1.getFloatingWindowBounds floatMoveIdlet sFloatMove2 ← runKeys sFloatMove1 [Key.alt 'L']let afterFloatMove := sFloatMove2.getFloatingWindowBounds floatMoveIdmatch beforeFloatMove, afterFloatMove with| some (_, left0, _, _), some (_, left1, _, _) =>assertEqual "Alt-Shift-l moves floating window right" (left0 + 1) left1| _, _ =>assertEqual "Alt-Shift-l moves floating window right" true falselet sFloatMoveGrp0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatMoveGrp1 ← runKeys sFloatMoveGrp0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatMoveGrp2 ← runKeys sFloatMoveGrp1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let moveGrpIds := sFloatMoveGrp2.getCurrentWorkspace.getFloatingWindowIds.toListlet beforeGrp := moveGrpIds.filterMap (fun wid => (sFloatMoveGrp2.getFloatingWindowBounds wid).map (fun b => (wid, b)))let sFloatMoveGrp3 ← runKeys sFloatMoveGrp2 [Key.alt 'J']let afterGrp := moveGrpIds.filterMap (fun wid => (sFloatMoveGrp3.getFloatingWindowBounds wid).map (fun b => (wid, b)))let movedAllDown :=moveGrpIds.all (fun wid =>match beforeGrp.find? (fun (id, _) => id == wid), afterGrp.find? (fun (id, _) => id == wid) with| some (_, (top0, _, _, _)), some (_, (top1, _, _, _)) => top1 == top0 + 1| _, _ => false)assertEqual "Alt-Shift-j moves all panes in active floating subtree down" true movedAllDownlet sFloatVs0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatVs1 ← runKeys sFloatVs0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatVs2 ← runKeys sFloatVs1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let wsFloatVs2 := sFloatVs2.getCurrentWorkspacelet floatingIdsVs := wsFloatVs2.getFloatingWindowIds.toListassertEqual "vsplit in floating window keeps new pane floating" true (floatingIdsVs.length >= 2)match floatingIdsVs with| a :: b :: _ =>let sideBySide :=match sFloatVs2.getFloatingWindowBounds a, sFloatVs2.getFloatingWindowBounds b with| some (t1, l1, h1, w1), some (t2, l2, h2, w2) =>t1 == t2 && h1 == h2 && ((l1 + w1 == l2) || (l2 + w2 == l1))| _, _ => falseassertEqual "vsplit in floating window keeps pair side-by-side" true sideBySide| _ =>assertEqual "vsplit in floating window keeps pair side-by-side" true falselet sFloatSp0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatSp1 ← runKeys sFloatSp0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatSp2 ← runKeys sFloatSp1 ([Key.char ':'] ++ keys "split" ++ [Key.enter])let wsFloatSp2 := sFloatSp2.getCurrentWorkspacelet floatingIdsSp := wsFloatSp2.getFloatingWindowIds.toListassertEqual "split in floating window keeps new pane floating" true (floatingIdsSp.length >= 2)match floatingIdsSp with| a :: b :: _ =>let stacked :=match sFloatSp2.getFloatingWindowBounds a, sFloatSp2.getFloatingWindowBounds b with| some (t1, l1, h1, w1), some (t2, l2, h2, w2) =>l1 == l2 && w1 == w2 && ((t1 + h1 == t2) || (t2 + h2 == t1))| _, _ => falseassertEqual "split in floating window keeps pair stacked" true stacked| _ =>assertEqual "split in floating window keeps pair stacked" true falselet sFloatMix0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatMix1 ← runKeys sFloatMix0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatMix2 ← runKeys sFloatMix1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let sFloatMix3 ← runKeys sFloatMix2 ([Key.char ':'] ++ keys "split" ++ [Key.enter])let wsFloatMix3 := sFloatMix3.getCurrentWorkspacelet floatingIdsMix := wsFloatMix3.getFloatingWindowIds.toListassertEqual "vsplit then split in floating window keeps three panes floating" 3 floatingIdsMix.lengthlet boundsMix := floatingIdsMix.filterMap (fun wid => sFloatMix3.getFloatingWindowBounds wid)assertEqual "vsplit then split in floating window resolves bounds for all panes" 3 boundsMix.lengthlet overlaps (a b : Nat × Nat × Nat × Nat) : Bool :=let (ta, la, ha, wa) := alet (tb, lb, hb, wb) := bla < lb + wb && lb < la + wa && ta < tb + hb && tb < ta + halet rec hasOverlap (xs : List (Nat × Nat × Nat × Nat)) : Bool :=match xs with| [] => false| x :: rest => rest.any (fun y => overlaps x y) || hasOverlap restassertEqual "vsplit then split in floating window panes do not overlap" false (hasOverlap boundsMix)let sFloatDeep0 := { s0 with windowHeight := 24, windowWidth := 80 }let sFloatDeep1 ← runKeys sFloatDeep0 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let sFloatDeep2 ← runKeys sFloatDeep1 ([Key.char ':'] ++ keys "vs" ++ [Key.enter])let sFloatDeep3 ← runKeys sFloatDeep2 ([Key.char ':'] ++ keys "hs" ++ [Key.enter])let sFloatDeep4 ← runKeys sFloatDeep3 ([Key.char ':'] ++ keys "vs" ++ [Key.enter])let wsFloatDeep4 := sFloatDeep4.getCurrentWorkspacelet floatingIdsDeep := wsFloatDeep4.getFloatingWindowIds.toListassertEqual "vs -> hs -> vs in floating window creates four panes" 4 floatingIdsDeep.lengthlet boundsDeep := floatingIdsDeep.filterMap (fun wid => sFloatDeep4.getFloatingWindowBounds wid)assertEqual "vs -> hs -> vs in floating window resolves bounds for all panes" 4 boundsDeep.lengthassertEqual "vs -> hs -> vs in floating window panes do not overlap" false (hasOverlap boundsDeep)let sideBySideTouch (a b : Nat × Nat × Nat × Nat) : Bool :=let (t1, l1, h1, w1) := alet (t2, l2, h2, w2) := b((l1 + w1 == l2) || (l2 + w2 == l1)) && (t1 < t2 + h2 && t2 < t1 + h1)let stackedTouch (a b : Nat × Nat × Nat × Nat) : Bool :=let (t1, l1, h1, w1) := alet (t2, l2, h2, w2) := b((t1 + h1 == t2) || (t2 + h2 == t1)) && (l1 < l2 + w2 && l2 < l1 + w1)let rec hasPairWith(pred : (Nat × Nat × Nat × Nat) → (Nat × Nat × Nat × Nat) → Bool)(xs : List (Nat × Nat × Nat × Nat)) : Bool :=match xs with| [] => false| x :: rest => rest.any (fun y => pred x y) || hasPairWith pred restassertEqual "vs -> hs -> vs in floating window keeps horizontal adjacency" true (hasPairWith sideBySideTouch boundsDeep)assertEqual "vs -> hs -> vs in floating window keeps vertical adjacency" true (hasPairWith stackedTouch boundsDeep)let longLine := "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"let longText := String.intercalate "\n" [longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine, longLine] ++ "\n"let s20 := { s0 with windowHeight := 14, windowWidth := 40 }let s21 ← runKeys s20 ([Key.char 'i'] ++ keys longText ++ [Key.esc] ++ keys "gg0")let s22 ← runKeys s21 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let s23 ← runKeys s22 [Key.char '1', Key.char '0', Key.char 'j']let (sRow23, _) := s23.getScrollassertEqual "floating window vertical scroll follows cursor" true (sRow23.val > 0)let s24 ← runKeys s23 [Key.char '3', Key.char '5', Key.char 'l']let (_, sCol24) := s24.getScrollassertEqual "floating window horizontal scroll follows cursor" true (sCol24.val > 0)let s25 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\n" ++ [Key.esc] ++ keys "gg0")let s26 ← runKeys s25 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let s27 ← runKeys s26 [Key.char 'v', Key.char 'l', Key.char 'd']assertBuffer "visual mode edits active floating window buffer" s27 "cd\n"let s28 ← runKeys s0 ([Key.char 'i'] ++ keys "abcd\nefgh\n" ++ [Key.esc] ++ keys "gg0")let s29 ← runKeys s28 ([Key.char ':'] ++ keys "floatwin on" ++ [Key.enter])let s30 ← runKeys s29 [Key.char 'l', Key.ctrl 'v', Key.char 'l', Key.char 'j', Key.char 'd']assertBuffer "visual block edits active floating window buffer" s30 "ad\neh\n"def testBufferExplorerCommand : IO Unit := doIO.println " Testing Buffer Explorer..."let s0 := ViE.initialStatelet stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-buffer-explorer-{stamp}"IO.FS.createDirAll tmpRootlet bufAPath := s!"{tmpRoot}/bufA.txt"let bufBPath := s!"{tmpRoot}/bufB.txt"IO.FS.writeFile bufAPath "alpha\n"IO.FS.writeFile bufBPath "beta\n"let sBuf0 := { s0 with windowHeight := 30, windowWidth := 100 }let sBuf1 ← runKeys sBuf0 ([Key.char ':'] ++ keys s!"e {bufAPath}" ++ [Key.enter])let sBuf2 ← runKeys sBuf1 ([Key.char ':'] ++ keys s!"e {bufBPath}" ++ [Key.enter])let targetBufIdOpt := sBuf2.getCurrentWorkspace.buffers.find? (fun b => b.filename == some bufAPath) |>.map (fun b => b.id)assertEqual "Buffer explorer target buffer exists" true targetBufIdOpt.isSomelet targetBufId := targetBufIdOpt.getD 0let sBuf3 ← runKeys sBuf2 ([Key.char ':'] ++ keys "buf list" ++ [Key.enter])assertEqual ":buf list opens buffer explorer" true ((sBuf3.getActiveBuffer.filename.getD "").startsWith "explorer://buffers")let explorerBufId := sBuf3.getActiveBuffer.idlet explorerBufOpt := sBuf3.explorers.find? (fun (id, _) => id == explorerBufId)match explorerBufOpt with| none =>assertEqual "Buffer explorer registered" true false| some (_, explorerBuf) =>let targetPath := s!"buffer://{targetBufId}"let targetIdxOpt := findEntryIndexByPath explorerBuf.entries targetPathassertEqual "Buffer explorer contains target buffer entry" true targetIdxOpt.isSomelet targetIdx := targetIdxOpt.getD 0let sBuf4 := sBuf3.updateActiveView fun v => { v with cursor := { row := ⟨2 + targetIdx⟩, col := 0 } }let sBuf5 ← runKeys sBuf4 [Key.enter]assertEqual "Buffer explorer Enter switches active buffer" (some bufAPath) sBuf5.getActiveBuffer.filenameassertEqual "Buffer explorer closes after selection" false (sBuf5.explorers.any (fun (id, _) => id == explorerBufId))let sBufCompat ← runKeys sBuf2 ([Key.char ':'] ++ keys "buffers" ++ [Key.enter])assertEqual ":buffers alias opens buffer explorer" true ((sBufCompat.getActiveBuffer.filename.getD "").startsWith "explorer://buffers")let sBufAlias ← runKeys sBuf2 ([Key.char ':'] ++ keys "ls" ++ [Key.enter])assertEqual ":ls alias opens buffer explorer" true ((sBufAlias.getActiveBuffer.filename.getD "").startsWith "explorer://buffers")def testExplorerCommandAliases : IO Unit := doIO.println " Testing Explorer Command Names..."let s0 := ViE.initialStatelet stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-ex-alias-{stamp}"IO.FS.createDirAll tmpRootIO.FS.writeFile s!"{tmpRoot}/x.txt" "x\n"let s1 ← runKeys s0 ([Key.char ':'] ++ keys s!"ex list {tmpRoot}" ++ [Key.enter])assertEqual ":ex list opens file explorer" true ((s1.getActiveBuffer.filename.getD "").startsWith "explorer://")let s2 ← runKeys s0 ([Key.char ':'] ++ keys s!"ee {tmpRoot}" ++ [Key.enter])assertEqual ":ee alias opens file explorer" true ((s2.getActiveBuffer.filename.getD "").startsWith "explorer://")let s3 ← runKeys s0 ([Key.char ':'] ++ keys "wgex" ++ [Key.enter])assertEqual ":wgex alias opens workgroup explorer" (some "explorer://workgroups") s3.getActiveBuffer.filenamedef testExplorerOpenUsesFocusedWindow : IO Unit := doIO.println " Testing Explorer Open Target Window..."let s0 := { ViE.initialState with windowHeight := 30, windowWidth := 100 }let stamp ← IO.monoMsNowlet tmpRoot := s!"/tmp/vie-ex-target-{stamp}"IO.FS.createDirAll tmpRootlet leftPath := s!"{tmpRoot}/left.txt"let rightPath := s!"{tmpRoot}/right.txt"let targetPath := s!"{tmpRoot}/target.txt"IO.FS.writeFile leftPath "left\n"IO.FS.writeFile rightPath "right\n"IO.FS.writeFile targetPath "target\n"let s1 ← runKeys s0 ([Key.char ':'] ++ keys s!"e {leftPath}" ++ [Key.enter])let s2 ← runKeys s1 ([Key.char ':'] ++ keys "vsplit" ++ [Key.enter])let rightWinId := s2.getCurrentWorkspace.activeWindowIdlet s3 ← runKeys s2 ([Key.char ':'] ++ keys s!"e {rightPath}" ++ [Key.enter])let s4 ← runKeys s3 ([Key.char ':'] ++ keys "wincmd w" ++ [Key.enter])let targetWinId := s4.getCurrentWorkspace.activeWindowIdassertEqual "wincmd w changes active target window" true (targetWinId != rightWinId)let s5 ← runKeys s4 ([Key.char ':'] ++ keys s!"ex list {tmpRoot}" ++ [Key.enter])let explorerBufId := s5.getActiveBuffer.idlet explorerOpt := s5.explorers.find? (fun (id, _) => id == explorerBufId)match explorerOpt with| none =>assertEqual "File explorer registered" true false| some (_, explorer) =>let targetIdxOpt := findEntryIndex explorer.entries "target.txt"assertEqual "Explorer contains target file" true targetIdxOpt.isSomelet targetIdx := targetIdxOpt.getD 0let s6 := s5.updateActiveView fun v => { v with cursor := { row := ⟨2 + targetIdx⟩, col := 0 } }let s7 ← runKeys s6 [Key.enter]assertEqual "Explorer Enter uses focused window from before open" targetWinId s7.getCurrentWorkspace.activeWindowIdassertEqual "Selected file opens in focused window" (some targetPath) s7.getActiveBuffer.filenamelet ws7 := s7.getCurrentWorkspacematch ws7.layout.findView rightWinId with| none =>assertEqual "Other split window still exists" true false| some rightView =>let rightBufName := ws7.buffers.find? (fun b => b.id == rightView.bufferId) |>.bind (fun b => b.filename)assertEqual "Non-focused split keeps prior buffer" (some rightPath) rightBufNamedef testLeanCompletionPopupBehavior : IO Unit := doIO.println " Testing Lean Completion Popup..."let popup : CompletionPopup := {items := #[{ label := "foo", insertText := "foo" },{ label := "foobar", insertText := "foobar" }]selected := 0anchorRow := 0anchorCol := 2}let s0 : EditorState :=({ ViE.initialState with mode := .insert, completionPopup := some popup }).updateActiveBuffer fun b =>{ b with filename := some "Main.lean", table := ViE.PieceTable.fromString "ab" }let s0 := s0.setCursor { row := ⟨0⟩, col := ⟨2⟩ }let s1 ← runKeys s0 [Key.char 'c']assertBuffer "completion popup keeps showing on char insert" s1 "abc"assertEqual "completion popup remains visible after char insert" true s1.completionPopup.isSomelet s2 ← runKeys s1 [Key.enter]assertBuffer "Enter inserts newline instead of accepting completion" s2 "abc\n"assertCursor "Enter moves cursor to next line head with completion popup" s2 1 0assertEqual "Enter closes completion popup" true s2.completionPopup.isNonedef test : IO Unit := doIO.println "Starting Expanded Keybind Tests..."testMotionstestEditingtestOperatorstestVisualtestCountedtestVimCompatMotionstestWorkgroupSwitchtestSearchtestCommandSubstitutetestCommandGlobaltestCursorDriftCustomTabStoptestCommandBloomtestUiCommandstestBufferExplorerCommandtestExplorerCommandAliasestestExplorerOpenUsesFocusedWindowtestLeanCompletionPopupBehaviorIO.println "All Expanded Keybind Tests passed!"end ViETest.Keybinds
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Typesimport ViE.State.Configimport ViE.Configimport ViE.Key.Mapimport ViE.Command.Implimport ViETest.Utilsopen ViEopen ViETest.Utilsnamespace ViETest.Integration-- Helper to construct a full Configdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}-- Run a sequence of keysdef runKeys (startState : EditorState) (keys : List Key) : IO EditorState := dolet config := makeTestConfiglet mut s := startStatefor k in keys dos ← ViE.update config s kreturn s-- Helper: Convert string to list of char keysdef keys (s : String) : List Key :=s.toList.map Key.chardef test : IO Unit := doIO.println "Starting Integration ViETest..."-- Test 1: Typing "abc" -> Undo -> Insert "d"-- Expected: "abc" -> "" -> "d"-- Bug Report: After Undo, Insert mode might fail or buffer might be corruptedlet s0 := ViE.initialState-- 1. Insert "abc"-- i a b c Esclet input1 := [Key.char 'i'] ++ keys "abc" ++ [Key.esc]let s1 ← runKeys s0 input1assertBuffer "Text after insert abc" s1 "abc"-- 2. Undo-- ulet s2 ← runKeys s1 [Key.char 'u']assertBuffer "Text after undo" s2 ""assertCursor "Cursor after undo should be (0,0)" s2 0 0-- 3. Insert "d"-- i d Esclet s3 ← runKeys s2 [Key.char 'i', Key.char 'd', Key.esc]assertBuffer "Text after re-insert d" s3 "d"assertCursor "Cursor after insert 'd' and Esc should be (0,0)" s3 0 0IO.println "IntegrationTest passed!"end ViETest.Integration
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViEimport ViETest.Utilsnamespace ViETest.InfoViewopen ViETest.Utilsdef makeTestConfig : ViE.Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}def test : IO Unit := doIO.println "Starting InfoView ViETest..."let s0 : ViE.EditorState :=({ ViE.initialState with message := "", floatingOverlay := none, mode := .normal, infoViewRequested := false }).updateActiveBuffer fun b =>{ b with filename := some "Main.lean" }let s0' ← ViE.Lsp.Lean.syncInfoViewWindow s0let ws0 := s0'.getCurrentWorkspaceassertEqual "InfoView hidden when not requested" 1 (ViE.Window.getWindowIds ws0.layout |>.length)let sNonLean := ({ s0 with infoViewRequested := true }).updateActiveBuffer fun b =>{ b with filename := some "note.md" }let sNonLean' ← ViE.Lsp.Lean.syncInfoViewWindow sNonLeanlet wsNonLean := sNonLean'.getCurrentWorkspaceassertEqual "InfoView hidden for non-Lean buffer" 1 (ViE.Window.getWindowIds wsNonLean.layout |>.length)let sLean := ({ s0 with infoViewRequested := true }).updateActiveBuffer fun b =>{ b with table := ViE.PieceTable.fromString "abc\n" }let sLean' ← ViE.Lsp.Lean.syncInfoViewWindow sLeanlet wsLean := sLean'.getCurrentWorkspaceassertEqual "InfoView split creates second window" 2 (ViE.Window.getWindowIds wsLean.layout |>.length)let infoBufOpt := wsLean.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)assertEqual "InfoView buffer exists" true infoBufOpt.isSomematch infoBufOpt with| none =>assertEqual "InfoView content check skipped unexpectedly" true false| some infoBuf =>let line0 := ViE.getLineFromBuffer infoBuf 0 |>.getD ""let line1 := ViE.getLineFromBuffer infoBuf 1 |>.getD ""assert "InfoView includes file line" (line0.startsWith "File: Main.lean")assertEqual "InfoView position line" "Position: 1:1 Byte: 0" line1let cfg := makeTestConfiglet sMoved ← ViE.update cfg sLean' (ViE.Key.char 'l')let wsMoved := sMoved.getCurrentWorkspacelet movedInfoBufOpt := wsMoved.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)assertEqual "InfoView buffer exists after cursor move" true movedInfoBufOpt.isSomematch movedInfoBufOpt with| none =>assertEqual "InfoView cursor move check skipped unexpectedly" true false| some infoBuf =>let line1 := ViE.getLineFromBuffer infoBuf 1 |>.getD ""assertEqual "InfoView position updates on cursor move" "Position: 1:2 Byte: 1" line1let sOff := { sLean' with infoViewRequested := false }let sOff' ← ViE.Lsp.Lean.syncInfoViewWindow sOfflet wsOff := sOff'.getCurrentWorkspaceassertEqual "InfoView off closes split window" 1 (ViE.Window.getWindowIds wsOff.layout |>.length)assertEqual "InfoView off removes buffer" true ((wsOff.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)).isNone)let sInfoOnly := ViE.Window.closeActiveWindow sLean'let sInfoOnly' ← ViE.Lsp.Lean.syncInfoViewWindow sInfoOnlylet wsInfoOnly := sInfoOnly'.getCurrentWorkspaceassertEqual "InfoView-only fallback requests quit" true sInfoOnly'.shouldQuitassertEqual "InfoView-only fallback removes InfoView buffer" true ((wsInfoOnly.buffers.find? (fun b => b.filename == some ViE.Lsp.Lean.infoViewVirtualPath)).isNone)IO.println "InfoView Test passed!"end ViETest.InfoView
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.Command.Explorerimport ViE.Window.Analysisimport ViE.Window.Actionsimport ViE.BlikuAdapterimport ViETest.Utilsnamespace ViETest.ExplorerPreviewopen ViETest.Utilsopen ViEdef findEntryIndex (entries : List FileEntry) (name : String) : Option Nat :=let rec loop (list : List FileEntry) (idx : Nat) : Option Nat :=match list with| [] => none| e :: rest =>if e.name == name thensome idxelseloop rest (idx + 1)loop entries 0def test : IO Unit := doIO.println "Starting Explorer Preview ViETest..."let s0 := { ViE.initialState with windowHeight := 40, windowWidth := 120 }let path := "ViETest/test_paths/dir0/dir1"let s1 ← ViE.Feature.openExplorer s0 pathlet buf := s1.getActiveBufferlet ws1 := s1.getCurrentWorkspaceassert "File explorer opens in regular window" (!ws1.isFloatingWindow ws1.activeWindowId)let model1 := ViE.BlikuAdapter.toModel s1let explorerClustered :=match model1.workspace.floatingClusters[0]? with| some cluster =>match cluster.root, cluster.sizePolicy with| .group gid, .multiPane => gid == buf.id| _, _ => false| none => falseassert "Bliku adapter lifts explorer/preview into floating multi-pane cluster" explorerClusteredlet baseBufId := s0.getActiveBuffer.idlet baseWinId :=(ViE.Window.getWindowIds ws1.layout).find? (fun wid =>if wid == ws1.activeWindowId thenfalseelsematch ws1.layout.findView wid with| some v => v.bufferId == baseBufId| none => false)let baseWindowStillExists :=(ViE.Window.getWindowIds ws1.layout).any (fun wid =>if wid == ws1.activeWindowId thenfalseelsematch ws1.layout.findView wid with| some v => v.bufferId == baseBufId| none => false)assert "Opening explorer keeps original buffer window intact" baseWindowStillExistslet explorerOpt := s1.explorers.find? (fun (id, _) => id == buf.id)match explorerOpt with| none =>throw (IO.userError "Explorer buffer not registered")| some (_, explorer) =>let idx1 := findEntryIndex explorer.entries "file1.txt"let idx2 := findEntryIndex explorer.entries "file2.txt"assert "file1.txt exists in explorer entries" idx1.isSomeassert "file2.txt exists in explorer entries" idx2.isSomelet row1 : Row := ⟨2 + idx1.get!⟩let row2 : Row := ⟨2 + idx2.get!⟩let exAfterOpen := s1.explorers.find? (fun (id, _) => id == buf.id) |>.map (fun (_, ex) => ex)match exAfterOpen with| none => throw (IO.userError "Explorer buffer not registered after open")| some exOpen =>assert "Preview window created on open" exOpen.previewWindowId.isSomelet previewRegular :=match exOpen.previewWindowId with| some wid => !s1.getCurrentWorkspace.isFloatingWindow wid| none => falseassert "Preview window opens in regular window" previewRegularlet pairSideBySide :=match exOpen.previewWindowId with| some wid =>let bounds := ViE.Window.getAllWindowBounds s1.getCurrentWorkspace.layout(if s1.windowHeight > 0 then s1.windowHeight - 1 else 0) s1.windowWidthmatch bounds.find? (fun (id, _, _, _, _) => id == s1.getCurrentWorkspace.activeWindowId),bounds.find? (fun (id, _, _, _, _) => id == wid) with| some (_, et, el, eh, ew), some (_, pt, pl, ph, pw) =>et == pt && eh == ph && ((el + ew <= pl) || (pl + pw <= el))| _, _ => false| none => falseassert "Explorer/preview pair is side-by-side" pairSideBySidelet s2 := s1.updateActiveView fun v => { v with cursor := { row := row1, col := 0 } }let s3 ← ViE.Feature.refreshExplorerPreview s2let exAfterOpt := s3.explorers.find? (fun (id, _) => id == buf.id)match exAfterOpt with| none =>throw (IO.userError "Explorer buffer not registered after preview")| some (_, exAfter) =>assert "Preview window created" exAfter.previewWindowId.isSomeassert "Preview buffer created" exAfter.previewBufferId.isSomelet ws := s3.getCurrentWorkspacelet previewWinId := exAfter.previewWindowId.get!let previewView := ws.layout.findView previewWinId |>.getD initialViewlet previewBuf := ws.buffers.find? (fun b => b.id == previewView.bufferId) |>.getD initialBufferassert "Preview filename has prefix" ((previewBuf.filename.getD "").startsWith "preview://")assert "Preview filename contains file1.txt" ((previewBuf.filename.getD "").contains "file1.txt")let dirIdx := findEntryIndex exAfter.entries "dir0"assert "dir0 exists in explorer entries" dirIdx.isSomelet dirRow : Row := ⟨2 + dirIdx.get!⟩let s3a := s3.updateActiveView fun v => { v with cursor := { row := dirRow, col := 0 } }let s3b ← ViE.Feature.handleExplorerEnter s3alet idsAfterDir := ViE.Window.getWindowIds s3b.getCurrentWorkspace.layoutassert "Preview window kept on directory navigation" (idsAfterDir.contains previewWinId)let newExplorerOpt := s3b.explorers.find? (fun (id, _) => id == s3b.getActiveBuffer.id)match newExplorerOpt with| none => throw (IO.userError "Explorer buffer not registered after directory nav")| some (_, exAfterDir) =>assert "Preview window id preserved on directory nav" (exAfterDir.previewWindowId == exAfter.previewWindowId)let s4 := s3.updateActiveView fun v => { v with cursor := { row := row2, col := 0 } }let s5 ← ViE.Feature.refreshExplorerPreview s4let ws5 := s5.getCurrentWorkspacelet previewView2 := ws5.layout.findView previewWinId |>.getD initialViewlet previewBuf2 := ws5.buffers.find? (fun b => b.id == previewView2.bufferId) |>.getD initialBufferassert "Preview filename updates to file2.txt" ((previewBuf2.filename.getD "").contains "file2.txt")let s5a := s5.updateActiveView fun v => { v with cursor := { row := row2, col := 0 } }let explorerWinId := s5a.getCurrentWorkspace.activeWindowIdlet s5b ← ViE.Feature.handleExplorerEnter s5alet ws5b := s5b.getCurrentWorkspaceassert "Opening file from explorer leaves regular window" (!ws5b.isFloatingWindow ws5b.activeWindowId)match baseWinId with| some wid =>assert "Opening file from explorer uses original buffer window" (ws5b.activeWindowId == wid)| none =>throw (IO.userError "Base window id not found after explorer open")let idsAfterOpen := ViE.Window.getWindowIds s5b.getCurrentWorkspace.layoutassert "Explorer window removed on file open" (!idsAfterOpen.contains explorerWinId)assert "Preview window removed on file open" (!idsAfterOpen.contains previewWinId)let bufIdsAfterOpen := s5b.getCurrentWorkspace.buffers.map (fun b => b.id)let previewBufId := exAfter.previewBufferId.getD 0assert "Preview buffer removed on file open" (!bufIdsAfterOpen.contains previewBufId)let sClose := ViE.Window.closeActiveWindow s5let idsAfter := ViE.Window.getWindowIds sClose.getCurrentWorkspace.layoutassert "Preview window removed on explorer close" (!idsAfter.contains previewWinId)let bufIdsAfterClose := sClose.getCurrentWorkspace.buffers.map (fun b => b.id)assert "Preview buffer removed on explorer close" (!bufIdsAfterClose.contains previewBufId)let s6 ← ViE.Feature.toggleExplorerPreview s5let exAfterClose := s6.explorers.find? (fun (id, _) => id == buf.id) |>.map (fun (_, ex) => ex)match exAfterClose with| none => throw (IO.userError "Explorer buffer missing after close")| some exClose =>assert "Preview window cleared" exClose.previewWindowId.isNoneIO.println "Explorer Preview Test passed!"end ViETest.ExplorerPreview
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.Configimport ViE.Command.Implimport ViE.Key.Mapimport ViE.State.Editnamespace ViETest.CursorReproductionopen 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}")-- Helper to construct a full Configdef makeTestConfig : Config := {settings := ViE.defaultConfigcommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}-- Run a sequence of keysdef runKeys (startState : EditorState) (keys : List Key) : IO EditorState := dolet config := makeTestConfiglet mut s := startStatefor k in keys dos ← ViE.update config s kreturn sdef test : IO Unit := doIO.println "Starting Cursor Reproduction ViETest..."let s := ViE.initialState-- Scenario: Insert, Undo, Insertlet s1 := s.insertChar 'a'let s1 := s1.commitEdit -- Force separate undo grouplet s2 := s1.insertChar 'b'-- Text: 'ab', Cursor: (0, 2)let s3 := s2.undo-- Text: 'a', Cursor should be (0, 1)let cursor := s3.getCursorif cursor.col.val != 1 thenIO.println s!"[FAIL] Undo cursor mismatch. Expected 1, got {cursor.col.val}"assert "Undo cursor" falseelseIO.println "[PASS] Undo cursor correct"-- Scenario: Wide character insert moves cursor by display widthlet s := ViE.initialStatelet wide := Char.ofNat 0x3042 -- Hiragana A (wide)let s := s.insertChar widelet cursor := s.getCursorif cursor.col.val != 2 thenIO.println s!"[FAIL] Wide char cursor mismatch. Expected 2, got {cursor.col.val}"assert "Wide char cursor" falseelseIO.println "[PASS] Wide char cursor correct"let s4 := s3.insertChar 'c'-- Text: 'ac'let text := getLineFromBuffer s4.getActiveBuffer 0 |>.getD ""if text != "ac" thenIO.println s!"[FAIL] Insert after undo failed. Expected 'ac', got '{text}'"assert "Insert after undo" falseelseIO.println "[PASS] Insert after undo works"-- Scenario: Paste, Undo, Paste (checking grouping too, but focusing on cursor)-- Resetlet s := ViE.initialStatelet s := s.insertChar 'x'let s := s.yankCurrentLine -- Yank 'x\n'let _s := s.pasteBelow -- Paste 'x\n' -> 'x\nx\n' ??-- PasteBelow pastes line.-- Scenario: x commandlet s := ViE.initialStatelet s := s.insertChar 'h'let s := s.insertChar 'e'let s := s.insertChar 'y'-- "hey"let s := (s.moveCursorLeft).moveCursorLeft -- At 'h' (0,0)? No: 3 -> 2 -> 1. 'e'-- "hey" cursor at 2 ('y'). Left -> 1 ('e'). Left -> 0 ('h').-- Actually moveCursorLeft from (0,3) -> (0,2) 'y'.-- Let's use setCursorlet s := s.setCursor (Point.make 0 1) -- 'e'let s := s.deleteCharAfterCursor -- Should delete 'e' -> "hy"let text := getLineFromBuffer s.getActiveBuffer 0 |>.getD ""if text != "hy" thenIO.println s!"[FAIL] 'x' command (deleteCharAfterCursor) failed. Expected 'hy', got '{text}'"assert "x command" falseelseIO.println "[PASS] 'x' command works"-- Scenario: Insert mode transition + single-char delete keeps line count and cursor in sync.let s := ViE.initialStatelet s ← runKeys s [Key.char 'i', Key.enter]let c1 := s.getCursorassert "Enter in insert creates second line" (s.getActiveBuffer.lineCount == 2)assert "Enter in insert moves cursor to next line" (c1.row.val == 1 && c1.col.val == 0)let s ← runKeys s [Key.esc]let c2 := s.getCursorassert "Esc from insert keeps valid cursor row" (c2.row.val < s.getActiveBuffer.lineCount)assert "Esc from insert keeps row/col at second line head" (c2.row.val == 1 && c2.col.val == 0)-- Re-enter insert and delete one char (the previous newline) via Backspace.let s ← runKeys s [Key.char 'i', Key.backspace]let c3 := s.getCursorassert "Backspace at BOL joins lines" (s.getActiveBuffer.lineCount == 1)assert "Backspace at BOL moves cursor to joined line end" (c3.row.val == 0 && c3.col.val == 0)-- In normal mode x at empty line is a no-op and must keep cursor valid.let s ← runKeys s [Key.esc, Key.char 'x']let c4 := s.getCursorassert "x on empty line keeps line count" (s.getActiveBuffer.lineCount == 1)assert "x on empty line keeps cursor in range" (c4.row.val < s.getActiveBuffer.lineCount)IO.println "Cursor Reproduction Test Finished"end ViETest.CursorReproduction
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Appimport ViE.Buffer.Managerimport ViE.Checkpointimport ViE.State.Configimport ViETest.Utilsnamespace ViETest.Checkpointopen ViETest.Utilsopen ViEdef testLoadSessionInvalid : IO Unit := doIO.println "Starting Checkpoint Parse Invalid ViETest..."IO.FS.writeFile ViE.Checkpoint.sessionFile "--ACTIVE--\nnot-a-number\n"let loaded <- ViE.Checkpoint.loadSessionassertEqual "Invalid checkpoint parses as none" none loadeddef testLoadSessionValid : IO Unit := doIO.println "Starting Checkpoint Parse Valid ViETest..."let content :="/tmp/file-a.txt\n" ++"0 0\n" ++"/tmp/file-b.txt\n" ++"3 4\n" ++"--ACTIVE--\n" ++"1\n"IO.FS.writeFile ViE.Checkpoint.sessionFile contentlet loaded <- ViE.Checkpoint.loadSessionlet expected : Option (List String × Nat × List (Nat × Nat)) :=some (["/tmp/file-a.txt", "/tmp/file-b.txt"], 1, [(0, 0), (3, 4)])assertEqual "Valid checkpoint parsing" expected loadeddef testBuildRestoredWorkspace : IO Unit := doIO.println "Starting Restored Workspace Build ViETest..."let stamp <- IO.monoMsNowlet root := s!"/tmp/vie-checkpoint-{stamp}"IO.FS.createDirAll rootlet f1 := s!"{root}/a.txt"let f2 := s!"{root}/b.txt"IO.FS.writeFile f1 "alpha\nbeta\n"IO.FS.writeFile f2 "line0\nline1\nline2\n"let ws <- ViE.buildRestoredWorkspace ViE.defaultConfig (some root) [f1, f2] 1 [(0, 0), (1, 2)]assertEqual "Restored workspace buffer count" 2 ws.buffers.lengthassertEqual "Restored workspace nextBufferId" 2 ws.nextBufferIdlet active := ws.layout.findView ws.activeWindowId |>.getD ViE.initialViewassertEqual "Restored active buffer id" 1 active.bufferIdassertEqual "Restored cursor row" 1 active.cursor.row.valassertEqual "Restored cursor col" 2 active.cursor.col.valmatch ws.buffers with| b1 :: b2 :: _ =>assertEqual "Restored first file path" (some f1) b1.filenameassertEqual "Restored second file path" (some f2) b2.filenameassertEqual "Restored inactive buffer is lazy" false b1.loadedassertEqual "Restored active buffer is loaded" true b2.loaded| _ =>throw (IO.userError "Expected two restored buffers")let s0 := ({ ViE.initialState with config := ViE.defaultConfig }).updateCurrentWorkspace (fun _ => ws)let s1 := s0.updateCurrentWorkspace fun ws =>{ ws with layout := ws.layout.updateView ws.activeWindowId (fun v => { v with bufferId := 0 }) }let s2 ← ViE.Buffer.ensureActiveBufferLoaded s1let activeLoaded := s2.getActiveBufferassertEqual "Lazy buffer loads on activation" true activeLoaded.loadedassertEqual "Lazy loaded buffer keeps file path" (some f1) activeLoaded.filenamedef testBuildRestoredWorkspaceCustomTabStop : IO Unit := doIO.println "Starting Restored Workspace Custom TabStop ViETest..."let stamp <- IO.monoMsNowlet root := s!"/tmp/vie-checkpoint-tab-{stamp}"IO.FS.createDirAll rootlet f1 := s!"{root}/tab.txt"IO.FS.writeFile f1 "\talpha\n"let settings := { ViE.defaultConfig with tabStop := 8 }let ws <- ViE.buildRestoredWorkspace settings (some root) [f1] 0 [(0, 8)]let active := ws.layout.findView ws.activeWindowId |>.getD ViE.initialViewassertEqual "Restored custom tabStop cursor row" 0 active.cursor.row.valassertEqual "Restored custom tabStop cursor col" 8 active.cursor.col.valdef test : IO Unit := dotestLoadSessionInvalidtestLoadSessionValidtestBuildRestoredWorkspacetestBuildRestoredWorkspaceCustomTabStopend ViETest.Checkpoint
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.Buffer.Contentimport ViE.Typesimport ViE.Data.PieceTableopen ViEnamespace ViETest.Bufferdef 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 := doIO.println "Starting Buffer ViETest..."-- 1. Test fromString and getLinelet text := "Hello\nWorld"let pt := PieceTable.fromString textlet buf : FileBuffer := { initialFileBuffer with table := pt }-- Check linesmatch getLineFromBuffer buf 0 with| some l => assert "Line 0 is Hello" (l == "Hello")| none => assert "Line 0 missing" falsematch getLineFromBuffer buf 1 with| some l => assert "Line 1 is World" (l == "World")| none => assert "Line 1 missing" falsematch getLineFromBuffer buf 2 with| some _ => assert "Line 2 should be missing" false| none => assert "Line 2 correctly missing" true-- 2. Test FileBuffer <-> TextBuffer conversionlet tb : TextBuffer := #[ #['A', 'B'], #['C'] ] -- "AB\nC"let buf2 := ViE.Buffer.fileBufferFromTextBuffer 1 (some "test.txt") tbassert "Buffer created from TextBuffer id" (buf2.id == 1)assert "Buffer created from TextBuffer filename" (buf2.filename == some "test.txt")let tb2 := ViE.Buffer.fileBufferToTextBuffer buf2assert "Roundtrip TextBuffer size" (tb2.size == 2)if tb2.size == 2 thenlet l0 := tb2[0]!let l1 := tb2[1]!assert "Roundtrip Line 0" (String.ofList l0.toList == "AB")assert "Roundtrip Line 1" (String.ofList l1.toList == "C")IO.println "BufferTest passed!"end ViETest.Buffer
-- SPDX-FileCopyrightText: 2026 Yuki Otsuka---- SPDX-License-Identifier: BSD-3import ViE.State.Configimport ViE.Configimport ViE.Command.Implimport ViE.Key.Mapimport ViE.State.Editimport ViE.State.Searchimport ViE.Data.PieceTable.Treeimport ViE.UInamespace ViE.Benchmarkopen ViE/-- Mock Config for Benchmarking -/def makeBenchConfig (buildLeafBits : Option Bool := none) : Config :=let base := ViE.defaultConfiglet settings :=match buildLeafBits with| some v => { base with searchBloomBuildLeafBits := v }| none => base{settings := settingscommands := ViE.Command.defaultCommandMapbindings := ViE.Key.makeKeyMap ViE.Command.defaultCommandMap}structure BenchOptions whereiterations : Nat := 1000render : Bool := truecases : List String := []textLines : Nat := 200lineLen : Nat := 80warmup : Nat := 0buildLeafBits : Option Bool := nonelistOnly : Bool := falsedef parseArgs (args : List String) : BenchOptions :=let rec loop (opts : BenchOptions) (args : List String) : BenchOptions :=match args with| [] => opts| "--no-render" :: rest => loop { opts with render := false } rest| "--case" :: name :: rest => loop { opts with cases := opts.cases ++ [name] } rest| "--lines" :: n :: rest =>match n.toNat? with| some v => loop { opts with textLines := v } rest| none => loop opts rest| "--line-len" :: n :: rest =>match n.toNat? with| some v => loop { opts with lineLen := v } rest| none => loop opts rest| "--warmup" :: n :: rest =>match n.toNat? with| some v => loop { opts with warmup := v } rest| none => loop opts rest| "--bloom-leaf-bits" :: rest => loop { opts with buildLeafBits := some true } rest| "--no-bloom-leaf-bits" :: rest => loop { opts with buildLeafBits := some false } rest| "--list" :: rest => loop { opts with listOnly := true } rest| arg :: rest =>match arg.toNat? with| some v => loop { opts with iterations := v } rest| none => loop opts restloop {} argsdef timeCase (label : String) (iterations : Nat) (f : IO Unit) : IO Unit := dolet t0 ← IO.monoMsNowflet t1 ← IO.monoMsNowlet ms := t1 - t0let opsPerSec := if ms == 0 then 0 else (iterations * 1000) / msIO.println s!"[bench] {label}: {ms} ms ({opsPerSec} ops/s)"/-- Build a simple multi-line ASCII buffer for search benchmarks. -/def buildText (lines : Nat) (lineLen : Nat) : String :=let line := String.ofList (List.replicate lineLen 'a')String.intercalate "\n" (List.replicate lines line)/-- Insert a needle in the middle of text for search benchmarks. -/def buildSearchText (lines : Nat) (lineLen : Nat) (needle : String) : String :=let baseLine := String.ofList (List.replicate lineLen 'a')let mid := lines / 2let leftLen := lineLen / 2let rightLen := if lineLen > leftLen + needle.length then lineLen - leftLen - needle.length else 0let needleLine :=(String.ofList (List.replicate leftLen 'a')) ++needle ++(String.ofList (List.replicate rightLen 'a'))let linesArr := Id.run dolet mut arr := Array.replicate lines baseLineif mid < arr.size thenarr := arr.set! mid needleLinereturn arrString.intercalate "\n" linesArr.toList/-- Case: Large insert workload (EditorState). -/def benchInsert (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor i in [0:iterations] dos := s.insertChar 'a'if i % 100 == 0 thens := s.commitEdit/-- Case: Mixed small edits/movements. -/def benchEditMix (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor _ in [0:iterations] dos := s.insertChar 'a's := s.moveCursorLefts := s.insertChar 'b's := s.moveCursorRight/-- Case: Clipboard (yank/paste). -/def benchClipboard (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStates := s.insertChar 'x'for _ in [0:iterations/50] dos := s.yankCurrentLines := s.pasteBelow/-- Case: Workgroup churn. -/def benchWorkgroups (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor i in [0:iterations/200] dos := ← ViE.Command.cmdWg ["new", s!"BenchGroup {i}"] ss := s.insertChar 'w'for _ in [0:iterations/200] dos := ← ViE.Command.cmdWg ["close"] s/-- Case: Window splits/cycles. -/def benchWindows (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor _ in [0:iterations/500] dos := ViE.Window.splitWindow s trues := ViE.Window.splitWindow s falses := s.insertChar 'v's := ViE.Window.cycleWindow sfor _ in [0:iterations/200] dos := ViE.Window.closeActiveWindow s/-- Case: Undo/Redo stress. -/def benchUndoRedo (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor i in [0:iterations] dos := s.insertChar 'a'if i % 50 == 0 thens := s.commitEditfor _ in [0:iterations/10] dos := s.undofor _ in [0:iterations/10] dos := s.redo/-- Case: Search workload (PieceTable). -/def benchSearch (iterations : Nat) (useBloom : Bool) (lines lineLen : Nat) (buildLeafBits : Bool) (cacheMax : Nat) : IO Unit := dolet needle := "needle"let text := buildSearchText lines lineLen needlelet pt := PieceTable.fromString text buildLeafBitslet pattern := needle.toUTF8let mut offset := 0let mut cache : Lean.RBMap Nat ByteArray compare := Lean.RBMap.emptylet mut order : Array Nat := #[]for _ in [0:iterations] dolet (res, cache', order') := PieceTree.searchNext pt.tree pt pattern offset searchChunkSize useBloom cache order cacheMaxcache := cache'order := order'offset := match res with| some r => r + 1| none => 0/-- Case: Render workload. -/def benchRender (iterations : Nat) : IO Unit := dolet mut s := ViE.initialStatefor _ in [0:iterations/20] dos := s.insertChar 'a'let _ ← ViE.UI.render s/-- Generate large text content of specified size (in bytes). -/def buildLargeText (sizeBytes : Nat) : String :=let lineLen := 80let line := String.ofList (List.replicate lineLen 'a') ++ "\n"let lineBytes := line.utf8ByteSizelet numLines := sizeBytes / lineBytesString.intercalate "" (List.replicate numLines line)/-- Case: Load large file (1MB). -/def benchLoadLarge1MB (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024) -- 1MBlet _ := PieceTable.fromString text buildLeafBitspure ()/-- Case: Load large file (10MB). -/def benchLoadLarge10MB (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024) -- 10MBlet _ := PieceTable.fromString text buildLeafBitspure ()/-- Case: Load large file (100MB). -/def benchLoadLarge100MB (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (100 * 1024 * 1024) -- 100MBlet _ := PieceTable.fromString text buildLeafBitspure ()/-- Case: Insert at middle of large file (1MB). -/def benchInsertMidLarge1MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0-- Consume split results so optimizer cannot erase this loop.for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Insert at middle of large file (10MB). -/def benchInsertMidLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0-- Consume split results so optimizer cannot erase this loop.for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Split operation on large file (1MB). -/def benchSplitLarge1MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: Split operation on large file (10MB). -/def benchSplitLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet midOffset := pt.tree.stats.bytes.toNat / 2let mut checksum := 0for _ in [0:iterations] dolet (l, r) := PieceTree.split pt.tree midOffset ptchecksum := checksum + l.stats.bytes.toNat + r.stats.bytes.toNatif checksum == 0 thenIO.println "Zero bytes?"/-- Case: GetBytes operation on large file (1MB). -/def benchGetBytesLarge1MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet chunkSize := 1024 -- 1KB chunkslet mut checksum := 0for i in [0:iterations] dolet offset := (i * chunkSize) % pt.tree.stats.bytes.toNatlet bytes := PieceTree.getBytes pt.tree offset chunkSize ptchecksum := checksum + bytes.sizeif checksum == 0 thenIO.println "Zero bytes?"/-- Case: GetBytes operation on large file (10MB). -/def benchGetBytesLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := dolet text := buildLargeText (10 * 1024 * 1024)let pt := PieceTable.fromString text buildLeafBitslet chunkSize := 1024 -- 1KB chunkslet mut checksum := 0for i in [0:iterations] dolet offset := (i * chunkSize) % pt.tree.stats.bytes.toNatlet bytes := PieceTree.getBytes pt.tree offset chunkSize ptchecksum := checksum + bytes.sizeif checksum == 0 thenIO.println "Zero bytes?"def availableCases : List String :=[ "insert", "edit", "clipboard", "workgroups", "windows", "undo", "search-bloom", "search-linear", "render","load-1mb", "load-10mb", "load-100mb","insert-mid-1mb", "insert-mid-10mb","split-1mb", "split-10mb","getbytes-1mb", "getbytes-10mb" ]/-- Run benchmark cases. -/def runBenchmark (opts : BenchOptions) : IO Unit := dolet buildLeafBits := opts.buildLeafBits.getD ViE.defaultConfig.searchBloomBuildLeafBitslet config := makeBenchConfig (some buildLeafBits)let cacheMax := config.settings.searchBloomCacheMaxlet cases := if opts.cases.isEmpty then availableCases else opts.casesIO.println s!"Starting benchmark: iter={opts.iterations}, render={opts.render}"if opts.warmup > 0 thenIO.println s!"Warmup: {opts.warmup} iterations"for _ in [0:opts.warmup] dolet mut s := ViE.initialStates := s.insertChar 'a'let _ ← pure sfor c in cases domatch c with| "insert" => timeCase "insert" opts.iterations (benchInsert opts.iterations)| "edit" => timeCase "edit" opts.iterations (benchEditMix opts.iterations)| "clipboard" => timeCase "clipboard" opts.iterations (benchClipboard opts.iterations)| "workgroups" => timeCase "workgroups" opts.iterations (benchWorkgroups opts.iterations)| "windows" => timeCase "windows" opts.iterations (benchWindows opts.iterations)| "undo" => timeCase "undo/redo" opts.iterations (benchUndoRedo opts.iterations)| "search-bloom" =>timeCase "search-bloom" opts.iterations (benchSearch opts.iterations true opts.textLines opts.lineLen buildLeafBits cacheMax)| "search-linear" =>timeCase "search-linear" opts.iterations (benchSearch opts.iterations false opts.textLines opts.lineLen buildLeafBits cacheMax)| "render" =>if opts.render thentimeCase "render" opts.iterations (benchRender opts.iterations)elseIO.println "[bench] render skipped (--no-render)"| "load-1mb" =>let text := buildLargeText (1024 * 1024)timeCase "load-1mb" opts.iterations (for _ in [0:opts.iterations] dolet pt := PieceTable.fromString text buildLeafBitsif pt.tree.stats.bytes == 0 then IO.println "Zero bytes?" -- force usage)| "load-10mb" =>let text := buildLargeText (10 * 1024 * 1024)timeCase "load-10mb" opts.iterations (for _ in [0:opts.iterations] dolet pt := PieceTable.fromString text buildLeafBitsif pt.tree.stats.bytes == 0 then IO.println "Zero bytes?")| "load-100mb" =>let text := buildLargeText (100 * 1024 * 1024)timeCase "load-100mb" opts.iterations (for _ in [0:opts.iterations] dolet pt := PieceTable.fromString text buildLeafBitsif pt.tree.stats.bytes == 0 then IO.println "Zero bytes?")| "insert-mid-1mb" => timeCase "insert-mid-1mb" opts.iterations (benchInsertMidLarge1MB opts.iterations buildLeafBits)| "insert-mid-10mb" => timeCase "insert-mid-10mb" opts.iterations (benchInsertMidLarge10MB opts.iterations buildLeafBits)| "split-1mb" => timeCase "split-1mb" opts.iterations (benchSplitLarge1MB opts.iterations buildLeafBits)| "split-10mb" => timeCase "split-10mb" opts.iterations (benchSplitLarge10MB opts.iterations buildLeafBits)| "getbytes-1mb" => timeCase "getbytes-1mb" opts.iterations (benchGetBytesLarge1MB opts.iterations buildLeafBits)| "getbytes-10mb" => timeCase "getbytes-10mb" opts.iterations (benchGetBytesLarge10MB opts.iterations buildLeafBits)| other =>IO.println s!"[bench] Unknown case: {other}"IO.println "Benchmark completed."end ViE.Benchmark/-- CLI entrypoint. -/def main (args : List String) : IO Unit := dolet opts := ViE.Benchmark.parseArgs argsif opts.listOnly thenIO.println "Available cases:"IO.println (String.intercalate ", " ViE.Benchmark.availableCases)elseViE.Benchmark.runBenchmark opts