diff --git a/fcore/app-update.sml b/fcore/app-update.sml index 1487098..e3c5b3a 100644 --- a/fcore/app-update.sml +++ b/fcore/app-update.sml @@ -19,49 +19,69 @@ struct (newApp, drawMsg) end - fun moveBackward (app: app_type, fMove) = - let - val {buffer, windowWidth, windowHeight, startLine, cursorIdx, ...} = app + fun helpMoveBackward (app: app_type, buffer, cursorIdx, count, fMove) = + 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 (* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *) - val buffer = LineGap.goToIdx (cursorIdx, buffer) - val cursorIdx = fMove (buffer, cursorIdx) + let + val buffer = LineGap.goToIdx (cursorIdx, buffer) + val cursorIdx = fMove (buffer, cursorIdx) + in + helpMoveBackward (app, buffer, cursorIdx, count - 1, fMove) + end - (* 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 newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx) - in - (newApp, drawMsg) + fun moveBackward (app: app_type, count, fMove) = + let val {cursorIdx, buffer, ...} = app + in helpMoveBackward (app, buffer, cursorIdx, count, fMove) end - fun moveFowrards (app: app_type, fMove) = - let - val {buffer, windowWidth, windowHeight, startLine, cursorIdx, ...} = app + fun helpMoveForwards (app: app_type, buffer, cursorIdx, count, fMove) = + if count = 0 then + let + val {windowWidth, windowHeight, startLine, ...} = app + (* todo: get new startLine if cursor has moved out of screen *) - (* move LineGap to cursorIdx, which is necessary for finding newCursorIdx *) - val buffer = LineGap.goToIdx (cursorIdx, buffer) - val cursorIdx = fMove (buffer, cursorIdx) + (* 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) - (* todo: get new startLine if cursor has moved out of screen *) + 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) + in + helpMoveForwards (app, buffer, cursorIdx, count - 1, fMove) + end - (* 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 newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx) - in - (newApp, drawMsg) + fun moveForwards (app: app_type, count, fMove) = + let val {cursorIdx, buffer, ...} = app + in helpMoveForwards (app, buffer, cursorIdx, count, fMove) end fun firstNonSpaceChr (app: app_type) = let - val {buffer, windowWidth, windowHeight, startLine, cursorIdx, ...} = app + val {buffer, cursorIdx, windowWidth, windowHeight, startLine, ...} = app (* move LineGap and buffer to start of line *) val buffer = LineGap.goToIdx (cursorIdx, buffer) @@ -78,30 +98,79 @@ struct val drawMsg = TextBuilder.build (startLine, cursorIdx, buffer, windowWidth, windowHeight) - val newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx) + val mode = NORMAL_MODE "" + val newApp = AppWith.bufferAndCursorIdx (app, buffer, cursorIdx, mode) in (newApp, drawMsg) end - fun handleChr (app: app_type, chr) = + (* number of characters which are integers *) + fun getNumLength (pos, str) = + if pos = String.size str then + pos + else + let + val chr = String.sub (str, pos) + in + if chr >= #"0" andalso chr <= #"9" then getNumLength (pos + 1, str) + else pos + end + + fun handleChr (app: app_type, count, chr, str) = case chr of - #"h" => moveBackward (app, Cursor.viH) - | #"j" => moveFowrards (app, Cursor.viJ) - | #"k" => moveBackward (app, Cursor.viK) - | #"l" => moveFowrards (app, Cursor.viL) - | #"0" => moveBackward (app, Cursor.vi0) - | #"$" => moveFowrards (app, Cursor.viDlr) - | #"w" => moveFowrards (app, Cursor.nextWord) - | #"W" => moveFowrards (app, Cursor.nextWORD) - | #"b" => moveBackward (app, Cursor.prevWord) - | #"B" => moveBackward (app, Cursor.prevWORD) - | #"e" => moveFowrards (app, Cursor.endOfWord) - | #"E" => moveFowrards (app, Cursor.endOfWORD) + #"h" => moveBackward (app, count, Cursor.viH) + | #"j" => moveForwards (app, count, Cursor.viJ) + | #"k" => moveBackward (app, count, Cursor.viK) + | #"l" => moveForwards (app, count, Cursor.viL) + | #"0" => moveBackward (app, 1, Cursor.vi0) + | #"$" => moveForwards (app, 1, Cursor.viDlr) + | #"w" => moveForwards (app, count, Cursor.nextWord) + | #"W" => moveForwards (app, count, Cursor.nextWORD) + | #"b" => moveBackward (app, count, Cursor.prevWord) + | #"B" => moveBackward (app, count, Cursor.prevWORD) + | #"e" => moveForwards (app, count, Cursor.endOfWord) + | #"E" => moveForwards (app, count, Cursor.endOfWORD) | #"^" => firstNonSpaceChr app - | _ => (app, []) + | _ => + (* user may be entering a cmd with more than one chr + * such as "2dw" to delete two word + * so add current chr to mode, and save it in the app state *) + let + val str = + if chr >= #"0" andalso chr <= #"9" then str ^ Char.toString chr + else "" + val mode = NORMAL_MODE str + val newApp = AppWith.mode (app, mode) + in + (newApp, []) + end + + fun parseNormalModeCommand (app, str, newCmd) = + if String.size str = 0 then + case newCmd of + RESIZE_EVENT (width, height) => resizeText (app, width, height) + | CHAR_EVENT chr => handleChr (app, 1, chr, str) + else + let + val numLength = getNumLength (0, str) + val count = String.substring (str, 0, numLength) + val count = + case Int.fromString count of + SOME x => x + | NONE => 1 + in + if numLength = String.size str then + (* reached end of str; str only contained numbers *) + case newCmd of + RESIZE_EVENT (width, height) => resizeText (app, width, height) + | CHAR_EVENT chr => handleChr (app, count, chr, str) + else + (* todo: continue parsing. *) + raise Match + end + + fun updateNormalMode (app, str, msg) = parseNormalModeCommand (app, str, msg) fun update (app, msg) = - case msg of - RESIZE_EVENT (width, height) => resizeText (app, width, height) - | CHAR_EVENT chr => handleChr (app, chr) + case #mode app of NORMAL_MODE str => updateNormalMode (app, str, msg) end diff --git a/fcore/app-with.sml b/fcore/app-with.sml index 9cdd9d4..632974c 100644 --- a/fcore/app-with.sml +++ b/fcore/app-with.sml @@ -22,13 +22,18 @@ struct } end - fun bufferAndCursorIdx (app: app_type, newBuffer, newCursorIdx) = + fun bufferAndCursorIdx (app: app_type, newBuffer, newCursorIdx, newMode) = let val - {mode, buffer = _, cursorIdx = _, windowWidth, windowHeight, startLine} = - app + { mode = _ + , buffer = _ + , cursorIdx = _ + , windowWidth + , windowHeight + , startLine + } = app in - { mode = mode + { mode = newMode , buffer = newBuffer , cursorIdx = newCursorIdx , windowWidth = windowWidth @@ -36,4 +41,18 @@ struct , startLine = startLine } end + + fun mode (app: app_type, newMode) = + let + val {mode = _, buffer, cursorIdx, windowWidth, windowHeight, startLine} = + app + in + { mode = newMode + , buffer = buffer + , cursorIdx = cursorIdx + , windowWidth = windowWidth + , windowHeight = windowHeight + , startLine = startLine + } + end end diff --git a/shf b/shf index 87dfa96..e9e83af 100755 Binary files a/shf and b/shf differ