сделал normal mode, начал писать режим ввода команд
parent
d92d7adc9b
commit
b37156ef79
19
main.py
19
main.py
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
17
mvc/views.py
17
mvc/views.py
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue