import tools from abc import ABC, abstractmethod from mvc.views import CursesAdapter from mvc.models import VimModel, ReturnCode def CommonInput(HandleInputFunc): """common actions for all strategies decorator""" def wrapper(self, symbolCode: int) -> int: result = self.HandleCommonInput(symbolCode) if result != ReturnCode.CONTINUE: # an action common to all strategies occurred self.model.Scroll() return result return_code = HandleInputFunc(self, symbolCode) self.model.Scroll() return return_code return wrapper class BaseStrategy(ABC): def __init__(self, model: VimModel, adapter: CursesAdapter, mode: str): self.model = model self.adapter = adapter self.mode = model.mode = mode def HandleCommonInput(self, symbolCode: int) -> int: """common actions for all strategies""" match symbolCode: case self.adapter.KEY_ESCAPE: return ReturnCode.SET_BASIC_MODE case self.adapter.KEY_LEFT: self.model.MoveLeft() case self.adapter.KEY_RIGHT: self.model.MoveRight() case self.adapter.KEY_UP: self.model.MoveUp() case self.adapter.KEY_DOWN: self.model.MoveDown() case self.adapter.KEY_CTRL_S: self.model.SaveFile() return ReturnCode.CONTINUE @abstractmethod def HandleInput(self, symbolCode: int) -> int: pass class Controller: def __init__(self, strategy: BaseStrategy): self.strategy = strategy def SetStrategy(self, strategy: BaseStrategy): self.strategy = strategy def HandleInput(self, symbolCode: int): return self.strategy.HandleInput(symbolCode) class CommandStrategy(BaseStrategy): """command input mode""" def __init__(self, model: VimModel, adapter: CursesAdapter): super().__init__(model, adapter, mode="COMMAND") @CommonInput def HandleInput(self, symbolCode) -> int: """user input processing""" returnCode = ReturnCode.GOOD match symbolCode: case self.adapter.KEY_ENTER: returnCode = self.model.EnterCommand() case self.adapter.KEY_BACKSPACE_1 | self.adapter.KEY_BACKSPACE_2: self.model.BackspaceCommand() case _: if not tools.isAscii(symbolCode): return ReturnCode.GOOD self.model.InsertCommandSymbol(symbolCode) return returnCode class EditStrategy(BaseStrategy): """edit mode""" def __init__(self, model: VimModel, adapter: CursesAdapter): super().__init__(model, adapter, mode="EDIT") @CommonInput def HandleInput(self, symbolCode) -> int: """user input processing""" match symbolCode: case self.adapter.KEY_ENTER: self.model.Enter() case self.adapter.KEY_BACKSPACE_1 | self.adapter.KEY_BACKSPACE_2: self.model.Backspace() case _: if not tools.isAscii(symbolCode): return ReturnCode.GOOD if not self.model.inputAfterCursor: self.model.InsertSymbol(symbolCode) else: self.model.InsertSymbolAfterCursor(symbolCode) return ReturnCode.GOOD class NormalStrategy(BaseStrategy): """navigation mode""" def __init__(self, model: VimModel, adapter: CursesAdapter): super().__init__(model, adapter, mode="NORMAL") @CommonInput def HandleInput(self, symbolCode) -> int: """user input processing""" self.model.UpdateKeyLog(symbolCode) match symbolCode: case self.adapter.KEY_TWO_DOTS: return ReturnCode.SET_COMMAND_MODE case self.adapter.KEY_NULL | self.adapter.KEY_XOR: self.model.ToLineStart() case self.adapter.KEY_DOLLAR: self.model.ToLineEnd() case self.adapter.KEY_p: self.model.Paste() case self.adapter.KEY_w: if self.model.CombinationCheck("diw", 3): self.model.DeleteWord() elif self.model.CombinationCheck("yw", 3): self.model.CopyWord() else: self.model.ToWordEnd() case self.adapter.KEY_b: self.model.ToWordStart() case self.adapter.KEY_d: if self.model.CombinationCheck("dd", 3): self.model.CutLine() case self.adapter.KEY_y: if self.model.CombinationCheck("yy", 3): self.model.CopyLine() case self.adapter.KEY_x: self.model.DeleteNext() case self.adapter.KEY_U: self.model.RecoverLine() case self.adapter.KEY_G: num, ind = tools.ExtracrtNumBeforeLastKey(''.join([chr(item[0]) for item in self.model.keyLog])) if num != None and ind != None: self.model.MoveToLine(num) else: self.model.ToFileEnd() case self.adapter.KEY_g: if self.model.CombinationCheck("gg", 3): self.model.ToFileStart() case self.adapter.KEY_PG_UP: self.model.PageUp() case self.adapter.KEY_PG_DOWN: self.model.PageDown() return ReturnCode.GOOD