text editor inspired vim and yi
import ViE.State
import ViE.Types
import ViE.Command.Dispatch

namespace ViE.Key

open ViE

def handleCommandInput (commands : CommandMap) (state : EditorState) (k : Key) : IO EditorState := do
  match k with
  | Key.esc => return { state with mode := .normal, message := "", inputState := { state.inputState with commandBuffer := "" } }
  | Key.enter =>
    let newState ← ViE.Command.executeCommand commands state
    if newState.shouldQuit then
      return newState
    else
      return { newState with mode := .normal, inputState := { newState.inputState with commandBuffer := "" } }
  | Key.backspace =>
    if state.inputState.commandBuffer.length > 0 then
      let newCmd := state.inputState.commandBuffer.dropEnd 1
      return { state with inputState := { state.inputState with commandBuffer := newCmd.toString } }
    else
      return state
  | Key.char c => return { state with inputState := { state.inputState with commandBuffer := state.inputState.commandBuffer.push c } }
  | _ => return state

def handleSearchInput (state : EditorState) (k : Key) : IO EditorState := do
  match k with
  | Key.esc =>
      return {
        state with
          mode := .normal
          inputState := { state.inputState with commandBuffer := "", pendingSearch := false }
      }
  | Key.enter =>
      let pattern := state.inputState.commandBuffer
      if pattern.isEmpty then
        return {
          state with
            mode := .normal
            message := "Empty search pattern"
            inputState := { state.inputState with commandBuffer := "", pendingSearch := false }
        }
      else
        let direction := if state.mode == .searchBackward then SearchDirection.backward else SearchDirection.forward
        let s' :=
          match state.searchState with
          | some st =>
              if st.pattern == pattern && st.direction == direction then
                state
              else
                ViE.startOrUpdateSearch state pattern direction false
          | none =>
              ViE.startOrUpdateSearch state pattern direction false
        let s'' := ViE.findNextMatch s' (some direction)
        return {
          s'' with
            mode := .normal
            inputState := { s''.inputState with commandBuffer := "", pendingSearch := false }
        }
  | Key.backspace =>
      if state.inputState.commandBuffer.length > 0 then
        let newCmd := state.inputState.commandBuffer.dropEnd 1
        if newCmd.isEmpty then
          return {
            state with
              searchState := none
              message := ""
              dirty := true
              inputState := {
                state.inputState with
                  commandBuffer := ""
                  pendingSearch := false
              }
          }
        else
        let now ← IO.monoMsNow
        let s1 := {
          state with
            inputState := {
              state.inputState with
                commandBuffer := newCmd.toString
                lastSearchInputTime := now
                pendingSearch := true
            }
        }
        if now - state.inputState.lastSearchRunTime >= ViE.incrementalSearchDebounceMs then
          let s2 := ViE.runIncrementalSearch s1
          return { s2 with inputState := { s2.inputState with pendingSearch := false, lastSearchRunTime := now } }
        else
          return s1
      else
        return state
  | Key.char c =>
      let now ← IO.monoMsNow
      let s1 := {
        state with
          inputState := {
            state.inputState with
              commandBuffer := state.inputState.commandBuffer.push c
              lastSearchInputTime := now
              pendingSearch := true
          }
      }
      if now - state.inputState.lastSearchRunTime >= ViE.incrementalSearchDebounceMs then
        let s2 := ViE.runIncrementalSearch s1
        return { s2 with inputState := { s2.inputState with pendingSearch := false, lastSearchRunTime := now } }
      else
        return s1
  | _ => return state

def clearInput (s : EditorState) : EditorState :=
  { s with inputState := { s.inputState with countBuffer := "", previousKey := none } }

end ViE.Key