Compare commits

...

2 Commits

4 changed files with 66 additions and 39 deletions

View File

@ -4,6 +4,7 @@ from mvc.views import VimView, CursesAdapter
from mvc.controllers import Controller, ReturnCode from mvc.controllers import Controller, ReturnCode
from mvc.controllers import EditStrategy, CommandStrategy, NormalStrategy from mvc.controllers import EditStrategy, CommandStrategy, NormalStrategy
import tools import tools
from mystring import MyString as MyString
def main(): def main():
adapter = CursesAdapter() adapter = CursesAdapter()
@ -14,8 +15,10 @@ def main():
controller = Controller(strategy) controller = Controller(strategy)
# load file from sys argv # load file from sys argv
if len(sys.argv) > 1: model.LoadFile(sys.argv[1]) if len(sys.argv) > 1: model.LoadFile(MyString(sys.argv[1]))
else: model.LoadFile("File not found but i create it"+str(tools.UnixSec())+".txt") else: model.LoadFile(MyString("File not found but i create it").data()+\
MyString(str(tools.UnixSec())).data()+\
MyString(".txt").data())
view.SetModel(model) # view subscribe model view.SetModel(model) # view subscribe model

View File

@ -2,6 +2,7 @@ import tools
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from mvc.views import CursesAdapter from mvc.views import CursesAdapter
from mvc.models import VimModel, ReturnCode from mvc.models import VimModel, ReturnCode
from mystring import MyString as MyString
def CommonInput(HandleInputFunc): def CommonInput(HandleInputFunc):
"""common actions for all strategies decorator""" """common actions for all strategies decorator"""
@ -51,7 +52,7 @@ class Controller:
class CommandStrategy(BaseStrategy): class CommandStrategy(BaseStrategy):
"""command input mode""" """command input mode"""
def __init__(self, model: VimModel, adapter: CursesAdapter): def __init__(self, model: VimModel, adapter: CursesAdapter):
super().__init__(model, adapter, mode="COMMAND") super().__init__(model, adapter, mode=MyString("COMMAND"))
@CommonInput @CommonInput
def HandleInput(self, symbolCode) -> int: def HandleInput(self, symbolCode) -> int:
@ -70,7 +71,7 @@ class CommandStrategy(BaseStrategy):
class EditStrategy(BaseStrategy): class EditStrategy(BaseStrategy):
"""edit mode""" """edit mode"""
def __init__(self, model: VimModel, adapter: CursesAdapter): def __init__(self, model: VimModel, adapter: CursesAdapter):
super().__init__(model, adapter, mode="EDIT") super().__init__(model, adapter, mode=MyString("EDIT"))
@CommonInput @CommonInput
def HandleInput(self, symbolCode) -> int: def HandleInput(self, symbolCode) -> int:
@ -90,7 +91,7 @@ class EditStrategy(BaseStrategy):
class NormalStrategy(BaseStrategy): class NormalStrategy(BaseStrategy):
"""navigation mode""" """navigation mode"""
def __init__(self, model: VimModel, adapter: CursesAdapter): def __init__(self, model: VimModel, adapter: CursesAdapter):
super().__init__(model, adapter, mode="NORMAL") super().__init__(model, adapter, mode=MyString("NORMAL"))
@CommonInput @CommonInput
def HandleInput(self, symbolCode) -> int: def HandleInput(self, symbolCode) -> int:

View File

