сделал normal mode, начал писать режим ввода команд

master
serr 2025-02-04 18:36:12 +03:00
parent d92d7adc9b
commit b37156ef79
4 changed files with 118 additions and 35 deletions

19
main.py
View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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()