refactor command-parsing code for normal mode, so that non-character events (KEY_ESC, RESIZE_EVENT, WITH_SEARCH_LIST) are dealt with at the beginning, and that the remainder of the parsing code looks at the current string and the new character typed. This is safe because pattern matching on the other commands (KEY_ESC, RESIZE_EVENT, WITH_SEARCH_LIST) always calls the same functions.
This commit is contained in:
@@ -21,24 +21,15 @@ struct
|
|||||||
AppWith.mode (app, mode, [])
|
AppWith.mode (app, mode, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
fun parseMoveToChr (count, app, fMove, newCmd) =
|
fun parseMoveToChr (count, app, fMove, chrCmd) =
|
||||||
case newCmd of
|
NormalMove.moveToChr (app, count, fMove, chrCmd)
|
||||||
CHAR_EVENT chr => NormalMove.moveToChr (app, count, fMove, chr)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) => Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList => Finish.withSearchList (app, searchList)
|
|
||||||
|
|
||||||
fun parseGo (count, app, newCmd) =
|
fun parseGo (count, app, chrCmd) =
|
||||||
case newCmd of
|
case chrCmd of
|
||||||
CHAR_EVENT chr =>
|
#"e" => MoveToEndOfPrevWord.move (app, count)
|
||||||
(case chr of
|
| #"E" => MoveToEndOfPrevWORD.move (app, count)
|
||||||
#"e" => MoveToEndOfPrevWord.move (app, count)
|
| #"g" => NormalMove.moveToStart app
|
||||||
| #"E" => MoveToEndOfPrevWORD.move (app, count)
|
| _ => Finish.clearMode app
|
||||||
| #"g" => NormalMove.moveToStart app
|
|
||||||
| _ => Finish.clearMode app)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) => Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList => Finish.withSearchList (app, searchList)
|
|
||||||
|
|
||||||
fun parseChr (app: app_type, count, chr, str) =
|
fun parseChr (app: app_type, count, chr, str) =
|
||||||
case chr of
|
case chr of
|
||||||
@@ -137,127 +128,69 @@ struct
|
|||||||
| #">" => NormalDelete.deleteAroundChrClose (app, chr)
|
| #">" => NormalDelete.deleteAroundChrClose (app, chr)
|
||||||
| _ => Finish.clearMode app
|
| _ => Finish.clearMode app
|
||||||
|
|
||||||
fun parseDelete (strPos, str, count, app, newCmd) =
|
fun parseDeleteTerminal (str, count, app, chrCmd) =
|
||||||
|
case chrCmd of
|
||||||
|
(* terminal commands: require no input after *)
|
||||||
|
#"h" => NormalDelete.delete (app, count, Cursor.viH)
|
||||||
|
| #"l" => NormalDelete.delete (app, count, Cursor.viL)
|
||||||
|
(* vi's 'j' and 'k' commands move up or down a column
|
||||||
|
* but 'dj' or 'dk' delete whole lines
|
||||||
|
* so their implementation differs from
|
||||||
|
* other cursor motions *)
|
||||||
|
| #"j" => NormalDelete.deleteLine (app, count + 1)
|
||||||
|
| #"k" => NormalDelete.deleteLineBack (app, count)
|
||||||
|
| #"w" => NormalDelete.deleteByDfa (app, count, Cursor.nextWord)
|
||||||
|
| #"W" => NormalDelete.deleteByDfa (app, count, Cursor.nextWORD)
|
||||||
|
| #"b" => NormalDelete.deleteByDfa (app, count, Cursor.prevWord)
|
||||||
|
| #"B" => NormalDelete.deleteByDfa (app, count, Cursor.prevWORD)
|
||||||
|
| #"e" => NormalDelete.deleteByDfa (app, count, Cursor.endOfWordForDelete)
|
||||||
|
| #"E" => NormalDelete.deleteByDfa (app, count, Cursor.endOfWORDForDelete)
|
||||||
|
| #"0" => NormalDelete.delete (app, 1, Cursor.vi0)
|
||||||
|
| #"$" => NormalDelete.deleteToEndOfLine app
|
||||||
|
| #"^" => NormalDelete.deleteToFirstNonSpaceChr app
|
||||||
|
| #"d" => NormalDelete.deleteLine (app, count)
|
||||||
|
| #"n" => NormalDelete.deleteToNextMatch (app, count)
|
||||||
|
| #"N" => NormalDelete.deleteToPrevMatch (app, count)
|
||||||
|
| #"%" => NormalDelete.deletePair app
|
||||||
|
(* non-terminal commands which require appending chr *)
|
||||||
|
| #"t" => appendChr (app, chrCmd, str)
|
||||||
|
| #"T" => appendChr (app, chrCmd, str)
|
||||||
|
| #"f" => appendChr (app, chrCmd, str)
|
||||||
|
| #"F" => appendChr (app, chrCmd, str)
|
||||||
|
| #"g" => appendChr (app, chrCmd, str)
|
||||||
|
| #"i" => appendChr (app, chrCmd, str)
|
||||||
|
| #"a" => appendChr (app, chrCmd, str)
|
||||||
|
(* invalid command: reset mode *)
|
||||||
|
| _ => Finish.clearMode app
|
||||||
|
|
||||||
|
fun parseDeleteGo (app, count, chrCmd) =
|
||||||
|
case chrCmd of
|
||||||
|
#"e" => NormalDelete.deleteByDfa (app, count, Cursor.endOfPrevWord)
|
||||||
|
| #"E" => NormalDelete.deleteByDfa (app, count, Cursor.endOfPrevWORD)
|
||||||
|
| #"g" => NormalDelete.deleteToStart app
|
||||||
|
| _ => Finish.clearMode app
|
||||||
|
|
||||||
|
fun parseDelete (strPos, str, count, app, chrCmd) =
|
||||||
if strPos = String.size str - 1 then
|
if strPos = String.size str - 1 then
|
||||||
(* have to check newCmd *)
|
parseDeleteTerminal (str, count, app, chrCmd)
|
||||||
case newCmd of
|
|
||||||
CHAR_EVENT chr =>
|
|
||||||
(case chr of
|
|
||||||
(* terminal commands: require no input after *)
|
|
||||||
#"h" => NormalDelete.delete (app, count, Cursor.viH)
|
|
||||||
| #"l" => NormalDelete.delete (app, count, Cursor.viL)
|
|
||||||
(* vi's 'j' and 'k' commands move up or down a column
|
|
||||||
* but 'dj' or 'dk' delete whole lines
|
|
||||||
* so their implementation differs from
|
|
||||||
* other cursor motions *)
|
|
||||||
| #"j" => NormalDelete.deleteLine (app, count + 1)
|
|
||||||
| #"k" => NormalDelete.deleteLineBack (app, count)
|
|
||||||
| #"w" => NormalDelete.deleteByDfa (app, count, Cursor.nextWord)
|
|
||||||
| #"W" => NormalDelete.deleteByDfa (app, count, Cursor.nextWORD)
|
|
||||||
| #"b" => NormalDelete.deleteByDfa (app, count, Cursor.prevWord)
|
|
||||||
| #"B" => NormalDelete.deleteByDfa (app, count, Cursor.prevWORD)
|
|
||||||
| #"e" =>
|
|
||||||
NormalDelete.deleteByDfa (app, count, Cursor.endOfWordForDelete)
|
|
||||||
| #"E" =>
|
|
||||||
NormalDelete.deleteByDfa (app, count, Cursor.endOfWORDForDelete)
|
|
||||||
| #"0" => NormalDelete.delete (app, 1, Cursor.vi0)
|
|
||||||
| #"$" => NormalDelete.deleteToEndOfLine app
|
|
||||||
| #"^" => NormalDelete.deleteToFirstNonSpaceChr app
|
|
||||||
| #"d" => NormalDelete.deleteLine (app, count)
|
|
||||||
| #"n" => NormalDelete.deleteToNextMatch (app, count)
|
|
||||||
| #"N" => NormalDelete.deleteToPrevMatch (app, count)
|
|
||||||
| #"%" => NormalDelete.deletePair app
|
|
||||||
(* non-terminal commands which require appending chr *)
|
|
||||||
| #"t" => appendChr (app, chr, str)
|
|
||||||
| #"T" => appendChr (app, chr, str)
|
|
||||||
| #"f" => appendChr (app, chr, str)
|
|
||||||
| #"F" => appendChr (app, chr, str)
|
|
||||||
| #"g" => appendChr (app, chr, str)
|
|
||||||
| #"i" => appendChr (app, chr, str)
|
|
||||||
| #"a" => appendChr (app, chr, str)
|
|
||||||
(* invalid command: reset mode *)
|
|
||||||
| _ => Finish.clearMode app)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) => Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList => Finish.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
|
||||||
#"t" =>
|
#"t" =>
|
||||||
(* delete till chr, forwards *)
|
NormalDelete.deleteToChr (app, 1, Cursor.tillNextChr, op+, chrCmd)
|
||||||
(case newCmd of
|
|
||||||
CHAR_EVENT chr =>
|
|
||||||
NormalDelete.deleteToChr (app, 1, Cursor.tillNextChr, op+, chr)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| #"T" =>
|
| #"T" =>
|
||||||
(* delete till chr, backwards *)
|
NormalDelete.deleteToChr (app, 1, Cursor.tillPrevChr, op-, chrCmd)
|
||||||
(case newCmd of
|
|
||||||
CHAR_EVENT chr =>
|
|
||||||
NormalDelete.deleteToChr (app, 1, Cursor.tillPrevChr, op-, chr)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| #"f" =>
|
| #"f" =>
|
||||||
(case newCmd of
|
NormalDelete.deleteToChr (app, count, Cursor.toNextChr, op+, chrCmd)
|
||||||
CHAR_EVENT chr =>
|
|
||||||
NormalDelete.deleteToChr (app, count, Cursor.toNextChr, op+, chr)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| #"F" =>
|
| #"F" =>
|
||||||
(* delete to chr, backwards *)
|
NormalDelete.deleteToChr (app, count, Cursor.toPrevChr, op-, chrCmd)
|
||||||
(case newCmd of
|
| #"g" => parseDeleteGo (app, count, chrCmd)
|
||||||
CHAR_EVENT chr =>
|
| #"i" => parseDeleteInside (app, chrCmd)
|
||||||
NormalDelete.deleteToChr (app, count, Cursor.toPrevChr, op-, chr)
|
| #"a" => parseDeleteAround (app, chrCmd)
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| #"g" =>
|
|
||||||
(* same events as parseGo *)
|
|
||||||
(case newCmd of
|
|
||||||
CHAR_EVENT chr =>
|
|
||||||
(case chr of
|
|
||||||
#"e" =>
|
|
||||||
NormalDelete.deleteByDfa (app, count, Cursor.endOfPrevWord)
|
|
||||||
| #"E" =>
|
|
||||||
NormalDelete.deleteByDfa (app, count, Cursor.endOfPrevWORD)
|
|
||||||
| #"g" => NormalDelete.deleteToStart app
|
|
||||||
| _ => Finish.clearMode app)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| #"i" =>
|
|
||||||
(case newCmd of
|
|
||||||
CHAR_EVENT chr => parseDeleteInside (app, chr)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| #"a" =>
|
|
||||||
(case newCmd of
|
|
||||||
CHAR_EVENT chr => parseDeleteAround (app, chr)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList))
|
|
||||||
| _ => Finish.clearMode app
|
| _ => Finish.clearMode app
|
||||||
|
|
||||||
(* useful reference as list of non-terminal commands *)
|
(* useful reference as list of non-terminal commands *)
|
||||||
fun parseAfterCount (strPos, str, count, app, newCmd) =
|
fun parseAfterCount (strPos, str, count, app, chrCmd) =
|
||||||
(* 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
|
||||||
* to go 3 words forwards
|
* to go 3 words forwards
|
||||||
@@ -270,41 +203,32 @@ struct
|
|||||||
* tillNextChr with count of 1 has same effect
|
* tillNextChr with count of 1 has same effect
|
||||||
* as tillNextChr with any count above 1
|
* as tillNextChr with any count above 1
|
||||||
* so just hardcode 1 *)
|
* so just hardcode 1 *)
|
||||||
parseMoveToChr (1, app, Cursor.tillNextChr, newCmd)
|
parseMoveToChr (1, app, Cursor.tillNextChr, chrCmd)
|
||||||
| #"T" =>
|
| #"T" =>
|
||||||
(* to just before chr, backward *)
|
(* to just before chr, backward *)
|
||||||
parseMoveToChr (1, app, Cursor.tillPrevChr, newCmd)
|
parseMoveToChr (1, app, Cursor.tillPrevChr, chrCmd)
|
||||||
| #"y" => (* yank *) Finish.clearMode app
|
| #"y" => (* yank *) Finish.clearMode app
|
||||||
| #"d" => (* delete *) parseDelete (strPos, str, count, app, newCmd)
|
| #"d" => (* delete *) parseDelete (strPos, str, count, app, chrCmd)
|
||||||
| #"f" =>
|
| #"f" =>
|
||||||
(* to chr, forward *)
|
(* to chr, forward *)
|
||||||
parseMoveToChr (count, app, Cursor.toNextChr, newCmd)
|
parseMoveToChr (count, app, Cursor.toNextChr, chrCmd)
|
||||||
| #"F" =>
|
| #"F" =>
|
||||||
(* to chr, backward *)
|
(* to chr, backward *)
|
||||||
parseMoveToChr (count, app, Cursor.toPrevChr, newCmd)
|
parseMoveToChr (count, app, Cursor.toPrevChr, chrCmd)
|
||||||
| #"g" => (* go *) parseGo (count, app, newCmd)
|
| #"g" => (* go *) parseGo (count, app, chrCmd)
|
||||||
| #"c" => (* change *) Finish.clearMode app
|
| #"c" => (* change *) Finish.clearMode app
|
||||||
| _ =>
|
| _ =>
|
||||||
(* isn't a non-terminal cmd
|
(* isn't a non-terminal cmd
|
||||||
* this case should never happen*)
|
* this case should never happen*)
|
||||||
Finish.clearMode app
|
Finish.clearMode app
|
||||||
|
|
||||||
fun parseNormalModeCommand (app, str, newCmd) =
|
fun parseNormalModeCommand (app, str, chrCmd) =
|
||||||
if String.size str = 0 then
|
if String.size str = 0 then
|
||||||
case newCmd of
|
parseChr (app, 1, chrCmd, str)
|
||||||
CHAR_EVENT chr => parseChr (app, 1, chr, str)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) => Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList => Finish.withSearchList (app, searchList)
|
|
||||||
else if String.size str = 1 then
|
else if String.size str = 1 then
|
||||||
case newCmd of
|
case Int.fromString str of
|
||||||
CHAR_EVENT chr =>
|
SOME count => parseChr (app, count, chrCmd, str)
|
||||||
(case Int.fromString str of
|
| NONE => parseAfterCount (0, str, 1, app, chrCmd)
|
||||||
SOME count => parseChr (app, count, chr, str)
|
|
||||||
| NONE => parseAfterCount (0, str, 1, app, newCmd))
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) => Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList => Finish.withSearchList (app, searchList)
|
|
||||||
else
|
else
|
||||||
let
|
let
|
||||||
val numLength = getNumLength (0, str)
|
val numLength = getNumLength (0, str)
|
||||||
@@ -316,17 +240,16 @@ struct
|
|||||||
in
|
in
|
||||||
if numLength = String.size str then
|
if numLength = String.size str then
|
||||||
(* reached end of str; str only contained numbers *)
|
(* reached end of str; str only contained numbers *)
|
||||||
case newCmd of
|
parseChr (app, count, chrCmd, str)
|
||||||
CHAR_EVENT chr => parseChr (app, count, chr, str)
|
|
||||||
| KEY_ESC => Finish.clearMode app
|
|
||||||
| RESIZE_EVENT (width, height) =>
|
|
||||||
Finish.resizeText (app, width, height)
|
|
||||||
| WITH_SEARCH_LIST searchList =>
|
|
||||||
Finish.withSearchList (app, searchList)
|
|
||||||
else
|
else
|
||||||
(* continue parsing. *)
|
(* continue parsing. *)
|
||||||
parseAfterCount (numLength, str, count, app, newCmd)
|
parseAfterCount (numLength, str, count, app, chrCmd)
|
||||||
end
|
end
|
||||||
|
|
||||||
fun update (app, str, msg) = parseNormalModeCommand (app, str, msg)
|
fun update (app, str, msg) =
|
||||||
|
case msg of
|
||||||
|
CHAR_EVENT chrCmd => parseNormalModeCommand (app, str, chrCmd)
|
||||||
|
| KEY_ESC => Finish.clearMode app
|
||||||
|
| RESIZE_EVENT (width, height) => Finish.resizeText (app, width, height)
|
||||||
|
| WITH_SEARCH_LIST searchList => Finish.withSearchList (app, searchList)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user