progress with moving cursor to specific char (vi's 't' command and vi's 'f' command)
This commit is contained in:
@@ -112,10 +112,19 @@ struct
|
|||||||
let
|
let
|
||||||
val chr = String.sub (str, pos)
|
val chr = String.sub (str, pos)
|
||||||
in
|
in
|
||||||
if chr >= #"0" andalso chr <= #"9" then getNumLength (pos + 1, str)
|
if Char.isDigit chr then getNumLength (pos + 1, str)
|
||||||
else pos
|
else pos
|
||||||
end
|
end
|
||||||
|
|
||||||
|
fun appendChr (app: app_type, chr, str) =
|
||||||
|
let
|
||||||
|
val str = str ^ Char.toString chr
|
||||||
|
val mode = NORMAL_MODE str
|
||||||
|
val newApp = AppWith.mode (app, mode)
|
||||||
|
in
|
||||||
|
(newApp, [])
|
||||||
|
end
|
||||||
|
|
||||||
fun handleChr (app: app_type, count, chr, str) =
|
fun handleChr (app: app_type, count, chr, str) =
|
||||||
case chr of
|
case chr of
|
||||||
#"h" => moveBackward (app, count, Cursor.viH)
|
#"h" => moveBackward (app, count, Cursor.viH)
|
||||||
@@ -132,13 +141,23 @@ struct
|
|||||||
| #"0" => moveBackward (app, 1, Cursor.vi0)
|
| #"0" => moveBackward (app, 1, Cursor.vi0)
|
||||||
| #"$" => moveForwards (app, 1, Cursor.viDlr)
|
| #"$" => moveForwards (app, 1, Cursor.viDlr)
|
||||||
| #"^" => firstNonSpaceChr app
|
| #"^" => firstNonSpaceChr app
|
||||||
|
(* multi-char commands which can be appended *)
|
||||||
|
| #"t" => appendChr (app, chr, str)
|
||||||
|
| #"T" => appendChr (app, chr, str)
|
||||||
|
| #"y" => appendChr (app, chr, str)
|
||||||
|
| #"d" => appendChr (app, chr, str)
|
||||||
|
| #"f" => appendChr (app, chr, str)
|
||||||
|
| #"F" => appendChr (app, chr, str)
|
||||||
|
| #"g" => appendChr (app, chr, str)
|
||||||
|
| #"c" => appendChr (app, chr, str)
|
||||||
| _ =>
|
| _ =>
|
||||||
(* user may be entering a cmd with more than one chr
|
(* user may be entering a cmd with more than one chr
|
||||||
* such as "2dw" to delete two word
|
* such as "2dw" to delete two word
|
||||||
* so add current chr to mode, and save it in the app state *)
|
* so add current chr to mode, and save it in the app state *)
|
||||||
let
|
let
|
||||||
val str =
|
val str =
|
||||||
if chr >= #"0" andalso chr <= #"9" then str ^ Char.toString chr
|
if Char.isDigit chr
|
||||||
|
then str ^ Char.toString chr
|
||||||
else ""
|
else ""
|
||||||
val mode = NORMAL_MODE str
|
val mode = NORMAL_MODE str
|
||||||
val newApp = AppWith.mode (app, mode)
|
val newApp = AppWith.mode (app, mode)
|
||||||
@@ -146,7 +165,52 @@ struct
|
|||||||
(newApp, [])
|
(newApp, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
fun helpMoveToChrNext (app: app_type, buffer, cursorIdx, count, fMove, chr) =
|
||||||
|
if count = 0 then
|
||||||
|
let
|
||||||
|
val {windowWidth, windowHeight, startLine, ...} = app
|
||||||
|
(* todo: get new startLine if cursor has moved out of screen *)
|
||||||
|
|
||||||
|
(* move LineGap to first line displayed on screen, and build new text *)
|
||||||
|
val buffer = LineGap.goToLine (startLine, buffer)
|
||||||
|
val drawMsg = TextBuilder.build
|
||||||
|
(startLine, cursorIdx, buffer, windowWidth, windowHeight)
|
||||||
|
|
||||||
|
val mode = NORMAL_MODE ""
|
||||||
|
val newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx, mode)
|
||||||
|
in
|
||||||
|
(newApp, drawMsg)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
let
|
||||||
|
(* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *)
|
||||||
|
val buffer = LineGap.goToIdx (cursorIdx, buffer)
|
||||||
|
val cursorIdx = fMove (buffer, cursorIdx, chr)
|
||||||
|
in
|
||||||
|
helpMoveToChrNext (app, buffer, cursorIdx, count - 1, fMove, chr)
|
||||||
|
end
|
||||||
|
|
||||||
|
fun moveToChrNext (app: app_type, count, fMove, chr) =
|
||||||
|
let val {cursorIdx, buffer, ...} = app
|
||||||
|
in helpMoveToChrNext (app, buffer, cursorIdx, count, fMove, chr)
|
||||||
|
end
|
||||||
|
|
||||||
|
(* temp placeholder function *)
|
||||||
|
fun clearMode app =
|
||||||
|
let
|
||||||
|
val mode = NORMAL_MODE ""
|
||||||
|
val newApp = AppWith.mode (app, mode)
|
||||||
|
in
|
||||||
|
(newApp, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
fun handleNextChr (count, app, fMove, newCmd) =
|
||||||
|
case newCmd of
|
||||||
|
CHAR_EVENT chr => moveToChrNext (app, count, fMove, chr)
|
||||||
|
| RESIZE_EVENT (width, height) => resizeText (app, width, height)
|
||||||
|
|
||||||
(* useful reference as list of non-terminal commands *)
|
(* useful reference as list of non-terminal commands *)
|
||||||
|
(* todo: actually parse, checking if there are further strings or input *)
|
||||||
fun parseAfterCount (strPos, str, count, app, newCmd) =
|
fun parseAfterCount (strPos, str, count, app, newCmd) =
|
||||||
(* we are trying to parse multi-char but non-terminal strings here.
|
(* we are trying to parse multi-char but non-terminal strings here.
|
||||||
* For example, we don't want to parse 3w which is a terminal commmand
|
* For example, we don't want to parse 3w which is a terminal commmand
|
||||||
@@ -154,43 +218,51 @@ struct
|
|||||||
* but we do want to parse 3d which is a non-terminal command
|
* but we do want to parse 3d which is a non-terminal command
|
||||||
* which can be made terminal by adding "w" or "e" at the end.
|
* which can be made terminal by adding "w" or "e" at the end.
|
||||||
* *)
|
* *)
|
||||||
case String.sub (str, strPos + 1) of
|
case String.sub (str, strPos) of
|
||||||
#"t" =>
|
#"t" =>
|
||||||
(* to just before char, forward *)
|
(* to just before char, forward
|
||||||
0
|
* tillNextChr with count of 1 has same effect
|
||||||
|
* as tillNextChr with any count above 1
|
||||||
|
* so just hardcode 1 *)
|
||||||
|
handleNextChr (1, app, Cursor.tillNextChr, newCmd)
|
||||||
| #"T" =>
|
| #"T" =>
|
||||||
(* to just before chr, backward *)
|
(* to just before chr, backward *)
|
||||||
0
|
clearMode app
|
||||||
| #"y" =>
|
| #"y" =>
|
||||||
(* yank *)
|
(* yank *)
|
||||||
0
|
clearMode app
|
||||||
| #"d" =>
|
| #"d" =>
|
||||||
(* delete *)
|
(* delete *)
|
||||||
0
|
clearMode app
|
||||||
| #"f" =>
|
| #"f" =>
|
||||||
(* to chr, forward *)
|
(* to chr, forward *)
|
||||||
0
|
handleNextChr (count, app, Cursor.toNextChr, newCmd)
|
||||||
| #"F" =>
|
| #"F" =>
|
||||||
(* to chr, backward *)
|
(* to chr, backward *)
|
||||||
0
|
clearMode app
|
||||||
| #"g" =>
|
| #"g" =>
|
||||||
(* go *)
|
(* go *)
|
||||||
0
|
clearMode app
|
||||||
| #"c" =>
|
| #"c" =>
|
||||||
(* change *)
|
(* change *)
|
||||||
0
|
clearMode app
|
||||||
| #"/" =>
|
|
||||||
(* search *)
|
|
||||||
0
|
|
||||||
| _ =>
|
| _ =>
|
||||||
(* isn't a non-terminal cmd *)
|
(* isn't a non-terminal cmd
|
||||||
0
|
* this case should never happen*)
|
||||||
|
clearMode app
|
||||||
|
|
||||||
fun parseNormalModeCommand (app, str, newCmd) =
|
fun parseNormalModeCommand (app, str, newCmd) =
|
||||||
if String.size str = 0 then
|
if String.size str = 0 then
|
||||||
case newCmd of
|
case newCmd of
|
||||||
RESIZE_EVENT (width, height) => resizeText (app, width, height)
|
RESIZE_EVENT (width, height) => resizeText (app, width, height)
|
||||||
| CHAR_EVENT chr => handleChr (app, 1, chr, str)
|
| CHAR_EVENT chr => handleChr (app, 1, chr, str)
|
||||||
|
else if String.size str = 1 then
|
||||||
|
case newCmd of
|
||||||
|
RESIZE_EVENT (width, height) => resizeText (app, width, height)
|
||||||
|
| CHAR_EVENT chr =>
|
||||||
|
(case Int.fromString str of
|
||||||
|
SOME count => handleChr (app, count, chr, str)
|
||||||
|
| NONE => parseAfterCount (0, str, 1, app, newCmd))
|
||||||
else
|
else
|
||||||
let
|
let
|
||||||
val numLength = getNumLength (0, str)
|
val numLength = getNumLength (0, str)
|
||||||
@@ -205,10 +277,12 @@ struct
|
|||||||
case newCmd of
|
case newCmd of
|
||||||
RESIZE_EVENT (width, height) => resizeText (app, width, height)
|
RESIZE_EVENT (width, height) => resizeText (app, width, height)
|
||||||
| CHAR_EVENT chr => handleChr (app, count, chr, str)
|
| CHAR_EVENT chr => handleChr (app, count, chr, str)
|
||||||
|
else if numLength + 1 < String.size str then
|
||||||
|
(* continue parsing. *)
|
||||||
|
parseAfterCount (numLength + 1, str, count, app, newCmd)
|
||||||
else
|
else
|
||||||
(* todo: continue parsing. *)
|
(* continue parsing. *)
|
||||||
(* parseAfterCount (numLength, str, count, app, newCmd) *)
|
parseAfterCount (numLength, str, count, app, newCmd)
|
||||||
raise Match
|
|
||||||
end
|
end
|
||||||
|
|
||||||
fun updateNormalMode (app, str, msg) = parseNormalModeCommand (app, str, msg)
|
fun updateNormalMode (app, str, msg) = parseNormalModeCommand (app, str, msg)
|
||||||
|
|||||||
@@ -1262,4 +1262,72 @@ struct
|
|||||||
| (_, _) =>
|
| (_, _) =>
|
||||||
cursorIdx
|
cursorIdx
|
||||||
end
|
end
|
||||||
|
|
||||||
|
fun helpNextChr (strPos, str, absIdx, stl, ltl, origIdx, findChr, fResult) =
|
||||||
|
if strPos = String.size str then
|
||||||
|
case (stl, ltl) of
|
||||||
|
(shd :: stl, lhd :: ltl) =>
|
||||||
|
helpNextChr
|
||||||
|
(0, shd, absIdx, stl, ltl, origIdx, findChr, fResult)
|
||||||
|
| (_, _) =>
|
||||||
|
origIdx
|
||||||
|
else
|
||||||
|
if String.sub (str, strPos) = findChr then
|
||||||
|
fResult absIdx
|
||||||
|
else
|
||||||
|
helpNextChr
|
||||||
|
(strPos + 1, str, absIdx + 1, stl, ltl, origIdx, findChr, fResult)
|
||||||
|
|
||||||
|
fun startNextChr (shd, strIdx, absIdx, stl, ltl, findChr, fResult) =
|
||||||
|
(* we want to start iterating from next char after strIdx *)
|
||||||
|
if strIdx - 1 < String.size shd then
|
||||||
|
helpNextChr
|
||||||
|
(strIdx + 1, shd, absIdx + 1, stl, ltl, absIdx, findChr, fResult)
|
||||||
|
else
|
||||||
|
case (stl, ltl) of
|
||||||
|
(stlhd :: stltl, ltlhd :: ltltl) =>
|
||||||
|
helpNextChr
|
||||||
|
(0, stlhd, absIdx + 1, stltl, ltltl, absIdx, findChr, fResult)
|
||||||
|
| (_, _) =>
|
||||||
|
(* tl is empty; just return absIdx *)
|
||||||
|
absIdx
|
||||||
|
|
||||||
|
fun nextChr (lineGap: LineGap.t, cursorIdx, chr, fResult) =
|
||||||
|
let
|
||||||
|
val {rightStrings, rightLines, idx = bufferIdx, ...} = lineGap
|
||||||
|
in
|
||||||
|
case (rightStrings, rightLines) of
|
||||||
|
(shd :: stl, lhd :: ltl) =>
|
||||||
|
let
|
||||||
|
(* convert absolute cursorIdx to idx relative to hd string *)
|
||||||
|
val strIdx = cursorIdx - bufferIdx
|
||||||
|
in
|
||||||
|
if strIdx < String.size shd then
|
||||||
|
(* strIdx is in this string *)
|
||||||
|
startNextChr
|
||||||
|
(shd, strIdx, cursorIdx, stl, ltl, chr, fResult)
|
||||||
|
else
|
||||||
|
(* strIdx is in tl *)
|
||||||
|
(case (stl, ltl) of
|
||||||
|
(stlhd :: stltl, ltlhd :: ltltl) =>
|
||||||
|
let
|
||||||
|
val strIdx = strIdx - String.size shd
|
||||||
|
in
|
||||||
|
startNextChr
|
||||||
|
(shd, strIdx, cursorIdx, stl, ltl, chr, fResult)
|
||||||
|
end
|
||||||
|
| (_, _) => cursorIdx)
|
||||||
|
end
|
||||||
|
| (_, _) => cursorIdx
|
||||||
|
end
|
||||||
|
|
||||||
|
fun tillNextChrResult absIdx = absIdx - 1
|
||||||
|
|
||||||
|
fun tillNextChr (lineGap, cursorIdx, chr) =
|
||||||
|
nextChr (lineGap, cursorIdx, chr, tillNextChrResult)
|
||||||
|
|
||||||
|
fun toNextChrResult absIdx = absIdx
|
||||||
|
|
||||||
|
fun toNextChr (lineGap, cursorIdx, chr) =
|
||||||
|
nextChr (lineGap, cursorIdx, chr, toNextChrResult)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user