сделал normal mode, начал писать режим ввода команд
parent
d92d7adc9b
commit
b37156ef79
19
main.py
19
main.py
|
@ -1,13 +1,13 @@
|
|||
from mvc.models import VimModel
|
||||
from mvc.views import VimView
|
||||
from mvc.controllers import Controller, EditStrategy
|
||||
from mvc.controllers import Controller, ReturnCode
|
||||
from mvc.controllers import EditStrategy, CommandStrategy, NormalStrategy
|
||||
|
||||
def main():
|
||||
model = VimModel()
|
||||
view = VimView()
|
||||
model, view = VimModel(), VimView()
|
||||
|
||||
# начальный режим - редактирование
|
||||
strategy = EditStrategy(model, view.curses_adapter)
|
||||
strategy = NormalStrategy(model, view.curses_adapter)
|
||||
controller = Controller(strategy)
|
||||
|
||||
# Загрузка файла для редактирования
|
||||
|
@ -18,10 +18,17 @@ def main():
|
|||
view.Render(model.displayBuffer,
|
||||
model.currentLine, model.currentCol,
|
||||
model.scrollX, model.scrollY,
|
||||
model.file_path)
|
||||
model.ModeBar())
|
||||
|
||||
symbolCode = view.curses_adapter.GetChar()
|
||||
if not controller.HandleInput(symbolCode): break
|
||||
|
||||
match controller.HandleInput(symbolCode):
|
||||
case ReturnCode.SET_BASIC_MODE:
|
||||
controller.SetStrategy(NormalStrategy(model, view.curses_adapter))
|
||||
case ReturnCode.SET_COMMAND_MODE:
|
||||
controller.SetStrategy(CommandStrategy(model, view.curses_adapter))
|
||||
case ReturnCode.EXIT_CODE:
|
||||
break
|
||||
|
||||
view.curses_adapter.Cleanup()
|
||||
|
||||
|
|
|
@ -1,33 +1,67 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from mvc.views import CursesAdapter
|
||||
from mvc.models import VimModel
|
||||
|
||||
class IStrategy(ABC):
|
||||
def __init__(self, model: VimModel, adapter: CursesAdapter):
|
||||
from mvc.views import CursesAdapter
|
||||
from mvc.models import VimModel, ReturnCode
|
||||
|
||||
def isAscii(symbolCode: int) -> bool:
|
||||
if symbolCode > 126 or symbolCode < 32: return False
|
||||
else: return True
|
||||
|
||||
class Strategy(ABC):
|
||||
def __init__(self, model: VimModel, adapter: CursesAdapter, mode: str):
|
||||
self.model = model
|
||||
self.adapter = adapter
|
||||
self.mode = model.mode = mode
|
||||
|
||||
def isAscii(symbolCode: int) -> bool:
|
||||
if symbolCode > 126 or symbolCode < 32: return False
|
||||
else: return True
|
||||
|
||||
@abstractmethod
|
||||
def HandleInput(self, symbolCode: int) -> bool:
|
||||
def HandleInput(self, symbolCode: int) -> int:
|
||||
pass
|
||||
|
||||
class Controller:
|
||||
def __init__(self, strategy: IStrategy):
|
||||
def __init__(self, strategy: Strategy):
|
||||
self.strategy = strategy
|
||||
def SetController(self, strategy: IStrategy):
|
||||
def SetStrategy(self, strategy: Strategy):
|
||||
self.strategy = strategy
|
||||
def HandleInput(self, symbolCode: int):
|
||||
return self.strategy.HandleInput(symbolCode)
|
||||
|
||||
class EditStrategy(IStrategy):
|
||||
class CommandStrategy(Strategy):
|
||||
"""Режим ввода коман"""
|
||||
def __init__(self, model: VimModel, adapter: CursesAdapter):
|
||||
self.model = model
|
||||
self.adapter = adapter
|
||||
|
||||
def HandleInput(self, symbolCode):
|
||||
super().__init__(model, adapter, mode="COMMAND")
|
||||
|
||||
def HandleInput(self, symbolCode) -> int:
|
||||
"""Обработка ввода пользователя"""
|
||||
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()
|
||||
case self.adapter.KEY_ENTER: return self.model.EnterCommand()
|
||||
case self.adapter.KEY_BACKSPACE_1: self.model.BackspaceCommand()
|
||||
case self.adapter.KEY_BACKSPACE_2: self.model.BackspaceCommand()
|
||||
case _:
|
||||
if not isAscii(symbolCode): return ReturnCode.GOOD
|
||||
self.model.InsertCommandSymbol(symbolCode)
|
||||
|
||||
self.model.Scroll(self.adapter.lines, self.adapter.cols)
|
||||
return ReturnCode.GOOD
|
||||
|
||||
|
||||
class EditStrategy(Strategy):
|
||||
"""Режим редактирования"""
|
||||
def __init__(self, model: VimModel, adapter: CursesAdapter):
|
||||
super().__init__(model, adapter, mode="EDIT")
|
||||
|
||||
def HandleInput(self, symbolCode) -> int:
|
||||
"""Обработка ввода пользователя"""
|
||||
match symbolCode:
|
||||
case self.adapter.KEY_ESCAPE: return False
|
||||
case self.adapter.KEY_LEFT: self.model.MoveLeft()
|
||||
case self.adapter.KEY_RIGHT: self.model.MoveRight()
|
||||
case self.adapter.KEY_UP: self.model.MoveUp()
|
||||
|
@ -37,10 +71,26 @@ class EditStrategy(IStrategy):
|
|||
case self.adapter.KEY_BACKSPACE_2: self.model.Backspace()
|
||||
case self.adapter.KEY_CTRL_S: self.model.SaveFile()
|
||||
case _:
|
||||
if symbolCode > 127 or symbolCode < 0:
|
||||
print("Only 1-byte symbols!")
|
||||
return False
|
||||
if not isAscii(symbolCode): return ReturnCode.GOOD
|
||||
self.model.InsertSymbol(symbolCode)
|
||||
|
||||
self.model.Scroll(self.adapter.lines, self.adapter.cols)
|
||||
return True
|
||||
return ReturnCode.GOOD
|
||||
|
||||
class NormalStrategy(Strategy):
|
||||
"""Режим навигации"""
|
||||
def __init__(self, model: VimModel, adapter: CursesAdapter):
|
||||
super().__init__(model, adapter, mode="NORMAL")
|
||||
|
||||
def HandleInput(self, symbolCode) -> int:
|
||||
"""Обработка ввода пользователя"""
|
||||
match symbolCode:
|
||||
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()
|
||||
case self.adapter.KEY_TWO_DOTS: return ReturnCode.SET_COMMAND_MODE
|
||||
|
||||
self.model.Scroll(self.adapter.lines, self.adapter.cols)
|
||||
return ReturnCode.GOOD
|
|
@ -1,3 +1,11 @@
|
|||
from enum import Enum
|
||||
|
||||
class ReturnCode(Enum):
|
||||
GOOD = -101
|
||||
EXIT_CODE = -100
|
||||
SET_BASIC_MODE = -99
|
||||
SET_COMMAND_MODE = -98
|
||||
|
||||
class VimModel:
|
||||
def __init__(self):
|
||||
self.displayBuffer = [] # буфер для хранения всех строк
|
||||
|
@ -6,6 +14,14 @@ class VimModel:
|
|||
self.scrollY = 0 # вертикальная прокрутка
|
||||
self.scrollX = 0 # горизонтальная прокрутка
|
||||
self.file_path = "" # путь к файлу
|
||||
self.mode = ""
|
||||
self.commandBuffer = [] # буффер для команды
|
||||
|
||||
def ModeBar(self) -> str:
|
||||
modeBar = f"MODE: {self.mode} | FILE: {self.file_path} | LINE: {self.currentLine+1}/{len(self.displayBuffer)}"
|
||||
if self.mode == "COMMAND":
|
||||
modeBar += f" | COMMAND BUFFER: {''.join(self.commandBuffer)}"
|
||||
return modeBar
|
||||
|
||||
def Scroll(self, displayLines: int, displayCols: int) -> None:
|
||||
if self.currentLine < self.scrollY:
|
||||
|
@ -18,11 +34,24 @@ class VimModel:
|
|||
elif self.currentCol >= self.scrollX + displayCols:
|
||||
self.scrollX = self.currentCol - displayCols + 1
|
||||
|
||||
def InsertCommandSymbol(self, symbolCode: int) -> None:
|
||||
self.commandBuffer.append(chr(symbolCode))
|
||||
|
||||
def InsertSymbol(self, symbolCode: int) -> None:
|
||||
if self.currentCol <= len(self.displayBuffer[self.currentLine]): # проверяем, не превышает ли индекс колонки длину строки
|
||||
self.displayBuffer[self.currentLine].insert(self.currentCol, chr(symbolCode))
|
||||
self.currentCol += 1
|
||||
|
||||
def EnterCommand(self):
|
||||
cmd = ''.join(self.commandBuffer)
|
||||
self.commandBuffer.clear()
|
||||
match cmd:
|
||||
case "$":
|
||||
self.currentCol = len(self.displayBuffer[self.currentLine])
|
||||
return ReturnCode.GOOD
|
||||
case "q":
|
||||
return ReturnCode.EXIT_CODE
|
||||
|
||||
def Enter(self) -> None:
|
||||
# Разделяем текущую строку на две части
|
||||
new_line = self.displayBuffer[self.currentLine][self.currentCol:]
|
||||
|
@ -31,6 +60,10 @@ class VimModel:
|
|||
self.displayBuffer.insert(self.currentLine, new_line) # Вставляем новую строку
|
||||
self.currentCol = 0 # Сбрасываем индекс колонки
|
||||
|
||||
def BackspaceCommand(self) -> None:
|
||||
if len(self.commandBuffer) > 0:
|
||||
self.commandBuffer.pop()
|
||||
|
||||
def Backspace(self) -> None:
|
||||
if self.currentCol > 0: # Если символ существует в текущей строке
|
||||
self.currentCol -= 1
|
||||
|
|
17
mvc/views.py
17
mvc/views.py
|
@ -8,7 +8,7 @@ class VimView:
|
|||
displayBuffer: list[list[str]],
|
||||
currentLine: int, currentCol: int,
|
||||
scrollX: int, scrollY: int,
|
||||
file_path: str):
|
||||
modeBarData: str):
|
||||
"""Отрисовка текущего состояния"""
|
||||
|
||||
self.curses_adapter.screen.clear()
|
||||
|
@ -25,22 +25,14 @@ class VimView:
|
|||
self.curses_adapter.SetString(i, 0, '')
|
||||
|
||||
# update mode bar
|
||||
self.EditModeBar(currentLine, len(displayBuffer), file_path)
|
||||
self.SetModeBar(modeBarData)
|
||||
# update cursor
|
||||
self.curses_adapter.SetCursor(currentLine - scrollY, currentCol - scrollX)
|
||||
self.curses_adapter.Refresh()
|
||||
|
||||
def EditModeBar(self, currentLine, totalLines, fileName):
|
||||
def SetModeBar(self, modeBarData: str):
|
||||
"""Print edit mode information panel"""
|
||||
self.curses_adapter.screen.addstr(self.curses_adapter.lines - 1, 0, ' ' * (self.curses_adapter.cols - 1)) # Очистка строки
|
||||
self.curses_adapter.screen.addstr(self.curses_adapter.lines - 1, 0, "FILE: ")
|
||||
self.curses_adapter.screen.addstr(fileName, curses.color_pair(1)) # Имя файла
|
||||
self.curses_adapter.screen.addstr(" | MODE:")
|
||||
self.curses_adapter.screen.addstr(" EDIT", curses.color_pair(1))
|
||||
self.curses_adapter.screen.addstr(" | LINE: ")
|
||||
self.curses_adapter.screen.addstr(str(currentLine + 1), curses.color_pair(1))
|
||||
self.curses_adapter.screen.addstr("/")
|
||||
self.curses_adapter.screen.addstr(str(totalLines), curses.color_pair(1)) # Общее количество строк
|
||||
self.curses_adapter.SetString(self.curses_adapter.lines - 1, 0, modeBarData)
|
||||
|
||||
class CursesAdapter:
|
||||
def __init__(self) -> None:
|
||||
|
@ -53,6 +45,7 @@ class CursesAdapter:
|
|||
self.KEY_ENTER = 10
|
||||
self.KEY_CTRL_S = 19
|
||||
self.KEY_ESCAPE = 27
|
||||
self.KEY_TWO_DOTS = 59
|
||||
|
||||
|
||||
self.screen = curses.initscr()
|
||||
|
|
Loading…
Reference in New Issue