32SDOYNGPOAAMEQMZLZUE3XE6D5CFSZU6M3JDRERUVPAB4TLZOOAC TKAAHADV62OQORAO7MYRRRHTGPMG3GKXU4Z4MFNLAO6DLTSDU3CAC WRBKZMYVNHRWT7TTUGTDJ3TMWZB32QYW5PCLKTTVAJ2YF6OI3LTAC 6W6OJAUT3KLOXXKYR4GA3MG4ACLO2T4KX3SGD2KZDX6MYEZYTPHAC NBG3GUEJJMWS2FRJDU26KRQLA6O2YK77PDAPBMD3SKWLDTK23BVAC U45XPF3YKPXXRJ4MN24T2RV7GOL4FZKQSWX5P5I7WT4HC5XF4FHAC 5SFTBD4FW6GQPKRUJBAII5R2EZPDYG6CH57BIJD4IVZBE6JZB2ZQC CNJGJCJZ6LRHHIRSRATSE3D3Z5OQGSSTDMXCPVXSKRQLYJFME6IAC GUD2J453E3UTRJZIEEUUJ3ZIGL72ETUXTO57XQIZ2Y3ZU3C2R55QC DTAIE7PK6TZLNSBQGENS6RT7FFOTMAPSE6ZTPDADINGJF5NTHLWQC KJLCU4X5Z3CZM4AORMWIKOPBWPVTDMLK4LYH7UXBJFUGL7AUQCSQC OHL2PRHBX74FZKWZU7IR6RSXA36PDZDROVI4AHTX4SO7VQFQ5RQQC #[]elselet rec matchesAt (i j : Nat) : Bool :=if j == n then trueelse if haystack[i + j]! == needle[j]! then matchesAt i (j + 1) else falselet rec loop (i : Nat) (acc : Array (Nat × Nat)) : Array (Nat × Nat) :=if i + n > h then accelselet acc' := if matchesAt i 0 then acc.push (i, i + n) else accloop (i + 1) acc'loop 0 #[]
return #[]let limit := h - n + 1let mut out : Array (Nat × Nat) := #[]for i in [0:limit] dolet mut matched := truefor j in [0:n] doif matched && haystack[i + j]! != needle[j]! thenmatched := falseif matched thenout := out.push (i, i + n)return out
partial def findNextForward (buffer : FileBuffer) (row : Row) (col : Col) (started : Bool) : Row × Col :=let lineLen := lineDisplayWidth buffer rowif lineLen == 0 then(row, 0) -- Stop at empty lineelse if col.val >= lineLen then-- End of line, wrap to next lineif row.val + 1 < FileBuffer.lineCount buffer thenlet nextRow := row.succlet nextLen := lineDisplayWidth buffer nextRowif nextLen == 0 then(nextRow, 0) -- Stop at empty line
def findNextForwardCore (buffer : FileBuffer) (row : Row) (col : Col) (started : Bool) (fuel : Nat) : Row × Col :=match fuel with| 0 => (row, col)| fuel + 1 =>let lineLen := lineDisplayWidth buffer rowif lineLen == 0 then(row, 0) -- Stop at empty lineelse if col.val >= lineLen then-- End of line, wrap to next lineif row.val + 1 < FileBuffer.lineCount buffer thenlet nextRow := row.succlet nextLen := lineDisplayWidth buffer nextRowif nextLen == 0 then(nextRow, 0) -- Stop at empty lineelsefindNextForwardCore buffer nextRow 0 true fuel
findNextForward buffer nextRow 0 trueelse(row, col) -- End of fileelselet lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr collet isKw := isKeyword clet isSpace := c.isWhitespaceif !started thenif isSpace thenfindNextForward buffer row (nextCol buffer row col) trueelsefindNextWordEndForward buffer row (nextCol buffer row col) isKw
(row, col) -- End of file
if isSpace thenfindNextForward buffer row (nextCol buffer row col) startedelse(row, col) -- Found start of next wordpartial def findNextWordEndForward (buffer : FileBuffer) (row : Row) (col : Col) (wasTv : Bool) : Row × Col :=let lineLen := lineDisplayWidth buffer rowif col.val >= lineLen thenif row.val + 1 < FileBuffer.lineCount buffer thenfindNextForward buffer row.succ 0 trueelse(row, col)elselet lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr collet isKw := isKeyword clet isSpace := c.isWhitespace
let lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr collet isKw := isKeyword clet isSpace := c.isWhitespaceif !started then
findNextWordEndForward buffer row (nextCol buffer row col) wasTv
findNextWordEndForwardCore buffer row (nextCol buffer row col) isKw fuelelseif isSpace thenfindNextForwardCore buffer row (nextCol buffer row col) started fuelelse(row, col) -- Found start of next worddef findNextWordEndForwardCore (buffer : FileBuffer) (row : Row) (col : Col) (wasTv : Bool) (fuel : Nat) : Row × Col :=match fuel with| 0 => (row, col)| fuel + 1 =>let lineLen := lineDisplayWidth buffer rowif col.val >= lineLen thenif row.val + 1 < FileBuffer.lineCount buffer thenfindNextForwardCore buffer row.succ 0 true fuelelse(row, col)elselet lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr collet isKw := isKeyword clet isSpace := c.isWhitespaceif isSpace thenfindNextForwardCore buffer row (nextCol buffer row col) true fuelelse if isKw != wasTv then(row, col)elsefindNextWordEndForwardCore buffer row (nextCol buffer row col) wasTv fuel
def findNextForward (buffer : FileBuffer) (row : Row) (col : Col) (started : Bool) : Row × Col :=findNextForwardCore buffer row col started (wordMoveFuel buffer)def findNextWordEndForward (buffer : FileBuffer) (row : Row) (col : Col) (wasTv : Bool) : Row × Col :=findNextWordEndForwardCore buffer row col wasTv (wordMoveFuel buffer)
partial def findPrevStart (buffer : FileBuffer) (row : Row) (col : Col) : Row × Col :=let lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr colif c.isWhitespace thenif col.val == 0 thenif row.val > 0 thenlet prevRow := row.predlet prevLen := lineDisplayWidth buffer prevRowif prevLen > 0 then findPrevStart buffer prevRow ⟨prevLen - 1⟩ else (prevRow, 0)else (0, 0)elsefindPrevStart buffer row (prevCol buffer row col)elselet isKw := isKeyword cconsumeWordBackward buffer row col isKw
def findPrevStartCore (buffer : FileBuffer) (row : Row) (col : Col) (fuel : Nat) : Row × Col :=match fuel with| 0 => (row, col)| fuel + 1 =>let lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr colif c.isWhitespace thenif col.val == 0 thenif row.val > 0 thenlet prevRow := row.predlet prevLen := lineDisplayWidth buffer prevRowif prevLen > 0 then findPrevStartCore buffer prevRow ⟨prevLen - 1⟩ fuel else (prevRow, 0)else (0, 0)elsefindPrevStartCore buffer row (prevCol buffer row col) fuelelselet isKw := isKeyword cconsumeWordBackwardCore buffer row col isKw fuel
def consumeWordBackwardCore (buffer : FileBuffer) (row : Row) (col : Col) (wantKw : Bool) (fuel : Nat) : Row × Col :=match fuel with| 0 => (row, col)| fuel + 1 =>let lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr collet isKw := isKeyword cif c.isWhitespace || isKw != wantKw then(row, nextCol buffer row col)elseif col.val == 0 then (row, 0)else consumeWordBackwardCore buffer row (prevCol buffer row col) wantKw fuelend
partial def consumeWordBackward (buffer : FileBuffer) (row : Row) (col : Col) (wantKw : Bool) : Row × Col :=let lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr collet isKw := isKeyword cif c.isWhitespace || isKw != wantKw then(row, nextCol buffer row col)elseif col.val == 0 then (row, 0)else consumeWordBackward buffer row (prevCol buffer row col) wantKw
def findPrevStart (buffer : FileBuffer) (row : Row) (col : Col) : Row × Col :=findPrevStartCore buffer row col (wordMoveFuel buffer)
partial def findNextEnd (buffer : FileBuffer) (row : Row) (col : Col) : Row × Col :=let lineLen := lineDisplayWidth buffer rowif col.val >= lineLen thenif row.val + 1 < FileBuffer.lineCount buffer thenfindNextEnd buffer row.succ 0else(row, col)elselet lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr colif c.isWhitespace thenfindNextEnd buffer row (nextCol buffer row col)elselet isKw := isKeyword cconsumeWordToEnd buffer row col isKw
def findNextEndCore (buffer : FileBuffer) (row : Row) (col : Col) (fuel : Nat) : Row × Col :=match fuel with| 0 => (row, col)| fuel + 1 =>let lineLen := lineDisplayWidth buffer rowif col.val >= lineLen thenif row.val + 1 < FileBuffer.lineCount buffer thenfindNextEndCore buffer row.succ 0 fuelelse(row, col)elselet lineStr := lineString buffer rowlet c := charAtDisplayCol lineStr colif c.isWhitespace thenfindNextEndCore buffer row (nextCol buffer row col) fuelelselet isKw := isKeyword cconsumeWordToEndCore buffer row col isKw fuel
partial def consumeWordToEnd (buffer : FileBuffer) (row : Row) (col : Col) (wantKw : Bool) : Row × Col :=let lineLen := lineDisplayWidth buffer rowlet nextColVal := (nextCol buffer row col).valif nextColVal >= lineLen then(row, col)elselet lineStr := lineString buffer rowlet nextC := charAtDisplayCol lineStr (nextCol buffer row col)let nextKw := isKeyword nextCif nextC.isWhitespace || nextKw != wantKw then(row, col)elseconsumeWordToEnd buffer row (nextCol buffer row col) wantKw
def consumeWordToEndCore (buffer : FileBuffer) (row : Row) (col : Col) (wantKw : Bool) (fuel : Nat) : Row × Col :=match fuel with| 0 => (row, col)| fuel + 1 =>let lineLen := lineDisplayWidth buffer rowlet nextColVal := (nextCol buffer row col).valif nextColVal >= lineLen then(row, col)elselet lineStr := lineString buffer rowlet nextC := charAtDisplayCol lineStr (nextCol buffer row col)let nextKw := isKeyword nextCif nextC.isWhitespace || nextKw != wantKw then(row, col)elseconsumeWordToEndCore buffer row (nextCol buffer row col) wantKw fuel
partial def PieceTable.findLineForOffset (pt : PieceTable) (target : Nat) (low high : Nat) : Option (Nat × Nat) :=if low > high then noneelselet mid := (low + high) / 2match pt.getLineRange mid with| some (start, len) =>let endOff := start + lenif target >= start && target <= endOff thensome (mid, target - start)else if target < start thenif mid == 0 then noneelse PieceTable.findLineForOffset pt target low (mid - 1)elsePieceTable.findLineForOffset pt target (mid + 1) high| none => none
private def PieceTable.findLineForOffsetCore (pt : PieceTable) (target : Nat) (low high : Nat) (fuel : Nat) : Option (Nat × Nat) :=match fuel with| 0 => none| fuel + 1 =>if low > high then noneelselet mid := (low + high) / 2match pt.getLineRange mid with| some (start, len) =>let endOff := start + lenif target >= start && target <= endOff thensome (mid, target - start)else if target < start thenif mid == 0 then noneelse PieceTable.findLineForOffsetCore pt target low (mid - 1) fuelelsePieceTable.findLineForOffsetCore pt target (mid + 1) high fuel| none => nonedef PieceTable.findLineForOffset (pt : PieceTable) (target : Nat) (low high : Nat) : Option (Nat × Nat) :=let fuel := pt.lineCount + 1PieceTable.findLineForOffsetCore pt target low high fuel
partial def buildPrefixBytes (pieces : Array ViE.Piece) (pt : ViE.PieceTable) : Array UInt8 :=let rec loop (i : Nat) (acc : Array UInt8) : Array UInt8 :=if i >= pieces.size || acc.size >= 2 thenaccelselet p := pieces[i]!let buf := PieceTableHelper.getBuffer pt p.sourcelet slice := buf.extract p.start (p.start + p.length)if slice.size > 0 thenlet need := 2 - acc.sizelet take := takeFirstBytes slice.data needloop (i + 1) (acc ++ take)elseloop (i + 1) accloop 0 #[]
def buildPrefixBytes (pieces : Array ViE.Piece) (pt : ViE.PieceTable) : Array UInt8 := Id.run dolet mut i := 0let mut acc : Array UInt8 := #[]while i < pieces.size && acc.size < 2 dolet p := pieces[i]!let buf := PieceTableHelper.getBuffer pt p.sourcelet slice := buf.extract p.start (p.start + p.length)if slice.size > 0 thenlet need := 2 - acc.sizelet take := takeFirstBytes slice.data needacc := acc ++ takei := i + 1return acc
partial def buildSuffixBytes (pieces : Array ViE.Piece) (pt : ViE.PieceTable) : Array UInt8 :=let rec loop (i : Nat) (acc : Array UInt8) : Array UInt8 :=if i == 0 || acc.size >= 2 thenaccelselet idx := i - 1let p := pieces[idx]!let buf := PieceTableHelper.getBuffer pt p.sourcelet slice := buf.extract p.start (p.start + p.length)if slice.size > 0 thenlet need := 2 - acc.sizelet take := takeLastBytes slice.data needloop idx (take ++ acc)elseloop idx accloop pieces.size #[]
def buildSuffixBytes (pieces : Array ViE.Piece) (pt : ViE.PieceTable) : Array UInt8 := Id.run dolet mut i := pieces.sizelet mut acc : Array UInt8 := #[]while i > 0 && acc.size < 2 dolet idx := i - 1let p := pieces[idx]!let buf := PieceTableHelper.getBuffer pt p.sourcelet slice := buf.extract p.start (p.start + p.length)if slice.size > 0 thenlet need := 2 - acc.sizelet take := takeLastBytes slice.data needacc := take ++ acci := idxreturn acc
termination_by pieces.sizedecreasing_by· simp_wfhave hgt : ViE.NodeCapacity < pieces.size := Nat.lt_of_not_ge (by assumption)have hsize : 0 < pieces.size := Nat.lt_trans (by decide : 0 < ViE.NodeCapacity) hgthave hdiv : pieces.size / 2 < pieces.size := Nat.div_lt_self hsize (by decide : 1 < 2)exact Nat.lt_of_le_of_lt (Nat.min_le_left _ _) hdiv· simp_wfhave hgt : ViE.NodeCapacity < pieces.size := Nat.lt_of_not_ge (by assumption)have hsize : 0 < pieces.size := Nat.lt_trans (by decide : 0 < ViE.NodeCapacity) hgthave hge2 : 2 ≤ pieces.size := Nat.le_trans (by decide : 2 ≤ ViE.NodeCapacity) (Nat.le_of_lt hgt)have hhalf : 0 < pieces.size / 2 := Nat.div_pos hge2 (by decide : 0 < 2)exact Nat.sub_lt hsize hhalf
/-- Concatenate two trees while maintaining B+ tree invariants -/partial def concat (l : ViE.PieceTree) (r : ViE.PieceTree) (pt : ViE.PieceTable) : ViE.PieceTree :=match l, r with| ViE.PieceTree.empty, _ => r| _, ViE.PieceTree.empty => l
/-- Concatenate two trees while maintaining B+ tree invariants. -/private def concatCore (l : ViE.PieceTree) (r : ViE.PieceTree) (pt : ViE.PieceTable) (fuel : Nat) : ViE.PieceTree :=match fuel with| 0 => mkInternal #[l, r]| fuel + 1 =>match l, r with| ViE.PieceTree.empty, _ => r| _, ViE.PieceTree.empty => l
| ViE.PieceTree.leaf ps1 _ _, ViE.PieceTree.leaf ps2 _ _ =>-- Try to merge the last piece of ps1 with the first piece of ps2if ps1.size > 0 && ps2.size > 0 thenlet p1 := ps1.back!let p2 := ps2[0]!if p1.source == p2.source && p1.start + p1.length == p2.start thenlet mergedPiece : ViE.Piece := { p1 withlength := p1.length + p2.length,lineBreaks := p1.lineBreaks + p2.lineBreaks,charCount := p1.charCount + p2.charCount}let ps := (ps1.pop).push mergedPiece ++ (ps2.extract 1 ps2.size)if ps.size <= ViE.NodeCapacity thenmkLeaf ps pt
| ViE.PieceTree.leaf ps1 _ _, ViE.PieceTree.leaf ps2 _ _ =>-- Try to merge the last piece of ps1 with the first piece of ps2if ps1.size > 0 && ps2.size > 0 thenlet p1 := ps1.back!let p2 := ps2[0]!if p1.source == p2.source && p1.start + p1.length == p2.start thenlet mergedPiece : ViE.Piece := { p1 withlength := p1.length + p2.length,lineBreaks := p1.lineBreaks + p2.lineBreaks,charCount := p1.charCount + p2.charCount}let ps := (ps1.pop).push mergedPiece ++ (ps2.extract 1 ps2.size)if ps.size <= ViE.NodeCapacity thenmkLeaf ps ptelselet mid := ps.size / 2mkInternal #[mkLeaf (ps.extract 0 mid) pt, mkLeaf (ps.extract mid ps.size) pt]
let mid := ps.size / 2mkInternal #[mkLeaf (ps.extract 0 mid) pt, mkLeaf (ps.extract mid ps.size) pt]
let ps := ps1 ++ ps2if ps.size <= ViE.NodeCapacity then mkLeaf ps ptelse mkInternal #[mkLeaf ps1 pt, mkLeaf ps2 pt]else if ps1.size > 0 then lelse r| ViE.PieceTree.internal cs1 _ _, ViE.PieceTree.internal cs2 _ _ =>let h1 := height llet h2 := height rif h1 == h2 then-- Merge boundary childrenlet lastChild := cs1.back!let firstChild := cs2[0]!let merged := concatCore lastChild firstChild pt fuelmatch merged with| ViE.PieceTree.internal newCS s _ =>if s.height == h1 then-- newCS is at the same level as siblingslet combined := (cs1.pop) ++ newCS ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]else-- height did not increase, just one childlet combined := (cs1.pop).push merged ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]| _ =>let combined := (cs1.pop).push merged ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]else if h1 > h2 then-- Insert r into l's right sidelet lastChild := cs1.back!let newLast := concatCore lastChild r pt fuelmatch newLast with| ViE.PieceTree.internal newChildren s _ =>if s.height == h1 then-- Split happened at l's levellet combined := (cs1.pop) ++ newChildrenif combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]elsemkInternal ((cs1.pop).push newLast)| _ => mkInternal ((cs1.pop).push newLast)
let ps := ps1 ++ ps2if ps.size <= ViE.NodeCapacity then mkLeaf ps ptelse mkInternal #[mkLeaf ps1 pt, mkLeaf ps2 pt]else if ps1.size > 0 then lelse r
-- Insert l into r's left sidelet firstChild := cs2[0]!let newFirst := concatCore l firstChild pt fuelmatch newFirst with| ViE.PieceTree.internal newChildren s _ =>if s.height == h2 thenlet combined := newChildren ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]elsemkInternal (#[newFirst] ++ (cs2.extract 1 cs2.size))| _ => mkInternal (#[newFirst] ++ (cs2.extract 1 cs2.size))
| ViE.PieceTree.internal cs1 _ _, ViE.PieceTree.internal cs2 _ _ =>let h1 := height llet h2 := height rif h1 == h2 then-- Merge boundary childrenlet lastChild := cs1.back!let firstChild := cs2[0]!let merged := concat lastChild firstChild ptmatch merged with| ViE.PieceTree.internal newCS s _ =>if s.height == h1 then-- newCS is at the same level as siblingslet combined := (cs1.pop) ++ newCS ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]else-- height did not increase, just one childlet combined := (cs1.pop).push merged ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]| _ =>let combined := (cs1.pop).push merged ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]else if h1 > h2 then-- Insert r into l's right sidelet lastChild := cs1.back!let newLast := concat lastChild r ptmatch newLast with| ViE.PieceTree.internal newChildren s _ =>if s.height == h1 then-- Split happened at l's levellet combined := (cs1.pop) ++ newChildrenif combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]elsemkInternal ((cs1.pop).push newLast)| _ => mkInternal ((cs1.pop).push newLast)else-- Insert l into r's left sidelet firstChild := cs2[0]!let newFirst := concat l firstChild ptmatch newFirst with| ViE.PieceTree.internal newChildren s _ =>if s.height == h2 thenlet combined := newChildren ++ (cs2.extract 1 cs2.size)if combined.size <= ViE.NodeCapacity then mkInternal combinedelselet mid := combined.size / 2mkInternal #[mkInternal (combined.extract 0 mid), mkInternal (combined.extract mid combined.size)]elsemkInternal (#[newFirst] ++ (cs2.extract 1 cs2.size))| _ => mkInternal (#[newFirst] ++ (cs2.extract 1 cs2.size))
-- Mixed types: wrap the smaller one and recurse| ViE.PieceTree.leaf .. , ViE.PieceTree.internal .. => concatCore (mkInternal #[l]) r pt fuel| ViE.PieceTree.internal .. , ViE.PieceTree.leaf .. => concatCore l (mkInternal #[r]) pt fueltermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _def concat (l : ViE.PieceTree) (r : ViE.PieceTree) (pt : ViE.PieceTable) : ViE.PieceTree :=let fuel := (length l + length r + 1) * 4 + 16concatCore l r pt fuel
-- Mixed types: wrap the smaller one and recurse| ViE.PieceTree.leaf .. , ViE.PieceTree.internal .. => concat (mkInternal #[l]) r pt| ViE.PieceTree.internal .. , ViE.PieceTree.leaf .. => concat l (mkInternal #[r]) pt
/-- Split the tree at a given byte offset. -/private def splitCore (t : ViE.PieceTree) (offset : Nat) (pt : ViE.PieceTable) (fuel : Nat) : (ViE.PieceTree × ViE.PieceTree) :=match fuel with| 0 => (t, ViE.PieceTree.empty)| fuel + 1 =>match t with| ViE.PieceTree.empty => (ViE.PieceTree.empty, ViE.PieceTree.empty)| ViE.PieceTree.leaf pieces _ _ =>Id.run dolet mut i := 0let mut accOffset := 0while i < pieces.size dolet p := pieces[i]!if offset < accOffset + p.length thenlet relOffset := offset - accOffsetif relOffset == 0 thenreturn (mkLeaf (pieces.extract 0 i) pt, mkLeaf (pieces.extract i pieces.size) pt)let (p1, p2) := PieceTableHelper.splitPiece p relOffset ptreturn (mkLeaf ((pieces.extract 0 i).push p1) pt, mkLeaf (#[p2] ++ (pieces.extract (i + 1) pieces.size)) pt)accOffset := accOffset + p.lengthi := i + 1return (t, ViE.PieceTree.empty)| ViE.PieceTree.internal children _ _ =>Id.run dolet mut i := 0let mut accOffset := 0while i < children.size dolet c := children[i]!let cLen := length cif offset < accOffset + cLen thenlet (l, r) := splitCore c (offset - accOffset) pt fuellet leftSide := mkInternal ((children.extract 0 i).push l)let rightSide := mkInternal (#[r] ++ (children.extract (i + 1) children.size))return (leftSide, rightSide)accOffset := accOffset + cLeni := i + 1return (t, ViE.PieceTree.empty)termination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _
/-- Split the tree at a given byte offset -/partial def split (t : ViE.PieceTree) (offset : Nat) (pt : ViE.PieceTable) : (ViE.PieceTree × ViE.PieceTree) :=match t with| ViE.PieceTree.empty => (ViE.PieceTree.empty, ViE.PieceTree.empty)| ViE.PieceTree.leaf pieces _ _ =>let rec findSplitLeaf (i : Nat) (accOffset : Nat) : (ViE.PieceTree × ViE.PieceTree) :=if i >= pieces.size then (t, ViE.PieceTree.empty)elselet p := pieces[i]!if offset < accOffset + p.length thenlet relOffset := offset - accOffsetif relOffset == 0 then(mkLeaf (pieces.extract 0 i) pt, mkLeaf (pieces.extract i pieces.size) pt)elselet (p1, p2) := PieceTableHelper.splitPiece p relOffset pt(mkLeaf ((pieces.extract 0 i).push p1) pt, mkLeaf (#[p2] ++ (pieces.extract (i + 1) pieces.size)) pt)elsefindSplitLeaf (i + 1) (accOffset + p.length)findSplitLeaf 0 0| ViE.PieceTree.internal children _ _ =>let rec findSplitInternal (i : Nat) (accOffset : Nat) : (ViE.PieceTree × ViE.PieceTree) :=if i >= children.size then (t, ViE.PieceTree.empty)elselet c := children[i]!let cLen := length cif offset < accOffset + cLen thenlet (l, r) := split c (offset - accOffset) ptlet leftSide := mkInternal ((children.extract 0 i).push l)let rightSide := mkInternal (#[r] ++ (children.extract (i + 1) children.size))(leftSide, rightSide)elsefindSplitInternal (i + 1) (accOffset + cLen)findSplitInternal 0 0
def split (t : ViE.PieceTree) (offset : Nat) (pt : ViE.PieceTable) : (ViE.PieceTree × ViE.PieceTree) :=let fuel := (length t + 1) * 4 + 16splitCore t offset pt fuel
/-- Get bytes from tree -/partial def getBytes (t : ViE.PieceTree) (offset : Nat) (len : Nat) (pt : ViE.PieceTable) : ByteArray :=let rec collect (t : ViE.PieceTree) (off : Nat) (l : Nat) (acc : Array ByteArray) : (Array ByteArray × Nat) :=if l == 0 then (acc, 0)else match t with| ViE.PieceTree.empty => (acc, 0)| ViE.PieceTree.leaf pieces _ _ =>let rec scanLeaf (i : Nat) (accOffset : Nat) (currAcc : Array ByteArray) (remain : Nat) : (Array ByteArray × Nat) :=if i >= pieces.size || remain == 0 then (currAcc, l - remain)elselet p := pieces[i]!let pLen := p.lengthif off < accOffset + pLen thenlet pStart := if off > accOffset then off - accOffset else 0let readLen := min remain (pLen - pStart)let buf := PieceTableHelper.getBuffer pt p.sourcelet slice := buf.extract (p.start + pStart) (p.start + pStart + readLen)scanLeaf (i + 1) (accOffset + pLen) (currAcc.push slice) (remain - readLen)elsescanLeaf (i + 1) (accOffset + pLen) currAcc remainscanLeaf 0 0 acc l| ViE.PieceTree.internal children _ _ =>let rec scanInternal (i : Nat) (accOffset : Nat) (currAcc : Array ByteArray) (remain : Nat) : (Array ByteArray × Nat) :=if i >= children.size || remain == 0 then (currAcc, l - remain)elselet c := children[i]!let cLen := length cif off < accOffset + cLen thenlet cStart := if off > accOffset then off - accOffset else 0let (newAcc, readInThisChild) := collect c cStart remain currAccscanInternal (i + 1) (accOffset + cLen) newAcc (remain - readInThisChild)elsescanInternal (i + 1) (accOffset + cLen) currAcc remainscanInternal 0 0 acc llet (chunks, _) := collect t offset len #[]
/-- Get bytes from tree. -/private def getBytesCore (t : ViE.PieceTree) (offset : Nat) (len : Nat) (pt : ViE.PieceTable) (fuel : Nat) : ByteArray :=let rec collect (fuel : Nat) (t : ViE.PieceTree) (off : Nat) (l : Nat) (acc : Array ByteArray) : (Array ByteArray × Nat) :=match fuel with| 0 => (acc, 0)| fuel + 1 =>if l == 0 then (acc, 0)elsematch t with| ViE.PieceTree.empty => (acc, 0)| ViE.PieceTree.leaf pieces _ _ =>Id.run dolet mut i := 0let mut accOffset := 0let mut currAcc := acclet mut remain := lwhile i < pieces.size && remain > 0 dolet p := pieces[i]!let pLen := p.lengthif off < accOffset + pLen thenlet pStart := if off > accOffset then off - accOffset else 0let readLen := min remain (pLen - pStart)let buf := PieceTableHelper.getBuffer pt p.sourcelet slice := buf.extract (p.start + pStart) (p.start + pStart + readLen)currAcc := currAcc.push sliceremain := remain - readLenaccOffset := accOffset + pLeni := i + 1return (currAcc, l - remain)| ViE.PieceTree.internal children _ _ =>Id.run dolet mut i := 0let mut accOffset := 0let mut currAcc := acclet mut remain := lwhile i < children.size && remain > 0 dolet c := children[i]!let cLen := length cif off < accOffset + cLen thenlet cStart := if off > accOffset then off - accOffset else 0let (newAcc, readInThisChild) := collect fuel c cStart remain currAcccurrAcc := newAccremain := remain - readInThisChildaccOffset := accOffset + cLeni := i + 1return (currAcc, l - remain)let (chunks, _) := collect fuel t offset len #[]
partial def findNthNewlineLeaf (t : ViE.PieceTree) (n : Nat) (pt : ViE.PieceTable) : Option (ViE.Piece × Nat × Nat) :=let rec scan (t : ViE.PieceTree) (n : Nat) (accOffset : Nat) : Option (ViE.Piece × Nat × Nat) :=match t with| ViE.PieceTree.empty => none| ViE.PieceTree.leaf pieces _ _ =>let rec findInPieces (i : Nat) (currN : Nat) (currOff : Nat) : Option (ViE.Piece × Nat × Nat) :=if i >= pieces.size then noneelselet p := pieces[i]!if currN < p.lineBreaks thenlet buf := PieceTableHelper.getBuffer pt p.sourcelet rec findOffset (j : Nat) (targetN : Nat) : Nat :=if j >= p.length then p.lengthelse if buf[p.start + j]! == 10 thenif targetN == 0 then j + 1else findOffset (j + 1) (targetN - 1)else findOffset (j + 1) targetNlet relOffset := findOffset 0 currNsome (p, currOff, relOffset)elsefindInPieces (i + 1) (currN - p.lineBreaks) (currOff + p.length)findInPieces 0 n accOffset| ViE.PieceTree.internal children _ _ =>let rec findInChildren (i : Nat) (currN : Nat) (currOff : Nat) : Option (ViE.Piece × Nat × Nat) :=if i >= children.size then noneelselet child := children[i]!let childLines := lineBreaks childif currN < childLines thenscan child currN currOffelsefindInChildren (i + 1) (currN - childLines) (currOff + length child)findInChildren 0 n accOffsetscan t n 0
private def findNthNewlineLeafCore (t : ViE.PieceTree) (n : Nat) (pt : ViE.PieceTable) (accOffset : Nat) (fuel : Nat): Option (ViE.Piece × Nat × Nat) :=match fuel with| 0 => none| fuel + 1 =>match t with| ViE.PieceTree.empty => none| ViE.PieceTree.leaf pieces _ _ =>Id.run dolet mut i := 0let mut currN := nlet mut currOff := accOffsetwhile i < pieces.size dolet p := pieces[i]!if currN < p.lineBreaks thenlet buf := PieceTableHelper.getBuffer pt p.sourcelet mut j := 0let mut targetN := currNlet mut relOffset := p.lengthlet mut done := falsewhile j < p.length && !done doif buf[p.start + j]! == 10 thenif targetN == 0 thenrelOffset := j + 1done := trueelsetargetN := targetN - 1j := j + 1return some (p, currOff, relOffset)currN := currN - p.lineBreakscurrOff := currOff + p.lengthi := i + 1return none| ViE.PieceTree.internal children _ _ =>Id.run dolet mut i := 0let mut currN := nlet mut currOff := accOffsetwhile i < children.size dolet child := children[i]!let childLines := lineBreaks childif currN < childLines thenreturn findNthNewlineLeafCore child currN pt currOff fuelcurrN := currN - childLinescurrOff := currOff + length childi := i + 1return nonetermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _def findNthNewlineLeaf (t : ViE.PieceTree) (n : Nat) (pt : ViE.PieceTable) : Option (ViE.Piece × Nat × Nat) :=let fuel := (length t + lineBreaks t + 1) * 4 + 16findNthNewlineLeafCore t n pt 0 fuel
partial def maximalSuffixLoop (x : ByteArray) (useLe : Bool) (m ms j k p : Int) : (Int × Int) :=if j + k >= m then(ms, p)elselet a := x[Int.toNat (j + k)]!let b := x[Int.toNat (ms + k)]!if useLe thenif a < b thenmaximalSuffixLoop x useLe m ms (j + k) 1 (j + k - ms)else if a == b thenif k != p thenmaximalSuffixLoop x useLe m ms j (k + 1) pelsemaximalSuffixLoop x useLe m ms (j + p) 1 p
private def maximalSuffixLoopCore (x : ByteArray) (useLe : Bool) (m ms j k p : Int) (fuel : Nat) : (Int × Int) :=match fuel with| 0 => (ms, p)| fuel + 1 =>if j + k >= m then(ms, p)
maximalSuffixLoop x useLe m j (j + 1) 1 1elseif a > b thenmaximalSuffixLoop x useLe m ms (j + k) 1 (j + k - ms)else if a == b thenif k != p thenmaximalSuffixLoop x useLe m ms j (k + 1) p
let a := x[Int.toNat (j + k)]!let b := x[Int.toNat (ms + k)]!if useLe thenif a < b thenmaximalSuffixLoopCore x useLe m ms (j + k) 1 (j + k - ms) fuelelse if a == b thenif k != p thenmaximalSuffixLoopCore x useLe m ms j (k + 1) p fuelelsemaximalSuffixLoopCore x useLe m ms (j + p) 1 p fuelelsemaximalSuffixLoopCore x useLe m j (j + 1) 1 1 fuel
maximalSuffixLoop x useLe m ms (j + p) 1 pelsemaximalSuffixLoop x useLe m j (j + 1) 1 1
if a > b thenmaximalSuffixLoopCore x useLe m ms (j + k) 1 (j + k - ms) fuelelse if a == b thenif k != p thenmaximalSuffixLoopCore x useLe m ms j (k + 1) p fuelelsemaximalSuffixLoopCore x useLe m ms (j + p) 1 p fuelelsemaximalSuffixLoopCore x useLe m j (j + 1) 1 1 fueltermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _def maximalSuffixLoop (x : ByteArray) (useLe : Bool) (m ms j k p : Int) : (Int × Int) :=let mNat := Int.toNat mlet fuel := (mNat + 1) * (mNat + 1) + 1maximalSuffixLoopCore x useLe m ms j k p fuel
partial def twoWayForward1 (haystack needle : ByteArray) (i : Nat) (n : Nat) (j : Int) : Int :=if j >= n thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayForward1 haystack needle i n (j + 1)elsej
private def twoWayForward1Core (haystack needle : ByteArray) (i : Nat) (n : Nat) (j : Int) (fuel : Nat) : Int :=match fuel with| 0 => j| fuel + 1 =>if j >= n thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayForward1Core haystack needle i n (j + 1) fuelelsejtermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _
partial def twoWayBackward1 (haystack needle : ByteArray) (i : Nat) (mem : Int) (j : Int) : Int :=if j <= mem thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayBackward1 haystack needle i mem (j - 1)elsej
def twoWayForward1 (haystack needle : ByteArray) (i : Nat) (n : Nat) (j : Int) : Int :=let fuel := Int.toNat (Int.ofNat n - j) + 1twoWayForward1Core haystack needle i n j fuelprivate def twoWayBackward1Core (haystack needle : ByteArray) (i : Nat) (mem : Int) (j : Int) (fuel : Nat) : Int :=match fuel with| 0 => j| fuel + 1 =>if j <= mem thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayBackward1Core haystack needle i mem (j - 1) fuelelsejtermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _
partial def twoWayForward2 (haystack needle : ByteArray) (i : Nat) (n : Nat) (j : Int) : Int :=if j >= n thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayForward2 haystack needle i n (j + 1)elsej
def twoWayBackward1 (haystack needle : ByteArray) (i : Nat) (mem : Int) (j : Int) : Int :=let fuel := Int.toNat (j - mem) + 1twoWayBackward1Core haystack needle i mem j fuelprivate def twoWayForward2Core (haystack needle : ByteArray) (i : Nat) (n : Nat) (j : Int) (fuel : Nat) : Int :=match fuel with| 0 => j| fuel + 1 =>if j >= n thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayForward2Core haystack needle i n (j + 1) fuelelsejtermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _def twoWayForward2 (haystack needle : ByteArray) (i : Nat) (n : Nat) (j : Int) : Int :=let fuel := Int.toNat (Int.ofNat n - j) + 1twoWayForward2Core haystack needle i n j fuelprivate def twoWayBackward2Core (haystack needle : ByteArray) (i : Nat) (j : Int) (fuel : Nat) : Int :=match fuel with| 0 => j| fuel + 1 =>if j < 0 thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayBackward2Core haystack needle i (j - 1) fuelelsejtermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _
partial def twoWayBackward2 (haystack needle : ByteArray) (i : Nat) (j : Int) : Int :=if j < 0 thenjelse if haystack[i + Int.toNat j]! == needle[Int.toNat j]! thentwoWayBackward2 haystack needle i (j - 1)elsej
def twoWayBackward2 (haystack needle : ByteArray) (i : Nat) (j : Int) : Int :=let fuel := Int.toNat (j + 1) + 1twoWayBackward2Core haystack needle i j fuel
partial def twoWayShortLoop (haystack needle : ByteArray) (start maxStart msNat pNat n : Nat) (i : Nat) (mem : Int) : Option Nat :=if i > maxStart thennoneelselet j0 := max (Int.ofNat msNat) mem + 1let j := twoWayForward1 haystack needle i n j0if j >= n thenlet j2 := twoWayBackward1 haystack needle i mem (Int.ofNat msNat)if j2 <= mem thensome i
private def twoWayShortLoopCore (haystack needle : ByteArray) (start maxStart msNat pNat n : Nat) (i : Nat) (mem : Int) (fuel : Nat) : Option Nat :=match fuel with| 0 => none| fuel + 1 =>if i > maxStart thennone
let nextI := i + pNatlet nextMem := Int.ofNat (n - pNat - 1)twoWayShortLoop haystack needle start maxStart msNat pNat n nextI nextMemelselet shift := Int.toNat (j - Int.ofNat msNat)twoWayShortLoop haystack needle start maxStart msNat pNat n (i + shift) (-1)
let j0 := max (Int.ofNat msNat) mem + 1let j := twoWayForward1 haystack needle i n j0if j >= n thenlet j2 := twoWayBackward1 haystack needle i mem (Int.ofNat msNat)if j2 <= mem thensome ielselet nextI := i + pNatlet nextMem := Int.ofNat (n - pNat - 1)twoWayShortLoopCore haystack needle start maxStart msNat pNat n nextI nextMem fuelelselet shift := Int.toNat (j - Int.ofNat msNat)twoWayShortLoopCore haystack needle start maxStart msNat pNat n (i + shift) (-1) fueltermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _def twoWayShortLoop (haystack needle : ByteArray) (start maxStart msNat pNat n : Nat) (i : Nat) (mem : Int) : Option Nat :=let fuel := if start <= maxStart then maxStart - start + 2 else 1twoWayShortLoopCore haystack needle start maxStart msNat pNat n i mem fuel
partial def twoWayLongLoop (haystack needle : ByteArray) (start maxStart msNat pNat n : Nat) (i : Nat) : Option Nat :=if i > maxStart thennoneelselet j := twoWayForward2 haystack needle i n (Int.ofNat (msNat + 1))if j >= n thenlet j2 := twoWayBackward2 haystack needle i (Int.ofNat msNat)if j2 < 0 thensome i
private def twoWayLongLoopCore (haystack needle : ByteArray) (start maxStart msNat pNat n : Nat) (i : Nat) (fuel : Nat) : Option Nat :=match fuel with| 0 => none| fuel + 1 =>if i > maxStart thennone
let nextI := i + pNattwoWayLongLoop haystack needle start maxStart msNat pNat n nextIelselet shift := Int.toNat (j - Int.ofNat msNat)twoWayLongLoop haystack needle start maxStart msNat pNat n (i + shift)
let j := twoWayForward2 haystack needle i n (Int.ofNat (msNat + 1))if j >= n thenlet j2 := twoWayBackward2 haystack needle i (Int.ofNat msNat)if j2 < 0 thensome ielselet nextI := i + pNattwoWayLongLoopCore haystack needle start maxStart msNat pNat n nextI fuelelselet shift := Int.toNat (j - Int.ofNat msNat)twoWayLongLoopCore haystack needle start maxStart msNat pNat n (i + shift) fueltermination_by fueldecreasing_byall_goals exact Nat.lt_succ_self _def twoWayLongLoop (haystack needle : ByteArray) (start maxStart msNat pNat n : Nat) (i : Nat) : Option Nat :=let fuel := if start <= maxStart then maxStart - start + 2 else 1twoWayLongLoopCore haystack needle start maxStart msNat pNat n i fuel
let rec loop (i : Nat) (last : Option Nat) : Option Nat :=if i > maxStart then lastelsematch findPatternInBytes haystack needle i with| some idx =>if idx > maxStart then lastelse loop (idx + 1) (some idx)| none => lastloop 0 none
let rec loop (i : Nat) (last : Option Nat) (fuel : Nat) : Option Nat :=match fuel with| 0 => last| fuel + 1 =>if i > maxStart then lastelsematch findPatternInBytes haystack needle i with| some idx =>if idx > maxStart then lastelse loop (idx + 1) (some idx) fuel| none => lastloop 0 none (maxStart + 1)
partial def searchNext (t : ViE.PieceTree) (pt : ViE.PieceTable) (pattern : ByteArray) (startOffset : Nat) (chunkSize : Nat)(useBloom : Bool) (cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (_cacheMax : Nat)
private def searchNextCore (t : ViE.PieceTree) (pt : ViE.PieceTable) (pattern : ByteArray) (startOffset : Nat) (chunkSize : Nat)(useBloom : Bool) (cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (_cacheMax : Nat) (fuel : Nat)
else if pattern.size < 3 || !useBloom thenlet chunkLen := max chunkSize pattern.sizelet overlap := pattern.size - 1let found := Id.run dolet mut offset := startOffsetwhile offset < total dolet remaining := total - offsetlet readLen := min chunkLen remainingif readLen < pattern.size thenreturn nonelet bytes := getBytes t offset readLen ptmatch findPatternInBytes bytes pattern 0 with| some idx => return some (offset + idx)| none =>if offset + readLen >= total thenreturn noneoffset := offset + (readLen - overlap)return none(found, cache, order)
if pattern.size < 3 || !useBloom thenlet chunkLen := max chunkSize pattern.sizelet overlap := pattern.size - 1let rec loop (offset : Nat) : Option Nat :=if offset >= total then noneelselet remaining := total - offsetlet readLen := min chunkLen remainingif readLen < pattern.size thennone
let hashes := patternTrigramHashes patternlet overlap := pattern.size - 1let rec searchNode (node : ViE.PieceTree) (baseOffset : Nat)(cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (fuelN : Nat): (Option Nat × Lean.RBMap Nat ByteArray compare × Array Nat) :=match fuelN with| 0 => (none, cache, order)| fuelN + 1 =>if baseOffset + length node <= startOffset then(none, cache, order)
let bytes := getBytes t offset readLen ptmatch findPatternInBytes bytes pattern 0 with| some idx => some (offset + idx)| none =>if offset + readLen >= total then noneelselet nextOffset := offset + (readLen - overlap)loop nextOffset(loop startOffset, cache, order)elselet hashes := patternTrigramHashes patternlet overlap := pattern.size - 1let rec searchNode (node : ViE.PieceTree) (baseOffset : Nat)(cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat): (Option Nat × Lean.RBMap Nat ByteArray compare × Array Nat) :=if baseOffset + length node <= startOffset then(none, cache, order)elsematch node with| ViE.PieceTree.empty => (none, cache, order)| ViE.PieceTree.leaf _ _ m =>let relStart := if startOffset > baseOffset then startOffset - baseOffset else 0let remain := length node - relStartlet globalStart := baseOffset + relStartlet available := total - globalStartlet readLen := min (remain + overlap) availableif readLen < pattern.size then(none, cache, order)elselet crossesBoundary := readLen > remainif !crossesBoundary && !bloomMayContain m.bits hashes then
match node with| ViE.PieceTree.empty => (none, cache, order)| ViE.PieceTree.leaf _ _ m =>let relStart := if startOffset > baseOffset then startOffset - baseOffset else 0let remain := length node - relStartlet globalStart := baseOffset + relStartlet available := total - globalStartlet readLen := min (remain + overlap) availableif readLen < pattern.size then
let bytes := getBytes t globalStart readLen ptmatch findPatternInBytes bytes pattern 0 with| some idx => (some (globalStart + idx), cache, order)| none => (none, cache, order)| ViE.PieceTree.internal children _ m =>if !bloomMayContain m.bits hashes then(none, cache, order)elselet rec loop (i : Nat) (offset : Nat)(cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat): (Option Nat × Lean.RBMap Nat ByteArray compare × Array Nat) :=if i >= children.size then
let crossesBoundary := readLen > remainif !crossesBoundary && !bloomMayContain m.bits hashes then
let child := children[i]!let childLen := length childlet childEnd := offset + childLenif childEnd <= startOffset thenloop (i + 1) childEnd cache orderelsematch searchNode child offset cache order with| (some res, cache, order) => (some res, cache, order)| (none, cache, order) => loop (i + 1) childEnd cache orderloop 0 baseOffset cache ordersearchNode t 0 cache order
let bytes := getBytes t globalStart readLen ptmatch findPatternInBytes bytes pattern 0 with| some idx => (some (globalStart + idx), cache, order)| none => (none, cache, order)| ViE.PieceTree.internal children _ m =>if !bloomMayContain m.bits hashes then(none, cache, order)elseId.run dolet mut i := 0let mut offset := baseOffsetlet mut cacheAcc := cachelet mut orderAcc := orderwhile i < children.size dolet child := children[i]!let childLen := length childlet childEnd := offset + childLenif childEnd > startOffset thenlet (res, cache', order') := searchNode child offset cacheAcc orderAcc fuelNcacheAcc := cache'orderAcc := order'match res with| some _ => return (res, cacheAcc, orderAcc)| none => pure ()offset := childEndi := i + 1return (none, cacheAcc, orderAcc)searchNode t 0 cache order fueldef searchNext (t : ViE.PieceTree) (pt : ViE.PieceTable) (pattern : ByteArray) (startOffset : Nat) (chunkSize : Nat)(useBloom : Bool) (cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (cacheMax : Nat): (Option Nat × Lean.RBMap Nat ByteArray compare × Array Nat) :=let fuel := (length t + pattern.size + 1) * 8 + 64searchNextCore t pt pattern startOffset chunkSize useBloom cache order cacheMax fuel
partial def searchPrev (t : ViE.PieceTree) (pt : ViE.PieceTable) (pattern : ByteArray) (startExclusive : Nat) (chunkSize : Nat)(useBloom : Bool) (cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (_cacheMax : Nat)
private def searchPrevCore (t : ViE.PieceTree) (pt : ViE.PieceTable) (pattern : ByteArray) (startExclusive : Nat) (chunkSize : Nat)(useBloom : Bool) (cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (_cacheMax : Nat) (fuel : Nat)
if baseOffset >= end0 then(none, cache, order)elsematch node with| ViE.PieceTree.empty => (none, cache, order)| ViE.PieceTree.leaf _ _ m =>let relEnd := min (end0 - baseOffset) (length node)let globalEnd := baseOffset + relEndlet globalStart := if baseOffset > overlap then baseOffset - overlap else 0let readLen := globalEnd - globalStartif readLen < pattern.size then(none, cache, order)elselet crossesBoundary := globalStart < baseOffsetif !crossesBoundary && !bloomMayContain m.bits hashes then(none, cache, order)elselet bytes := getBytes t globalStart readLen ptmatch findLastPatternInBytes bytes pattern with| some idx =>let pos := globalStart + idxif pos < end0 then (some pos, cache, order) else (none, cache, order)| none => (none, cache, order)| ViE.PieceTree.internal children _ m =>if !bloomMayContain m.bits hashes then(none, cache, order)elselet spans : Array (ViE.PieceTree × Nat) := Id.run dolet mut acc : Array (ViE.PieceTree × Nat) := #[]let mut offset := baseOffsetfor child in children doacc := acc.push (child, offset)offset := offset + length childreturn acclet rec loop (i : Nat)(cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat): (Option Nat × Lean.RBMap Nat ByteArray compare × Array Nat) :=if i >= spans.size then
match fuelN with| 0 => (none, cache, order)| fuelN + 1 =>if baseOffset >= end0 then(none, cache, order)elsematch node with| ViE.PieceTree.empty => (none, cache, order)| ViE.PieceTree.leaf _ _ m =>let relEnd := min (end0 - baseOffset) (length node)let globalEnd := baseOffset + relEndlet globalStart := if baseOffset > overlap then baseOffset - overlap else 0let readLen := globalEnd - globalStartif readLen < pattern.size then
match searchNode child childOffset cache order with| (some res, cache, order) =>if res < end0 then (some res, cache, order) else loop (i + 1) cache order| (none, cache, order) => loop (i + 1) cache orderloop 0 cache ordersearchNode t 0 cache order
let bytes := getBytes t globalStart readLen ptmatch findLastPatternInBytes bytes pattern with| some idx =>let pos := globalStart + idxif pos < end0 then (some pos, cache, order) else (none, cache, order)| none => (none, cache, order)| ViE.PieceTree.internal children _ m =>if !bloomMayContain m.bits hashes then(none, cache, order)elselet spans : Array (ViE.PieceTree × Nat) := Id.run dolet mut acc : Array (ViE.PieceTree × Nat) := #[]let mut offset := baseOffsetfor child in children doacc := acc.push (child, offset)offset := offset + length childreturn accId.run dolet mut i := 0let mut cacheAcc := cachelet mut orderAcc := orderwhile i < spans.size dolet j := spans.size - 1 - ilet (child, childOffset) := spans[j]!if childOffset < end0 thenlet (res, cache', order') := searchNode child childOffset cacheAcc orderAcc fuelNcacheAcc := cache'orderAcc := order'match res with| some v =>if v < end0 thenreturn (some v, cacheAcc, orderAcc)pure ()| none => pure ()i := i + 1return (none, cacheAcc, orderAcc)searchNode t 0 cache order fueldef searchPrev (t : ViE.PieceTree) (pt : ViE.PieceTable) (pattern : ByteArray) (startExclusive : Nat) (chunkSize : Nat)(useBloom : Bool) (cache : Lean.RBMap Nat ByteArray compare) (order : Array Nat) (cacheMax : Nat): (Option Nat × Lean.RBMap Nat ByteArray compare × Array Nat) :=let fuel := (length t + pattern.size + 1) * 8 + 64searchPrevCore t pt pattern startExclusive chunkSize useBloom cache order cacheMax fuel
let rec loop (i : Nat) (acc : Array (Nat × Nat)) : Array (Nat × Nat) :=if i + n > h thenaccelsematch ViE.PieceTree.findPatternInBytes haystack needle i with| some idx =>if idx + n > h thenaccelseloop (idx + n) (acc.push (idx, idx + n))| none => accloop 0 #[]
let rec loop (fuel : Nat) (i : Nat) (acc : Array (Nat × Nat)) : Array (Nat × Nat) :=match fuel with| 0 => acc| fuel + 1 =>if i + n > h thenaccelsematch ViE.PieceTree.findPatternInBytes haystack needle i with| some idx =>if idx + n > h thenaccelselet nextI := if idx + n > i then idx + n else i + 1loop fuel nextI (acc.push (idx, idx + n))| none => accloop (h + 1) 0 #[]
if offset >= total thenaccelselet (res, cache', order') :=ViE.PieceTree.searchNext pt.tree pt pattern offset ViE.searchChunkSize false cache order 0match res with| some idx =>if idx + n > total thenaccelseloop (idx + n) cache' order' (acc.push (idx, idx + n))| none => accloop 0 Lean.RBMap.empty #[] #[]
match fuel with| 0 => acc| fuel + 1 =>if offset >= total thenaccelselet (res, cache', order') :=ViE.PieceTree.searchNext pt.tree pt pattern offset ViE.searchChunkSize false cache order 0match res with| some idx =>if idx + n > total thenaccelselet nextOffset := if idx + n > offset then idx + n else offset + 1loop fuel nextOffset cache' order' (acc.push (idx, idx + n))| none => accloop (total + 1) 0 Lean.RBMap.empty #[] #[]
partial def loop (config : Config) (state : EditorState) : IO Unit := do-- Only render if state is dirtylet state ← if state.dirty thenViE.UI.render stateelsepure state-- Reset dirty flag after render (or if it was already clean, keep it clean)let state := { state with dirty := false }let currentTime ← IO.monoMsNowlet c ← ViE.Terminal.readKey-- Update window sizelet (rows, cols) ← ViE.Terminal.getWindowSizelet resized := rows != state.windowHeight || cols != state.windowWidthlet state := { state with windowHeight := rows, windowWidth := cols, dirty := state.dirty || resized }-- Handle the case where readKey returns None (no input available)match c with| none =>-- Check for timeoutlet (stateAfterTimeout, keys) := ViE.checkTimeout state currentTimelet stateAfterTimeout := ViE.maybeRunIncrementalSearch stateAfterTimeout currentTimeif keys.isEmpty then-- No input and no timeout events, sleep briefly to avoid busy loopIO.sleep 10 -- 10msloop config stateAfterTimeout
def loop (config : Config) (state0 : EditorState) : IO Unit := dolet mut state := state0let mut quit := falsewhile !quit do-- Only render if state is dirtystate ← if state.dirty thenViE.UI.render state
| some ch =>-- Parse the character into keys using the new parseKey functionlet (stateAfterParse, keys) := ViE.parseKey state ch currentTime
-- Update window sizelet (rows, cols) ← ViE.Terminal.getWindowSizelet resized := rows != state.windowHeight || cols != state.windowWidthstate := { state with windowHeight := rows, windowWidth := cols, dirty := state.dirty || resized }
-- Process all returned keyslet mut finalState := stateAfterParsefor key in keys dofinalState ← ViE.update config finalState key
-- Handle the case where readKey returns None (no input available)match c with| none =>-- Check for timeoutlet (stateAfterTimeout, keys) := ViE.checkTimeout state currentTimelet stateAfterTimeout := ViE.maybeRunIncrementalSearch stateAfterTimeout currentTimeif keys.isEmpty then-- No input and no timeout events, sleep briefly to avoid busy loopIO.sleep 10 -- 10msstate := stateAfterTimeoutelse-- Process timeout keys (e.g. flushed Esc)let mut finalState := stateAfterTimeoutfor key in keys dofinalState ← ViE.update config finalState keyif finalState.shouldQuit thenquit := trueelsestate := { finalState with dirty := true }| some ch =>-- Parse the character into keys using the new parseKey functionlet (stateAfterParse, keys) := ViE.parseKey state ch currentTime
partial def countNodes (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ => 1 + cs.foldl (fun acc c => acc + countNodes c) 0
mutualdef countNodes (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ => 1 + countNodesList cs.toList
partial def countLeaves (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ => cs.foldl (fun acc c => acc + countLeaves c) 0
def countNodesList (ts : List PieceTree) : Nat :=match ts with| [] => 0| t :: rest => countNodes t + countNodesList restend
partial def maxLeafPieces (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf ps _ _ => ps.size| .internal cs _ _ => cs.foldl (fun acc c => max acc (maxLeafPieces c)) 0
mutualdef countLeaves (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 1| .internal cs _ _ => countLeavesList cs.toList
partial def maxInternalChildren (t : PieceTree) : Nat :=match t with| .empty => 0| .leaf _ _ _ => 0| .internal cs _ _ =>cs.foldl (fun acc c => max acc (maxInternalChildren c)) cs.size
def 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)end