diff --git a/mvc/models.py b/mvc/models.py index 2cc08cd..47571d4 100644 --- a/mvc/models.py +++ b/mvc/models.py @@ -1,5 +1,8 @@ import tools from enum import Enum +from typing import List + +from mystring import MyString as MyString class ReturnCode(Enum): GOOD = -101 @@ -10,24 +13,42 @@ class ReturnCode(Enum): CONTINUE = -96 class VimModel: + observers: List + displayLinesCount: int + displayColsCount: int + showLineNumbers: bool + lastSearch: tuple[str, int] # tuple (search str, direction) + displayBuffer: List[MyString] + dump: List[MyString] + currentLine: int + currentCol: int + scrollY: int + scrollX: int + file_path: str # filepath + mode: str + inputAfterCursor: bool + keyLog: List[tuple[int, int]] # key log for check combinations (tuples (symbol code, tap unix time)) + commandBuffer: str # buffer for command + exchangeBuffer: str # buffer for exchange + def __init__(self, displayLinesCount: int, displayColsCount: int): self.observers = [] self.displayLinesCount = displayLinesCount self.displayColsCount = displayColsCount self.showLineNumbers = True self.lastSearch = () - self.displayBuffer = [] # буфер для хранения всех строк + self.displayBuffer = [] self.dump = [] - self.currentLine = 0 # текущий индекс строки - self.currentCol = 0 # текущий индекс колонки - self.scrollY = 0 # вертикальная прокрутка - self.scrollX = 0 # горизонтальная прокрутка - self.file_path = "" # путь к файлу + self.currentLine = 0 + self.currentCol = 0 + self.scrollY = 0 + self.scrollX = 0 + self.file_path = "" self.mode = "" self.inputAfterCursor = False - self.keyLog = [] # лог нажатий клавиш (кортежи вида (код символа, юникс время нажатия)) - self.commandBuffer = [] # буффер для команды - self.exchangeBuffer = [] # буффер обмена + self.keyLog = [] + self.commandBuffer = "" + self.exchangeBuffer = "" def attach(self, observer): self.observers.append(observer) @@ -59,7 +80,7 @@ class VimModel: 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)}" + modeBar += f" | COMMAND BUFFER: {self.commandBuffer}" return modeBar def Scroll(self) -> None: @@ -81,7 +102,7 @@ class VimModel: self.scrollX = self.currentCol - visible_text_width + 1 def InsertCommandSymbol(self, symbolCode: int) -> None: - self.commandBuffer.append(chr(symbolCode)) + self.commandBuffer += chr(symbolCode) def InsertSymbol(self, symbolCode: int) -> None: if self.currentCol <= len(self.displayBuffer[self.currentLine]): # проверяем, не превышает ли индекс колонки длину строки @@ -99,7 +120,7 @@ class VimModel: self.currentCol = len(self.displayBuffer[self.currentLine]) def ToWordEnd(self) -> None: - line = ''.join(self.displayBuffer[self.currentLine]) + line = self.displayBuffer[self.currentLine].data() # Находим ближайший непробельный символ non_space_index = next((i for i, char in enumerate(line[self.currentCol:]) if char != ' '), None) if non_space_index is not None: @@ -108,7 +129,7 @@ class VimModel: self.currentCol = right_space_index if right_space_index != -1 else len(line) def ToWordStart(self) -> None: - line = ''.join(self.displayBuffer[self.currentLine]) + line = self.displayBuffer[self.currentLine].data() 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: left_space_index = line.rfind(' ', 0, non_space_index) @@ -124,45 +145,43 @@ class VimModel: def DeleteNext(self) -> None: if self.currentCol + 1 < len(self.displayBuffer[self.currentLine]): - del self.displayBuffer[self.currentLine][self.currentCol + 1] + self.displayBuffer[self.currentLine].erase(self.currentCol + 1, 1) def PageUp(self) -> None: self.currentCol = 0 if self.currentLine > self.displayLinesCount: self.currentLine -= self.displayLinesCount - 2 - else: - self.currentLine = 0 + else: self.currentLine = 0 def PageDown(self) -> None: self.currentCol = 0 if self.currentLine + self.displayLinesCount < len(self.displayBuffer): self.currentLine += self.displayLinesCount - 2 - else: - self.currentLine = len(self.displayBuffer) - 1 + else: self.currentLine = len(self.displayBuffer) - 1 def DeleteWord(self) -> None: start_index, end_index = self.WordUnderCursor() line = self.displayBuffer[self.currentLine] if end_index < len(line) and line[end_index] == ' ': end_index += 1 - self.displayBuffer[self.currentLine] = line[:start_index] + line[end_index:] + self.displayBuffer[self.currentLine].erase(start_index, end_index-start_index) self.currentCol = start_index def CopyWord(self) -> None: start_index, end_index = self.WordUnderCursor() - line = self.displayBuffer[self.currentLine] + line = self.displayBuffer[self.currentLine].data() self.exchangeBuffer = line[start_index:end_index+1] def Paste(self) -> None: - self.displayBuffer[self.currentLine][self.currentCol+1:self.currentCol+1] = self.exchangeBuffer + self.displayBuffer[self.currentLine].insert(self.currentCol, self.exchangeBuffer) def CutLine(self) -> None: - self.exchangeBuffer = self.displayBuffer[self.currentLine] - self.displayBuffer[self.currentLine] = [] + self.exchangeBuffer = self.displayBuffer[self.currentLine].data() + self.displayBuffer[self.currentLine] = MyString() self.currentCol = 0 def CopyLine(self) -> None: - self.exchangeBuffer = self.displayBuffer[self.currentLine] + self.exchangeBuffer = self.displayBuffer[self.currentLine].data() def MoveToLine(self, numberLine: int) -> None: numberLine -= 1 @@ -173,7 +192,7 @@ class VimModel: def EnterCommand(self): """Обработка введенной команды""" cmd = ''.join(self.commandBuffer) - self.commandBuffer.clear() + self.commandBuffer = "" match cmd: case "q": # Выход из программы if self.displayBuffer == self.dump: @@ -195,7 +214,7 @@ class VimModel: return ReturnCode.SET_EDIT_MODE case "S": # Удаление строки на которой курсор и вход в режим редактирования self.currentCol = 0 - self.displayBuffer[self.currentLine] = [] + self.displayBuffer[self.currentLine] = MyString() return ReturnCode.SET_EDIT_MODE case "o": self.InputAfterCursor() @@ -205,8 +224,8 @@ class VimModel: return ReturnCode.SET_BASIC_MODE case "n": if self.lastSearch != (): - index = tools.findSublistIndex(self.displayBuffer, - list(self.lastSearch[0]), + index = tools.findStringInMyStringList(self.displayBuffer, + self.lastSearch[0], self.currentLine, direction=self.lastSearch[1]) if index != -1: @@ -215,8 +234,8 @@ class VimModel: self.lastSearch = (self.lastSearch[0], self.lastSearch[1]) case "N": if self.lastSearch != (): - index = tools.findSublistIndex(self.displayBuffer, - list(self.lastSearch[0]), + index = tools.findStringInMyStringList(self.displayBuffer, + self.lastSearch[0], self.currentLine, direction=(self.lastSearch[1]+1)%2) if index != -1: @@ -224,7 +243,9 @@ class VimModel: self.currentCol = 0 self.lastSearch = (self.lastSearch[0], (self.lastSearch[1]+1)%2) case "e!": - self.displayBuffer = [sublist.copy() for sublist in self.dump] + self.displayBuffer = [sublist for sublist in self.dump] + self.currentCol = 0 + self.currentLine = 0 case "set num": self.showLineNumbers = not self.showLineNumbers case _: @@ -239,14 +260,14 @@ class VimModel: self.WriteFile(filename) # Заменяет символ на указанный elif len(cmd) == 3 and cmd[:2] == 'r ': - self.displayBuffer[self.currentLine][self.currentCol] = cmd[2] + self.displayBuffer[self.currentLine].replace(self.currentCol, 1, cmd[2]) # Переход на строку по введенному номеру elif cmd.isdigit(): self.MoveToLine(int(cmd)) # Поиск строки от курсора до конца файла elif len(cmd) > 1 and cmd[0] == '/': - index = tools.findSublistIndex(self.displayBuffer, - list(cmd[1:]), + index = tools.findStringInMyStringList(self.displayBuffer, + cmd[1:], self.currentLine, direction=1) if index != -1: @@ -255,8 +276,8 @@ class VimModel: self.lastSearch = (cmd[1:], 1) # Поиск строки от курсора до начала файла elif len(cmd) > 1 and cmd[0] == '?': - index = tools.findSublistIndex(self.displayBuffer, - list(cmd[1:]), + index = tools.findStringInMyStringList(self.displayBuffer, + cmd[1:], self.currentLine, direction=0) if index != -1: @@ -268,7 +289,7 @@ class VimModel: def WordUnderCursor(self)-> tuple[int, int]: """Возвращает индекс начала и индекс конца слова под курсором""" - line = ''.join(self.displayBuffer[self.currentLine]) + line = self.displayBuffer[self.currentLine].data() start_index = line.rfind(' ', 0, self.currentCol) start_index = 0 if start_index == -1 else start_index + 1 end_index = line.find(' ', self.currentCol) @@ -277,24 +298,24 @@ class VimModel: def Enter(self) -> None: # Разделяем текущую строку на две части - new_line = self.displayBuffer[self.currentLine][self.currentCol:] - self.displayBuffer[self.currentLine] = self.displayBuffer[self.currentLine][:self.currentCol] + new_line = self.displayBuffer[self.currentLine].substr(self.currentCol) + self.displayBuffer[self.currentLine] = self.displayBuffer[self.currentLine].substr(0, self.currentCol) self.currentLine += 1 # Переходим на следующую строку self.displayBuffer.insert(self.currentLine, new_line) # Вставляем новую строку self.currentCol = 0 # Сбрасываем индекс колонки def BackspaceCommand(self) -> None: if len(self.commandBuffer) > 0: - self.commandBuffer.pop() + self.commandBuffer = self.commandBuffer[:-1] def Backspace(self) -> None: if self.currentCol > 0: # Если символ существует в текущей строке self.currentCol -= 1 - del self.displayBuffer[self.currentLine][self.currentCol] # Удаляем символ + self.displayBuffer[self.currentLine].erase(self.currentCol, 1) # Удаляем символ elif self.currentLine > 0: # Если текущая строка не первая # Объединяем текущую строку с предыдущей prev_line_length = len(self.displayBuffer[self.currentLine - 1]) - self.displayBuffer[self.currentLine - 1].extend(self.displayBuffer[self.currentLine]) + self.displayBuffer[self.currentLine - 1].append(self.displayBuffer[self.currentLine].data()) del self.displayBuffer[self.currentLine] self.currentLine -= 1 self.currentCol = prev_line_length # Переходим в конец предыдущей строки @@ -325,10 +346,10 @@ class VimModel: def Dump(self) -> None: """Обновляет дамп данных""" - self.dump = [sublist.copy() for sublist in self.displayBuffer] + self.dump = [line.substr(0) for line in self.displayBuffer] def RecoverLine(self) -> None: - self.displayBuffer[self.currentLine] = self.dump[self.currentLine].copy() + self.displayBuffer[self.currentLine] = self.dump[self.currentLine] self.currentCol = 0 def LoadFile(self, file_path) -> None: @@ -338,12 +359,12 @@ class VimModel: self.mode = "NORMAL" try: with open(file_path, "r") as file: - self.displayBuffer = [list(line.rstrip('\n')) for line in file.readlines()] + self.displayBuffer = [MyString(line.rstrip()) for line in file.readlines()] self.Dump() except FileNotFoundError: print(f"File '{file_path}' not found. Starting with empty buffer.") self.displayBuffer = [] - self.displayBuffer.append([]) + self.displayBuffer.append(MyString()) def SaveFile(self) -> None: """Сохранение текущего файла""" @@ -354,7 +375,7 @@ class VimModel: try: with open(file_path, "w") as file: for line in self.displayBuffer: - file.write(''.join(line) + '\n') + file.write(line.data() + '\n') self.Dump() print(f"In file '{file_path}' written successfully.") except Exception as e: @@ -373,5 +394,5 @@ class VimModel: self.mode = "" self.inputAfterCursor = False self.keyLog = [] # лог нажатий клавиш (кортежи вида (код символа, юникс время нажатия)) - self.commandBuffer = [] # буффер для команды - self.exchangeBuffer = [] # буффер обмена \ No newline at end of file + self.commandBuffer = "" # буффер для команды + self.exchangeBuffer = "" # буффер обмена \ No newline at end of file diff --git a/mvc/views.py b/mvc/views.py index 74e1250..7d0eddb 100644 --- a/mvc/views.py +++ b/mvc/views.py @@ -1,6 +1,8 @@ from abc import ABC, abstractmethod import curses import mvc.models +from mystring import MyString as MyString + class CursesAdapter: def __init__(self) -> None: @@ -93,7 +95,7 @@ class VimView(Observer): ) def Render(self, - displayBuffer: list[list[str]], + displayBuffer: list[MyString], currentLine: int, currentCol: int, scrollX: int, scrollY: int, modeBarData: str, @@ -108,7 +110,7 @@ class VimView(Observer): # Отображение видимой части текста for i in range(self.curses_adapter.lines - 1): if i + scrollY < len(displayBuffer): - line = ''.join(displayBuffer[i + scrollY]) + line = displayBuffer[i + scrollY].data() # Если нужно отображать номера строк, добавляем их перед текстом if show_line_numbers: line_number = f"{i + scrollY + 1:6} " # 6 символов на номер строки diff --git a/tools.py b/tools.py index 495bedb..147599f 100644 --- a/tools.py +++ b/tools.py @@ -1,15 +1,15 @@ import time, re -def findSublistIndex(main_list, sublist, start_index=0, direction=1): +def findStringInMyStringList(main_list, sublist, start_index=0, direction=1): start_index = max(0, min(start_index, len(main_list) - 1)) if direction == 1: # forward for index in range(start_index+1, len(main_list)): - if main_list[index] == sublist: + if main_list[index].data() == sublist: return index else: # backward for index in range(start_index-1, -1, -1): - if main_list[index] == sublist: + if main_list[index].data() == sublist: return index return -1