@ -1,5 +1,7 @@
import tools import tools
from enum import Enum from enum import Enum
from typing import List
from mystring import MyString as MyString
class ReturnCode(Enum): class ReturnCode(Enum):
GOOD = -101 GOOD = -101
@ -10,24 +12,42 @@ class ReturnCode(Enum):
CONTINUE = -96 CONTINUE = -96
class VimModel: class VimModel:
observers: List
displayLinesCount: int
displayColsCount: int
showLineNumbers: bool
lastSearch: tuple[str, int] # tuple (search str, direction)
displayBuffer: List[List[MyString]]
dump: List[List[MyString]]
currentLine: int
currentCol: int
scrollY: int
scrollX: int
file_path: MyString # filepath
mode: MyString
inputAfterCursor: bool
keyLog: List[tuple[int, int]] # key log for check combinations (tuples (symbol code, tap unix time))
commandBuffer: List[MyString] # buffer for command
exchangeBuffer: List[MyString] # buffer for exchange
def __init__(self, displayLinesCount: int, displayColsCount: int): def __init__(self, displayLinesCount: int, displayColsCount: int):
self.observers = [] self.observers = []
self.displayLinesCount = displayLinesCount self.displayLinesCount = displayLinesCount
self.displayColsCount = displayColsCount self.displayColsCount = displayColsCount
self.showLineNumbers = True self.showLineNumbers = True
self.lastSearch = () self.lastSearch = ()
self.displayBuffer = [] # буфер для хранения всех строк self.displayBuffer = []
self.dump = [] self.dump = []
self.currentLine = 0 # текущий индекс строки self.currentLine = 0
self.currentCol = 0 # текущий индекс колонки self.currentCol = 0
self.scrollY = 0 # вертикальная прокрутка self.scrollY = 0
self.scrollX = 0 # горизонтальная прокрутка self.scrollX = 0
self.file_path = "" # путь к файлу self.file_path = MyString()
self.mode = "" self.mode = MyString()
self.inputAfterCursor = False self.inputAfterCursor = False
self.keyLog = [] # лог нажатий клавиш (кортежи вида (код символа, юникс время нажатия)) self.keyLog = []
self.commandBuffer = [] # буффер для команды self.commandBuffer = []
self.exchangeBuffer = [] # буффер обмена self.exchangeBuffer = []
def attach(self, observer): def attach(self, observer):
self.observers.append(observer) self.observers.append(observer)
@ -57,9 +77,9 @@ class VimModel:
return False return False
def ModeBar(self) -> str: def ModeBar(self) -> str:
modeBar = f"MODE: {self.mode} | FILE: {self.file_path} | LINE: {self.currentLine+1}/{len(self.displayBuffer)}" modeBar = f"MODE: {self.mode.data()} | FILE: {self.file_path.data()} | LINE: {self.currentLine+1}/{len(self.displayBuffer)}"
if self.mode == "COMMAND": if self.mode == MyString("COMMAND"):
modeBar += f" | COMMAND BUFFER: {''.join(self.commandBuffer)}" modeBar += f" | COMMAND BUFFER: {''.join(item.data() for item in self.commandBuffer)}"
return modeBar return modeBar
def Scroll(self) -> None: def Scroll(self) -> None:
@ -81,16 +101,16 @@ class VimModel:
self.scrollX = self.currentCol - visible_text_width + 1 self.scrollX = self.currentCol - visible_text_width + 1
def InsertCommandSymbol(self, symbolCode: int) -> None: def InsertCommandSymbol(self, symbolCode: int) -> None:
self.commandBuffer.append(chr(symbolCode)) self.commandBuffer.append(MyString(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, MyString(chr(symbolCode)))
self.currentCol += 1 self.currentCol += 1
def InsertSymbolAfterCursor(self, symbolCode: int) -> None: def InsertSymbolAfterCursor(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, MyString(chr(symbolCode)))
def ToLineStart(self) -> None: def ToLineStart(self) -> None:
self.currentCol = 0 self.currentCol = 0
@ -99,7 +119,7 @@ class VimModel:
self.currentCol = len(self.displayBuffer[self.currentLine]) self.currentCol = len(self.displayBuffer[self.currentLine])
def ToWordEnd(self) -> None: def ToWordEnd(self) -> None:
line = ''.join(self.displayBuffer[self.currentLine]) line = ''.join(item.data() for item in self.displayBuffer[self.currentLine])
# Находим ближайший непробельный символ # Находим ближайший непробельный символ
non_space_index = next((i for i, char in enumerate(line[self.currentCol:]) if char != ' '), None) non_space_index = next((i for i, char in enumerate(line[self.currentCol:]) if char != ' '), None)
if non_space_index is not None: if non_space_index is not None:
@ -108,7 +128,7 @@ class VimModel:
self.currentCol = right_space_index if right_space_index != -1 else len(line) self.currentCol = right_space_index if right_space_index != -1 else len(line)
def ToWordStart(self) -> None: def ToWordStart(self) -> None:
line = ''.join(self.displayBuffer[self.currentLine]) line = ''.join(item.data() for item in self.displayBuffer[self.currentLine])
non_space_index = next((i for i in range(self.currentCol - 1, -1, -1) if line[i] != ' '), None) non_space_index = next((i for i in range(self.currentCol - 1, -1, -1) if line[i] != ' '), None)
if non_space_index is not None: if non_space_index is not None:
left_space_index = line.rfind(' ', 0, non_space_index) left_space_index = line.rfind(' ', 0, non_space_index)
@ -143,7 +163,7 @@ class VimModel:
def DeleteWord(self) -> None: def DeleteWord(self) -> None:
start_index, end_index = self.WordUnderCursor() start_index, end_index = self.WordUnderCursor()
line = self.displayBuffer[self.currentLine] line = self.displayBuffer[self.currentLine]
if end_index < len(line) and line[end_index] == ' ': if end_index < len(line) and line[end_index] == MyString(' '):
end_index += 1 end_index += 1
self.displayBuffer[self.currentLine] = line[:start_index] + line[end_index:] self.displayBuffer[self.currentLine] = line[:start_index] + line[end_index:]
self.currentCol = start_index self.currentCol = start_index
@ -172,7 +192,7 @@ class VimModel:
def EnterCommand(self): def EnterCommand(self):
"""Обработка введенной команды""" """Обработка введенной команды"""
cmd = ''.join(self.commandBuffer) cmd = ''.join(item.data() for item in self.commandBuffer)
self.commandBuffer.clear() self.commandBuffer.clear()
match cmd: match cmd:
case "q": # Выход из программы case "q": # Выход из программы
@ -225,18 +245,20 @@ class VimModel:
self.lastSearch = (self.lastSearch[0], (self.lastSearch[1]+1)%2) self.lastSearch = (self.lastSearch[0], (self.lastSearch[1]+1)%2)
case "e!": case "e!":
self.displayBuffer = [sublist.copy() for sublist in self.dump] self.displayBuffer = [sublist.copy() for sublist in self.dump]
self.currentCol = 0
self.currentLine = 0
case "set num": case "set num":
self.showLineNumbers = not self.showLineNumbers self.showLineNumbers = not self.showLineNumbers
case _: case _:
# Открывает файл filename # Открывает файл filename
if len(cmd) > 2 and cmd[:2] == 'o ': if len(cmd) > 2 and cmd[:2] == 'o ':
filename = cmd[2:] filename = cmd[2:]
self.LoadFile(filename) self.LoadFile(MyString(filename))
return ReturnCode.SET_BASIC_MODE return ReturnCode.SET_BASIC_MODE
# Запись в файл filename # Запись в файл filename
elif len(cmd) > 2 and cmd[:2] == 'w ': elif len(cmd) > 2 and cmd[:2] == 'w ':
filename = cmd[2:] filename = cmd[2:]
self.WriteFile(filename) self.WriteFile(MyString(filename))
# Заменяет символ на указанный # Заменяет символ на указанный
elif len(cmd) == 3 and cmd[:2] == 'r ': elif len(cmd) == 3 and cmd[:2] == 'r ':
self.displayBuffer[self.currentLine][self.currentCol] = cmd[2] self.displayBuffer[self.currentLine][self.currentCol] = cmd[2]
@ -268,7 +290,7 @@ class VimModel:
def WordUnderCursor(self)-> tuple[int, int]: def WordUnderCursor(self)-> tuple[int, int]:
"""Возвращает индекс начала и индекс конца слова под курсором""" """Возвращает индекс начала и индекс конца слова под курсором"""
line = ''.join(self.displayBuffer[self.currentLine]) line = ''.join(item.data() for item in self.displayBuffer[self.currentLine])
start_index = line.rfind(' ', 0, self.currentCol) start_index = line.rfind(' ', 0, self.currentCol)
start_index = 0 if start_index == -1 else start_index + 1 start_index = 0 if start_index == -1 else start_index + 1
end_index = line.find(' ', self.currentCol) end_index = line.find(' ', self.currentCol)
@ -335,13 +357,13 @@ class VimModel:
"""Загрузка файла для редактирования""" """Загрузка файла для редактирования"""
self.Reset() self.Reset()
self.file_path = file_path self.file_path = file_path
self.mode = "NORMAL" self.mode = MyString("NORMAL")
try: try:
with open(file_path, "r") as file: with open(file_path.data(), "r") as file:
self.displayBuffer = [list(line.rstrip('\n')) for line in file.readlines()] self.displayBuffer = [[MyString(char) for char in line.rstrip('\n')] for line in file.readlines()]
self.Dump() self.Dump()
except FileNotFoundError: except FileNotFoundError:
print(f"File '{file_path}' not found. Starting with empty buffer.") print(f"File '{file_path.data()}' not found. Starting with empty buffer.")
self.displayBuffer = [] self.displayBuffer = []
self.displayBuffer.append([]) self.displayBuffer.append([])
@ -352,11 +374,11 @@ class VimModel:
def WriteFile(self, file_path) -> None: def WriteFile(self, file_path) -> None:
"""Запись в файл по указанному пути""" """Запись в файл по указанному пути"""
try: try:
with open(file_path, "w") as file: with open(file_path.data(), "w") as file:
for line in self.displayBuffer: for line in self.displayBuffer:
file.write(''.join(line) + '\n') file.write(''.join(item.data() for item in line) + '\n')
self.Dump() self.Dump()
print(f"In file '{file_path}' written successfully.") print(f"In file '{file_path.data()}' written successfully.")
except Exception as e: except Exception as e:
print(f"Error writing file: {str(e)}") print(f"Error writing file: {str(e)}")
@ -369,8 +391,8 @@ class VimModel:
self.currentCol = 0 # текущий индекс колонки self.currentCol = 0 # текущий индекс колонки
self.scrollY = 0 # вертикальная прокрутка self.scrollY = 0 # вертикальная прокрутка
self.scrollX = 0 # горизонтальная прокрутка self.scrollX = 0 # горизонтальная прокрутка
self.file_path = "" # путь к файлу self.file_path = MyString() # путь к файлу
self.mode = "" self.mode = MyString()
self.inputAfterCursor = False self.inputAfterCursor = False
self.keyLog = [] # лог нажатий клавиш (кортежи вида (код символа, юникс время нажатия)) self.keyLog = [] # лог нажатий клавиш (кортежи вида (код символа, юникс время нажатия))
self.commandBuffer = [] # буффер для команды self.commandBuffer = [] # буффер для команды

View File

@ -1,6 +1,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import curses import curses
import mvc.models import mvc.models
from mystring import MyString as MyString
class CursesAdapter: class CursesAdapter:
def __init__(self) -> None: def __init__(self) -> None:
@ -93,7 +94,7 @@ class VimView(Observer):
) )
def Render(self, def Render(self,
displayBuffer: list[list[str]], displayBuffer: list[list[MyString]],
currentLine: int, currentCol: int, currentLine: int, currentCol: int,
scrollX: int, scrollY: int, scrollX: int, scrollY: int,
modeBarData: str, modeBarData: str,
@ -108,7 +109,7 @@ class VimView(Observer):
# Отображение видимой части текста # Отображение видимой части текста
for i in range(self.curses_adapter.lines - 1): for i in range(self.curses_adapter.lines - 1):
if i + scrollY < len(displayBuffer): if i + scrollY < len(displayBuffer):
line = ''.join(displayBuffer[i + scrollY]) line = ''.join(item.data() for item in displayBuffer[i + scrollY])
# Если нужно отображать номера строк, добавляем их перед текстом # Если нужно отображать номера строк, добавляем их перед текстом
if show_line_numbers: if show_line_numbers:
line_number = f"{i + scrollY + 1:6} " # 6 символов на номер строки line_number = f"{i + scrollY + 1:6} " # 6 символов на номер строки