use concurrency for rebuilding search list after deletion so we don't block main thread on very, very large files

This commit is contained in:
2025-08-07 12:20:57 +01:00
parent fab8cfcf20
commit 704854c80f
10 changed files with 220 additions and 137 deletions

View File

@@ -9,6 +9,14 @@ struct
fun clearMode app = fun clearMode app =
AppWith.mode (app, NORMAL_MODE "", []) AppWith.mode (app, NORMAL_MODE "", [])
fun withSearchList (app: app_type, searchList) =
let
val {buffer, searchString, cursorIdx, ...} = app
val app = AppWith.searchList (app, searchList, buffer, searchString)
in
Finish.buildTextAndClear (app, buffer, cursorIdx, searchList, [])
end
fun resizeText (app: app_type, newWidth, newHeight) = fun resizeText (app: app_type, newWidth, newHeight) =
let let
val val
@@ -33,6 +41,7 @@ struct
, newHeight , newHeight
, searchList , searchList
, searchString , searchString
, []
) )
in in
AppWith.bufferAndSize AppWith.bufferAndSize
@@ -44,7 +53,8 @@ struct
* where the cursor may possibly jump off window by a wide marigin. * where the cursor may possibly jump off window by a wide marigin.
* Since the cursor may move away a lot, it is best to recenter. * Since the cursor may move away a lot, it is best to recenter.
* *) * *)
fun buildTextAndClearAfterChr (app: app_type, buffer, cursorIdx, searchList) = fun buildTextAndClearAfterChr
(app: app_type, buffer, cursorIdx, searchList, initialMsg) =
let let
val {windowWidth, windowHeight, startLine, searchString, ...} = app val {windowWidth, windowHeight, startLine, searchString, ...} = app
@@ -68,6 +78,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
val mode = NORMAL_MODE "" val mode = NORMAL_MODE ""
@@ -104,6 +115,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
in in
AppWith.bufferAndCursorIdx AppWith.bufferAndCursorIdx
@@ -129,6 +141,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
val mode = NORMAL_MODE "" val mode = NORMAL_MODE ""
@@ -169,6 +182,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
val mode = NORMAL_MODE "" val mode = NORMAL_MODE ""
@@ -211,6 +225,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
val mode = NORMAL_MODE "" val mode = NORMAL_MODE ""
@@ -253,6 +268,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
in in
AppWith.bufferAndCursorIdx AppWith.bufferAndCursorIdx
@@ -283,6 +299,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, []
) )
in in
AppWith.bufferAndCursorIdx AppWith.bufferAndCursorIdx
@@ -309,12 +326,12 @@ struct
val buffer = LineGap.goToIdx (cursorIdx, buffer) val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorIdx = Cursor.firstNonSpaceChr (buffer, cursorIdx) val cursorIdx = Cursor.firstNonSpaceChr (buffer, cursorIdx)
in in
Finish.buildTextAndClear (app, buffer, cursorIdx, #searchList app) Finish.buildTextAndClear (app, buffer, cursorIdx, #searchList app, [])
end end
fun helpMoveToChr (app: app_type, buffer, cursorIdx, count, fMove, chr) = fun helpMoveToChr (app: app_type, buffer, cursorIdx, count, fMove, chr) =
if count = 0 then if count = 0 then
buildTextAndClearAfterChr (app, buffer, cursorIdx, #searchList app) buildTextAndClearAfterChr (app, buffer, cursorIdx, #searchList app, [])
else else
let let
(* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *) (* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *)
@@ -335,6 +352,7 @@ struct
CHAR_EVENT chr => moveToChr (app, count, fMove, chr) CHAR_EVENT chr => moveToChr (app, count, fMove, chr)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList)
fun handleGo (count, app, newCmd) = fun handleGo (count, app, newCmd) =
case newCmd of case newCmd of
@@ -346,12 +364,19 @@ struct
| _ => clearMode app) | _ => clearMode app)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList)
(* text-delete functions *) (* text-delete functions *)
(** equivalent of vi's 'x' command **) (** equivalent of vi's 'x' command **)
fun helpRemoveChr (app: app_type, buffer, searchList, cursorIdx, count) = fun helpRemoveChr (app: app_type, buffer, cursorIdx, count) =
if count = 0 then if count = 0 then
Finish.buildTextAndClear (app, buffer, cursorIdx, searchList) let
val buffer = LineGap.goToEnd buffer
val initialMsg = [SEARCH (buffer, #searchString app)]
in
Finish.buildTextAndClear
(app, buffer, cursorIdx, SearchList.empty, initialMsg)
end
else else
let let
val buffer = LineGap.goToIdx (cursorIdx, buffer) val buffer = LineGap.goToIdx (cursorIdx, buffer)
@@ -374,9 +399,9 @@ struct
(* vi simply doesn't do anything on 'x' command (* vi simply doesn't do anything on 'x' command
* when cursor is at start of line, and next chr is line break * when cursor is at start of line, and next chr is line break
* so skip to end of loop by passing count of 0 *) * so skip to end of loop by passing count of 0 *)
helpRemoveChr (app, buffer, searchList, cursorIdx, 0) helpRemoveChr (app, buffer, cursorIdx, 0)
else if cursorIsStart then else if cursorIsStart then
helpRemoveChr (app, buffer, searchList, cursorIdx, 0) helpRemoveChr (app, buffer, cursorIdx, 0)
else if nextIsEnd then else if nextIsEnd then
let let
(* delete char at cursor and then decrement cursorIdx by 1 (* delete char at cursor and then decrement cursorIdx by 1
@@ -384,8 +409,6 @@ struct
val searchString = #searchString app val searchString = #searchString app
val buffer = LineGap.delete (cursorIdx, 1, buffer) val buffer = LineGap.delete (cursorIdx, 1, buffer)
val (buffer, searchList) = SearchList.build (buffer, searchString)
val cursorIdx = val cursorIdx =
if if
Cursor.isPrevChrStartOfLine (buffer, cursorIdx) Cursor.isPrevChrStartOfLine (buffer, cursorIdx)
@@ -393,21 +416,19 @@ struct
then cursorIdx then cursorIdx
else cursorIdx - 1 else cursorIdx - 1
in in
helpRemoveChr (app, buffer, searchList, cursorIdx, count - 1) helpRemoveChr (app, buffer, cursorIdx, count - 1)
end end
else else
let let
val searchString = #searchString app val searchString = #searchString app
val buffer = LineGap.delete (cursorIdx, 1, buffer) val buffer = LineGap.delete (cursorIdx, 1, buffer)
val (buffer, searchList) = SearchList.build (buffer, searchString)
in in
helpRemoveChr (app, buffer, searchList, cursorIdx, count - 1) helpRemoveChr (app, buffer, cursorIdx, count - 1)
end end
end end
fun removeChr (app: app_type, count) = fun removeChr (app: app_type, count) =
helpRemoveChr (app, #buffer app, #searchList app, #cursorIdx app, count) helpRemoveChr (app, #buffer app, #cursorIdx app, count)
fun helpDelete (app: app_type, buffer, cursorIdx, otherIdx, count, fMove) = fun helpDelete (app: app_type, buffer, cursorIdx, otherIdx, count, fMove) =
(* As a small optimisation to reduce allocations, (* As a small optimisation to reduce allocations,
@@ -426,8 +447,9 @@ struct
val buffer = LineGap.delete (low, length, buffer) val buffer = LineGap.delete (low, length, buffer)
val buffer = LineGap.goToEnd buffer
val searchString = #searchString app val searchString = #searchString app
val (buffer, searchList) = SearchList.build (buffer, searchString) val initialMsg = [SEARCH (buffer, searchString)]
(* If we have deleted from the buffer so that cursorIdx (* If we have deleted from the buffer so that cursorIdx
* is no longer a valid idx, * is no longer a valid idx,
@@ -435,7 +457,8 @@ struct
val buffer = LineGap.goToIdx (low, buffer) val buffer = LineGap.goToIdx (low, buffer)
val cursorIdx = Cursor.clipIdx (buffer, low) val cursorIdx = Cursor.clipIdx (buffer, low)
in in
Finish.buildTextAndClear (app, buffer, cursorIdx, searchList) Finish.buildTextAndClear
(app, buffer, cursorIdx, SearchList.empty, initialMsg)
end end
else else
let let
@@ -463,11 +486,12 @@ struct
val buffer = LineGap.delete (low, length, buffer) val buffer = LineGap.delete (low, length, buffer)
val (buffer, searchList) = SearchList.build (buffer, searchString) val buffer = LineGap.goToEnd buffer
val initialMsg = [SEARCH (buffer, searchString)]
val buffer = LineGap.goToIdx (low, buffer) val buffer = LineGap.goToIdx (low, buffer)
in in
Finish.buildTextAndClear (app, buffer, low, searchList) Finish.buildTextAndClear (app, buffer, low, SearchList.empty, initialMsg)
end end
fun deleteToEndOfLine (app: app_type) = fun deleteToEndOfLine (app: app_type) =
@@ -489,12 +513,8 @@ struct
val lastChr = Cursor.viDlr (buffer, cursorIdx, 1) val lastChr = Cursor.viDlr (buffer, cursorIdx, 1)
val length = lastChr - cursorIdx val length = lastChr - cursorIdx
val buffer = LineGap.delete (cursorIdx, length, buffer) val buffer = LineGap.delete (cursorIdx, length, buffer)
(* delete from searchList and map *)
val searchString = #searchString app
val (buffer, searchList) = SearchList.build (buffer, searchString)
in in
helpRemoveChr (app, buffer, searchList, cursorIdx, 1) helpRemoveChr (app, buffer, cursorIdx, 1)
end end
end end
@@ -509,11 +529,13 @@ struct
val length = finishIdx - startIdx val length = finishIdx - startIdx
val buffer = LineGap.delete (startIdx, length, buffer) val buffer = LineGap.delete (startIdx, length, buffer)
val (buffer, searchList) = SearchList.build (buffer, searchString) val buffer = LineGap.goToEnd buffer
val initialMsg = [SEARCH (buffer, searchString)]
val buffer = LineGap.goToIdx (startIdx, buffer) val buffer = LineGap.goToIdx (startIdx, buffer)
in in
Finish.buildTextAndClear (app, buffer, startIdx, searchList) Finish.buildTextAndClear
(app, buffer, startIdx, SearchList.empty, initialMsg)
end end
fun helpDeleteLineBack (app, buffer, low, high, count) = fun helpDeleteLineBack (app, buffer, low, high, count) =
@@ -523,12 +545,14 @@ struct
val length = high - low val length = high - low
val buffer = LineGap.delete (low, length, buffer) val buffer = LineGap.delete (low, length, buffer)
val buffer = LineGap.goToEnd buffer
val searchString = #searchString app val searchString = #searchString app
val (buffer, searchList) = SearchList.build (buffer, searchString) val initialMsg = [SEARCH (buffer, searchString)]
val buffer = LineGap.goToIdx (low, buffer) val buffer = LineGap.goToIdx (low, buffer)
in in
Finish.buildTextAndClear (app, buffer, low, searchList) Finish.buildTextAndClear
(app, buffer, low, SearchList.empty, initialMsg)
end end
else else
let let
@@ -575,9 +599,11 @@ struct
val length = high - low val length = high - low
val buffer = LineGap.delete (low, length, buffer) val buffer = LineGap.delete (low, length, buffer)
val (buffer, searchList) = SearchList.build (buffer, searchString)
val buffer = LineGap.goToEnd buffer
val initialMsg = [SEARCH (buffer, searchString)]
in in
Finish.buildTextAndClear (app, buffer, low, searchList) Finish.buildTextAndClear (app, buffer, low, SearchList.empty, initialMsg)
end end
fun helpDeleteToChr fun helpDeleteToChr
@@ -589,10 +615,12 @@ struct
val length = high - low val length = high - low
val buffer = LineGap.delete (low, length, buffer) val buffer = LineGap.delete (low, length, buffer)
val buffer = LineGap.goToEnd buffer
val searchString = #searchString app val searchString = #searchString app
val (buffer, searchList) = SearchList.build (buffer, searchString) val initialMsg = [SEARCH (buffer, searchString)]
in in
buildTextAndClearAfterChr (app, buffer, low, searchList) buildTextAndClearAfterChr
(app, buffer, low, SearchList.empty, initialMsg)
end end
else else
let let
@@ -625,6 +653,9 @@ struct
val buffer = LineGap.delete (0, cursorIdx, buffer) val buffer = LineGap.delete (0, cursorIdx, buffer)
val (buffer, searchList) = SearchList.build (buffer, searchString) val (buffer, searchList) = SearchList.build (buffer, searchString)
val buffer = LineGap.goToEnd buffer
val initialMsg = [SEARCH (buffer, #searchString app)]
val cursorIdx = 0 val cursorIdx = 0
val startLine = 0 val startLine = 0
val buffer = LineGap.goToIdx (cursorIdx, buffer) val buffer = LineGap.goToIdx (cursorIdx, buffer)
@@ -637,6 +668,7 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, initialMsg
) )
val mode = NORMAL_MODE "" val mode = NORMAL_MODE ""
@@ -767,6 +799,7 @@ struct
| _ => clearMode app) | _ => clearMode app)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList)
else else
(* have to continue parsing string *) (* have to continue parsing string *)
case String.sub (str, strPos + 1) of case String.sub (str, strPos + 1) of
@@ -776,27 +809,31 @@ struct
CHAR_EVENT chr => CHAR_EVENT chr =>
deleteToChr (app, 1, Cursor.tillNextChr, op+, chr) deleteToChr (app, 1, Cursor.tillNextChr, op+, chr)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height)) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList))
| #"T" => | #"T" =>
(* delete till chr, backwards *) (* delete till chr, backwards *)
(case newCmd of (case newCmd of
CHAR_EVENT chr => CHAR_EVENT chr =>
deleteToChr (app, 1, Cursor.tillPrevChr, op-, chr) deleteToChr (app, 1, Cursor.tillPrevChr, op-, chr)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height)) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList))
| #"f" => | #"f" =>
(case newCmd of (case newCmd of
CHAR_EVENT chr => CHAR_EVENT chr =>
deleteToChr (app, count, Cursor.toNextChr, op+, chr) deleteToChr (app, count, Cursor.toNextChr, op+, chr)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height)) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList))
| #"F" => | #"F" =>
(* delete to chr, backwards *) (* delete to chr, backwards *)
(case newCmd of (case newCmd of
CHAR_EVENT chr => CHAR_EVENT chr =>
deleteToChr (app, count, Cursor.toPrevChr, op-, chr) deleteToChr (app, count, Cursor.toPrevChr, op-, chr)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height)) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList))
| #"g" => | #"g" =>
(* same events as handleGo *) (* same events as handleGo *)
(case newCmd of (case newCmd of
@@ -807,7 +844,8 @@ struct
| #"g" => deleteToStart app | #"g" => deleteToStart app
| _ => clearMode app) | _ => clearMode app)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height)) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList))
| _ => clearMode app | _ => clearMode app
(* useful reference as list of non-terminal commands *) (* useful reference as list of non-terminal commands *)
@@ -849,6 +887,7 @@ struct
CHAR_EVENT chr => handleChr (app, 1, chr, str) CHAR_EVENT chr => handleChr (app, 1, chr, str)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList)
else if String.size str = 1 then else if String.size str = 1 then
case newCmd of case newCmd of
CHAR_EVENT chr => CHAR_EVENT chr =>
@@ -857,6 +896,7 @@ struct
| NONE => parseAfterCount (0, str, 1, app, newCmd)) | NONE => parseAfterCount (0, str, 1, app, newCmd))
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList)
else else
let let
val numLength = getNumLength (0, str) val numLength = getNumLength (0, str)
@@ -872,6 +912,7 @@ struct
CHAR_EVENT chr => handleChr (app, count, chr, str) CHAR_EVENT chr => handleChr (app, count, chr, str)
| KEY_ESC => clearMode app | KEY_ESC => clearMode app
| RESIZE_EVENT (width, height) => resizeText (app, width, height) | RESIZE_EVENT (width, height) => resizeText (app, width, height)
| WITH_SEARCH_LIST searchList => withSearchList (app, searchList)
else else
(* continue parsing. *) (* continue parsing. *)
parseAfterCount (numLength, str, count, app, newCmd) parseAfterCount (numLength, str, count, app, newCmd)

View File

@@ -2,7 +2,7 @@ structure Finish =
struct struct
open AppType open AppType
fun buildTextAndClear (app: app_type, buffer, cursorIdx, searchList) = fun buildTextAndClear (app: app_type, buffer, cursorIdx, searchList, msgs) =
let let
val {windowWidth, windowHeight, startLine, searchString, ...} = app val {windowWidth, windowHeight, startLine, searchString, ...} = app
@@ -18,7 +18,7 @@ struct
val buffer = LineGap.goToLine (startLine, buffer) val buffer = LineGap.goToLine (startLine, buffer)
val lineIdx = TextBuilder.getLineAbsIdx (startLine, buffer) val lineIdx = TextBuilder.getLineAbsIdx (startLine, buffer)
val drawMsg = TextBuilder.build val msgs = TextBuilder.build
( startLine ( startLine
, cursorIdx , cursorIdx
, buffer , buffer
@@ -26,11 +26,12 @@ struct
, windowHeight , windowHeight
, searchList , searchList
, searchString , searchString
, msgs
) )
val mode = NORMAL_MODE "" val mode = NORMAL_MODE ""
in in
AppWith.bufferAndCursorIdx AppWith.bufferAndCursorIdx
(app, buffer, cursorIdx, mode, startLine, searchList, drawMsg) (app, buffer, cursorIdx, mode, startLine, searchList, msgs)
end end
end end

