сделал 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.models import VimModel
from mvc.views import VimView 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(): def main():
model = VimModel() model, view = VimModel(), VimView()
view = VimView()
# начальный режим - редактирование # начальный режим - редактирование
strategy = EditStrategy(model, view.curses_adapter) strategy = NormalStrategy(model, view.curses_adapter)
controller = Controller(strategy) controller = Controller(strategy)
# Загрузка файла для редактирования # Загрузка файла для редактирования
@ -18,10 +18,17 @@ def main():
view.Render(model.displayBuffer, view.Render(model.displayBuffer,
model.currentLine, model.currentCol, model.currentLine, model.currentCol,
model.scrollX, model.scrollY, model.scrollX, model.scrollY,
model.file_path) model.ModeBar())
symbolCode = view.curses_adapter.GetChar() 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.curses_adapter.Cleanup()

View File

@ -1,33 +1,67 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from mvc.views import CursesAdapter
from mvc.models import VimModel
class IStrategy(ABC): from mvc.views import CursesAdapter
def __init__(self, model: VimModel, adapter: 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.model = model
self.adapter = adapter 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 @abstractmethod
def HandleInput(self, symbolCode: int) -> bool: def HandleInput(self, symbolCode: int) -> int:
pass pass
class Controller: class Controller:
def __init__(self, strategy: IStrategy): def __init__(self, strategy: Strategy):
self.strategy = strategy self.strategy = strategy
def SetController(self, strategy: IStrategy): def SetStrategy(self, strategy: Strategy):
self.strategy = strategy self.strategy = strategy
def HandleInput(self, symbolCode: int): def HandleInput(self, symbolCode: int):
return self.strategy.HandleInput(symbolCode) return self.strategy.HandleInput(symbolCode)
class EditStrategy(IStrategy): class CommandStrategy(Strategy):
"""Режим ввода коман"""
def __init__(self, model: VimModel, adapter: CursesAdapter): def __init__(self, model: VimModel, adapter: CursesAdapter):
self.model = model super().__init__(model, adapter, mode="COMMAND")
self.adapter = adapter
def HandleInput(self, symbolCode) -> int:
def HandleInput(self, symbolCode): """Обработка ввода пользователя"""
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: match symbolCode:
case self.adapter.KEY_ESCAPE: return False
case self.adapter.KEY_LEFT: self.model.MoveLeft() case self.adapter.KEY_LEFT: self.model.MoveLeft()
case self.adapter.KEY_RIGHT: self.model.MoveRight() case self.adapter.KEY_RIGHT: self.model.MoveRight()
case self.adapter.KEY_UP: self.model.MoveUp() 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_BACKSPACE_2: self.model.Backspace()
case self.adapter.KEY_CTRL_S: self.model.SaveFile() case self.adapter.KEY_CTRL_S: self.model.SaveFile()
case _: case _:
if symbolCode > 127 or symbolCode < 0: if not isAscii(symbolCode): return ReturnCode.GOOD
print("Only 1-byte symbols!")
return False
self.model.InsertSymbol(symbolCode) self.model.InsertSymbol(symbolCode)
self.model.Scroll(self.adapter.lines, self.adapter.cols) 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: class VimModel:
def __init__(self): def __init__(self):
self.displayBuffer = [] # буфер для хранения всех строк self.displayBuffer = [] # буфер для хранения всех строк
@ -6,6 +14,14 @@ class VimModel:
self.scrollY = 0 # вертикальная прокрутка self.scrollY = 0 # вертикальная прокрутка
self.scrollX = 0 # горизонтальная прокрутка self.scrollX = 0 # горизонтальная прокрутка
self.file_path = "" # путь к файлу 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: def Scroll(self, displayLines: int, displayCols: int) -> None:
if self.currentLine < self.scrollY: if self.currentLine < self.scrollY:
@ -18,11 +34,24 @@ class VimModel:
elif self.currentCol >= self.scrollX + displayCols: elif self.currentCol >= self.scrollX + displayCols:
self.scrollX = self.currentCol - displayCols + 1 self.scrollX = self.currentCol - displayCols + 1
def InsertCommandSymbol(self, symbolCode: int) -> None:
self.commandBuffer.append(chr(symbolCode))
def InsertSymbol(self, symbolCode: int) -> None: def InsertSymbol(self, symbolCode: int) -> None:
if self.currentCol <= len(self.displayBuffer[self.currentLine]): # проверяем, не превышает ли индекс колонки длину строки if self.currentCol <= len(self.displayBuffer[self.currentLine]): # проверяем, не превышает ли индекс колонки длину строки
self.displayBuffer[self.currentLine].insert(self.currentCol, chr(symbolCode)) self.displayBuffer[self.currentLine].insert(self.currentCol, chr(symbolCode))
self.currentCol += 1 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: def Enter(self) -> None:
# Разделяем текущую строку на две части # Разделяем текущую строку на две части
new_line = self.displayBuffer[self.currentLine][self.currentCol:] new_line = self.displayBuffer[self.currentLine][self.currentCol:]
@ -31,6 +60,10 @@ class VimModel:
self.displayBuffer.insert(self.currentLine, new_line) # Вставляем новую строку self.displayBuffer.insert(self.currentLine, new_line) # Вставляем новую строку
self.currentCol = 0 # Сбрасываем индекс колонки self.currentCol = 0 # Сбрасываем индекс колонки
def BackspaceCommand(self) -> None:
if len(self.commandBuffer) > 0:
self.commandBuffer.pop()
def Backspace(self) -> None: def Backspace(self) -> None:
if self.currentCol > 0: # Если символ существует в текущей строке if self.currentCol > 0: # Если символ существует в текущей строке
self.currentCol -= 1 self.currentCol -= 1

View File

@ -8,7 +8,7 @@ class VimView:
displayBuffer: list[list[str]], displayBuffer: list[list[str]],
currentLine: int, currentCol: int, currentLine: int, currentCol: int,
scrollX: int, scrollY: int, scrollX: int, scrollY: int,
file_path: str): modeBarData: str):
"""Отрисовка текущего состояния""" """Отрисовка текущего состояния"""
self.curses_adapter.screen.clear() self.curses_adapter.screen.clear()
@ -25,22 +25,14 @@ class VimView:
self.curses_adapter.SetString(i, 0, '') self.curses_adapter.SetString(i, 0, '')
# update mode bar # update mode bar
self.EditModeBar(currentLine, len(displayBuffer), file_path) self.SetModeBar(modeBarData)
# update cursor # update cursor
self.curses_adapter.SetCursor(currentLine - scrollY, currentCol - scrollX) self.curses_adapter.SetCursor(currentLine - scrollY, currentCol - scrollX)
self.curses_adapter.Refresh() self.curses_adapter.Refresh()
def EditModeBar(self, currentLine, totalLines, fileName): def SetModeBar(self, modeBarData: str):
"""Print edit mode information panel""" """Print edit mode information panel"""
self.curses_adapter.screen.addstr(self.curses_adapter.lines - 1, 0, ' ' * (self.curses_adapter.cols - 1)) # Очистка строки self.curses_adapter.SetString(self.curses_adapter.lines - 1, 0, modeBarData)
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)) # Общее количество строк
class CursesAdapter: class CursesAdapter:
def __init__(self) -> None: def __init__(self) -> None:
@ -53,6 +45,7 @@ class CursesAdapter:
self.KEY_ENTER = 10 self.KEY_ENTER = 10
self.KEY_CTRL_S = 19 self.KEY_CTRL_S = 19
self.KEY_ESCAPE = 27 self.KEY_ESCAPE = 27
self.KEY_TWO_DOTS = 59
self.screen = curses.initscr() self.screen = curses.initscr()