View File

@@ -12,7 +12,7 @@ functor MakeMove(Fn: MOVE): MAKE_MOVE =
struct struct
fun helpMove (app: AppType.app_type, buffer, cursorIdx, count) = fun helpMove (app: AppType.app_type, buffer, cursorIdx, count) =
if count = 0 then if count = 0 then
Finish.buildTextAndClear (app, buffer, cursorIdx, #searchList app) Finish.buildTextAndClear (app, buffer, cursorIdx, #searchList app, [])
else else
(* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *) (* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *)
let let
@@ -62,7 +62,7 @@ struct
val buffer = LineGap.goToIdx (cursorIdx, buffer) val buffer = LineGap.goToIdx (cursorIdx, buffer)
val cursorIdx = Fn.fMove (buffer, cursorIdx, count) val cursorIdx = Fn.fMove (buffer, cursorIdx, count)
in in
Finish.buildTextAndClear (app, buffer, cursorIdx, #searchList app) Finish.buildTextAndClear (app, buffer, cursorIdx, #searchList app, [])
end end
end end

View File

@@ -4,43 +4,22 @@ sig
val getLineAbsIdx: int * LineGap.t -> int val getLineAbsIdx: int * LineGap.t -> int
(* Prerequisites: LineGap is moved to requested line first. *) (* Prerequisites: LineGap is moved to requested line first. *)
val build: int * int * LineGap.t * int * int * SearchList.t * string val build:
-> MailboxType.t list int
* int
* LineGap.t
* int
* int
* SearchList.t
* string
* MailboxType.t list
-> MailboxType.t list
end end
structure TextBuilder :> TEXT_BUILDER = structure TextBuilder :> TEXT_BUILDER =
struct struct
open TextConstants open TextConstants
fun accToDrawMsg (textAcc, cursorAcc, bgAcc) =
let
open MailboxType
open DrawMsg
val textAcc = Vector.concat textAcc
val bgAcc = Vector.concat bgAcc
val textMsg = REDRAW_TEXT textAcc
val cursorMsg = REDRAW_CURSOR cursorAcc
val bgMsg = REDRAW_BG bgAcc
in
[DRAW bgMsg, DRAW textMsg, DRAW cursorMsg]
end
(* builds text from a string with char-wrap.
* char-wrap is a similar concept to word-wrap,
* but it breaks on character in the middle of a word.
*
* Will likely want multiple versions of these two mutually recursive
* functions for each selection and cursor type:
* cursor over an individual character,
* range selection where multiple characters are selected, etc.
*
* Todo:
* - Possibly add visual horizontal indentation when char-wrap occurs
* on an indented line *)
(* same as buildTextStringAfterCursor, except this keeps track of absolute
* index and cursor pos too *)
type env_data = type env_data =
{ r: Real32.real { r: Real32.real
, g: Real32.real , g: Real32.real
@@ -57,8 +36,40 @@ struct
(* fw/fh = float window width and float window height *) (* fw/fh = float window width and float window height *)
, fw: Real32.real , fw: Real32.real
, fh: Real32.real , fh: Real32.real
, msgs: MailboxType.t list
} }
fun accToDrawMsg (textAcc, cursorAcc, bgAcc, env: env_data) =
let
open MailboxType
open DrawMsg
val msgs = #msgs env
val textAcc = Vector.concat textAcc
val bgAcc = Vector.concat bgAcc
val textMsg = REDRAW_TEXT textAcc
val cursorMsg = REDRAW_CURSOR cursorAcc
val bgMsg = REDRAW_BG bgAcc
in
DRAW bgMsg :: DRAW textMsg :: DRAW cursorMsg :: msgs
end
(* builds text from a string with char-wrap.
* char-wrap is a similar concept to word-wrap,
* but it breaks on character in the middle of a word.
*
* Will likely want multiple versions of these two mutually recursive
* functions for each selection and cursor type:
* cursor over an individual character,
* range selection where multiple characters are selected, etc.
*
* Todo:
* - Possibly add visual horizontal indentation when char-wrap occurs
* on an indented line *)
(* same as buildTextStringAfterCursor, except this keeps track of absolute
* index and cursor pos too *)
fun buildTextString fun buildTextString
( pos ( pos
, str , str
@@ -159,7 +170,7 @@ struct
) )
end end
else else
accToDrawMsg (acc, cursorAcc, bgAcc) accToDrawMsg (acc, cursorAcc, bgAcc, env)
| chr => | chr =>
let let
val chrFun = Vector.sub (CozetteAscii.asciiTable, Char.ord chr) val chrFun = Vector.sub (CozetteAscii.asciiTable, Char.ord chr)
@@ -213,7 +224,7 @@ struct
) )
end end
else else
accToDrawMsg (acc, cursorAcc, bgAcc) accToDrawMsg (acc, cursorAcc, bgAcc, env)
else else
(* equal to cursor *) (* equal to cursor *)
let let
@@ -278,7 +289,7 @@ struct
) )
end end
else else
accToDrawMsg (acc, cursorAcc, bgAcc) accToDrawMsg (acc, cursorAcc, bgAcc, env)
end end
end end
else else
@@ -299,7 +310,7 @@ struct
, bgAcc , bgAcc
, env , env
) )
| [] => accToDrawMsg (acc, cursorAcc, bgAcc) | [] => accToDrawMsg (acc, cursorAcc, bgAcc, env)
fun isInSearchRange (absIdx, searchPos, searchHd, searchLen) = fun isInSearchRange (absIdx, searchPos, searchHd, searchLen) =
let val searchIdx = Vector.sub (searchHd, searchPos) let val searchIdx = Vector.sub (searchHd, searchPos)
@@ -329,21 +340,21 @@ struct
, searchLen , searchLen
) = ) =
if searchPos = Vector.length searchHd then if searchPos = Vector.length searchHd then
(* exhausted search list so call normal build function *) (* exhausted search list so call normal build function *)
buildTextString buildTextString
( pos ( pos
, str , str
, acc , acc
, posX , posX
, posY , posY
, startX , startX
, tl , tl
, absIdx , absIdx
, cursorPos , cursorPos
, cursorAcc , cursorAcc
, bgAcc , bgAcc
, env , env
) )
else if pos < String.size str then else if pos < String.size str then
case String.sub (str, pos) of case String.sub (str, pos) of
#" " => #" " =>
@@ -474,7 +485,7 @@ struct
) )
end end
else else
accToDrawMsg (acc, cursorAcc, bgAcc) accToDrawMsg (acc, cursorAcc, bgAcc, env)
| chr => | chr =>
let let
val chrFun = Vector.sub (CozetteAscii.asciiTable, Char.ord chr) val chrFun = Vector.sub (CozetteAscii.asciiTable, Char.ord chr)
@@ -582,7 +593,7 @@ struct
) )
end end
else else
accToDrawMsg (acc, cursorAcc, bgAcc) accToDrawMsg (acc, cursorAcc, bgAcc, env)
else else
(* equal to cursor *) (* equal to cursor *)
let let
@@ -652,7 +663,7 @@ struct
) )
end end
else else
accToDrawMsg (acc, cursorAcc, bgAcc) accToDrawMsg (acc, cursorAcc, bgAcc, env)
end end
end end
else else
@@ -676,7 +687,7 @@ struct
, searchPos , searchPos
, searchLen , searchLen
) )
| [] => accToDrawMsg (acc, cursorAcc, bgAcc) | [] => accToDrawMsg (acc, cursorAcc, bgAcc, env)
(* gets line start idx, relative to right hd *) (* gets line start idx, relative to right hd *)
fun helpGetLineStartIdx (startLine, curLine, rLnHd) = fun helpGetLineStartIdx (startLine, curLine, rLnHd) =
@@ -718,6 +729,7 @@ struct
, windowHeight , windowHeight
, searchList: SearchList.t , searchList: SearchList.t
, searchString , searchString
, msgs
) = ) =
let let
val {rightStrings, rightLines, line = curLine, idx = curIdx, ...} = val {rightStrings, rightLines, line = curLine, idx = curIdx, ...} =
@@ -742,44 +754,45 @@ struct
, hr = 0.211 , hr = 0.211
, hg = 0.219 , hg = 0.219
, hb = 0.25 , hb = 0.25
, msgs = msgs
} }
val cursorAcc = Vector.fromList [] val cursorAcc = Vector.fromList []
val searchPos = BinSearch.equalOrMore (absIdx, searchList) val searchPos = BinSearch.equalOrMore (absIdx, searchList)
in in
if searchPos < Vector.length searchList then if searchPos < Vector.length searchList then
buildTextStringSearch buildTextStringSearch
( startIdx ( startIdx
, rStrHd , rStrHd
, [] , []
, 5 , 5
, 5 , 5
, 5 , 5
, rStrTl , rStrTl
, absIdx , absIdx
, cursorPos , cursorPos
, cursorAcc , cursorAcc
, [] , []
, env , env
, searchList , searchList
, searchPos , searchPos
, String.size searchString , String.size searchString
) )
else else
buildTextString buildTextString
( startIdx ( startIdx
, rStrHd , rStrHd
, [] , []
, 5 , 5
, 5 , 5
, 5 , 5
, rStrTl , rStrTl
, absIdx , absIdx
, cursorPos , cursorPos
, cursorAcc , cursorAcc
, [] , []
, env , env
) )
end end
| (_, _) => | (_, _) =>
(* requested line goes beyond the buffer, (* requested line goes beyond the buffer,

View File

@@ -1,2 +1,8 @@
structure InputMsg = structure InputMsg =
struct datatype t = CHAR_EVENT of char | KEY_ESC | RESIZE_EVENT of int * int end struct
datatype t =
CHAR_EVENT of char
| KEY_ESC
| RESIZE_EVENT of int * int
| WITH_SEARCH_LIST of int vector
end

View File

@@ -1 +1,2 @@
structure MailboxType = struct datatype t = DRAW of DrawMsg.t end structure MailboxType =
struct datatype t = DRAW of DrawMsg.t | SEARCH of LineGap.t * string end

14
shell/search-thread.sml Normal file
View File

@@ -0,0 +1,14 @@
structure SearchThread =
struct
open CML
(* Prerequisite to sending message: move buffer to end. *)
fun loop (searchMailbox, inputMailbox) =
let
val (buffer, searchString) = Mailbox.recv searchMailbox
val (_, searchList) = SearchList.build (buffer, searchString)
val () = Mailbox.send (inputMailbox, InputMsg.WITH_SEARCH_LIST searchList)
in
loop (searchMailbox, inputMailbox)
end
end

View File

@@ -6,6 +6,7 @@ struct
(* create mailboxes for CML communication *) (* create mailboxes for CML communication *)
val inputMailbox = Mailbox.mailbox () val inputMailbox = Mailbox.mailbox ()
val drawMailbox = Mailbox.mailbox () val drawMailbox = Mailbox.mailbox ()
val searchMailbox = Mailbox.mailbox ()
fun frameBufferSizeCallback (width, height) = fun frameBufferSizeCallback (width, height) =
Mailbox.send (inputMailbox, RESIZE_EVENT (width, height)) Mailbox.send (inputMailbox, RESIZE_EVENT (width, height))
@@ -79,7 +80,9 @@ struct
val _ = CML.spawn (fn () => GlDraw.loop (drawMailbox, window)) val _ = CML.spawn (fn () => GlDraw.loop (drawMailbox, window))
val _ = CML.spawn (fn () => val _ = CML.spawn (fn () =>
UpdateThread.loop (app, inputMailbox, drawMailbox)) UpdateThread.loop (app, inputMailbox, drawMailbox, searchMailbox))
val _ = CML.spawn (fn () =>
SearchThread.loop (searchMailbox, inputMailbox))
in in
() ()
end end

View File

@@ -4,18 +4,21 @@ struct
open MailboxType open MailboxType
open InputMsg open InputMsg
fun sendMsg (msg, drawMailbox) = fun sendMsg (msg, drawMailbox, searchMailbox) =
case msg of DRAW msg => Mailbox.send (drawMailbox, msg) case msg of
DRAW msg => Mailbox.send (drawMailbox, msg)
| SEARCH (buffer, searchString) =>
Mailbox.send (searchMailbox, (buffer, searchString))
fun sendMsgs (msgList, drawMailbox) = fun sendMsgs (msgList, drawMailbox, searchMailbox) =
case msgList of case msgList of
hd :: tl => hd :: tl =>
let val _ = sendMsg (hd, drawMailbox) let val _ = sendMsg (hd, drawMailbox, searchMailbox)
in sendMsgs (tl, drawMailbox) in sendMsgs (tl, drawMailbox, searchMailbox)
end end
| [] => () | [] => ()
fun loop (app: AppType.app_type, inputMailbox, drawMailbox) = fun loop (app: AppType.app_type, inputMailbox, drawMailbox, searchMailbox) =
let let
val inputMsg = Mailbox.recv inputMailbox val inputMsg = Mailbox.recv inputMailbox
val () = val () =
@@ -33,8 +36,8 @@ struct
val app = AppUpdate.update (app, inputMsg) val app = AppUpdate.update (app, inputMsg)
handle e => ExceptionLogger.log e handle e => ExceptionLogger.log e
val () = sendMsgs (#msgs app, drawMailbox) val () = sendMsgs (#msgs app, drawMailbox, searchMailbox)
in in
loop (app, inputMailbox, drawMailbox) loop (app, inputMailbox, drawMailbox, searchMailbox)
end end
end end

View File

@@ -47,6 +47,7 @@ in
end end
shell/exception-logger.sml shell/exception-logger.sml
shell/search-thread.sml
shell/update-thread.sml shell/update-thread.sml
shell/gl-shaders.sml shell/gl-shaders.sml
shell/gl-draw.sml shell/gl-draw.sml