VIM-like-text-editor/mvc/models.py

192 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

from enum import Enum
class ReturnCode(Enum):
GOOD = -101
EXIT_CODE = -100
SET_BASIC_MODE = -99
SET_COMMAND_MODE = -98
SET_EDIT_MODE = -97
CONTINUE = -96
class VimModel:
def __init__(self):
self.displayBuffer = [] # буфер для хранения всех строк
self.currentLine = 0 # текущий индекс строки
self.currentCol = 0 # текущий индекс колонки
self.scrollY = 0 # вертикальная прокрутка
self.scrollX = 0 # горизонтальная прокрутка
self.file_path = "" # путь к файлу
self.mode = ""
self.commandBuffer = [] # буффер для команды
self.exchangeBuffer = [] # буффер обмена
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:
self.scrollY = self.currentLine
elif self.currentLine >= self.scrollY + displayLines - 1:
self.scrollY = self.currentLine - displayLines + 2
if self.currentCol < self.scrollX:
self.scrollX = self.currentCol
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])
case "^" | "0": # Перемещение в начало строки
self.currentCol = 0
case "q": # Выход из программы
return ReturnCode.EXIT_CODE
case "i": # Вход в режим редактирования
return ReturnCode.SET_EDIT_MODE
case "I": # Переходит в начало строки и начинает ввод текста
self.currentCol = 0
return ReturnCode.SET_EDIT_MODE
case "A": # Переходит в конец строки и начинает ввод текста
self.currentCol = len(self.displayBuffer[self.currentLine])
return ReturnCode.SET_EDIT_MODE
case "S": # Удаление строки на которой курсор и вход в режим редактирования
self.currentCol = 0
self.displayBuffer[self.currentLine] = []
return ReturnCode.SET_EDIT_MODE
case "RIGHT": # Перемещение курсора на 1 позицию вправо
self.currentCol += 1
case "LEFT": # Перемещение курсора на 1 позицию влево
if self.currentCol > 0:
self.currentCol -= 1
case "UP": # Перемещение курсора на 1 пизицию вверх
if self.currentLine > 0:
self.currentLine -= 1
if self.currentCol > len(self.displayBuffer[self.currentLine]):
self.currentCol = len(self.displayBuffer[self.currentLine])
case "DOWN": # Перемещение курсора на 1 пизицию вниз
if self.currentLine < len(self.displayBuffer) - 1:
self.currentLine += 1
if self.currentCol > len(self.displayBuffer[self.currentLine]):
self.currentCol = len(self.displayBuffer[self.currentLine])
case "w": # Перемещение курсора в конец слова справа от курсора
line = ''.join(self.displayBuffer[self.currentLine])
# Находим ближайший непробельный символ
non_space_index = next((i for i, char in enumerate(line[self.currentCol:]) if char != ' '), None)
if non_space_index is not None:
non_space_index += self.currentCol
right_space_index = line.find(' ', non_space_index)
if right_space_index != -1:
self.currentCol = right_space_index
else:
self.currentCol = len(self.displayBuffer[self.currentLine])
case "b": # Перемещает курсор в конец в начало слова слева от курсора
line = ''.join(self.displayBuffer[self.currentLine])
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)
if left_space_index != -1:
self.currentCol = left_space_index + 1
else:
self.currentCol = 0
case "gg": # Переход в начало файла
self.currentLine = 0
self.currentCol = 0
case "G": # Переход в конец файла
self.currentLine = len(self.displayBuffer) - 1
self.currentCol = len(self.displayBuffer[self.currentLine])
case "x": # Удаляет символ после курсора
if self.currentCol + 1 < len(self.displayBuffer[self.currentLine]):
del self.displayBuffer[self.currentLine][self.currentCol + 1]
case "yy": # копирует текущую строку
self.exchangeBuffer = self.displayBuffer[self.currentLine].copy()
case "dd": # вырезает текущую строку
self.exchangeBuffer = self.displayBuffer[self.currentLine].copy()
del self.displayBuffer[self.currentLine]
case "p": # вставить после курсора
self.displayBuffer[self.currentLine][self.currentCol+1:self.currentCol+1] = self.exchangeBuffer
return ReturnCode.GOOD
def Enter(self) -> None:
# Разделяем текущую строку на две части
new_line = self.displayBuffer[self.currentLine][self.currentCol:]
self.displayBuffer[self.currentLine] = self.displayBuffer[self.currentLine][: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()
def Backspace(self) -> None:
if self.currentCol > 0: # Если символ существует в текущей строке
self.currentCol -= 1
del self.displayBuffer[self.currentLine][self.currentCol] # Удаляем символ
elif self.currentLine > 0: # Если текущая строка не первая
# Объединяем текущую строку с предыдущей
prev_line_length = len(self.displayBuffer[self.currentLine - 1])
self.displayBuffer[self.currentLine - 1].extend(self.displayBuffer[self.currentLine])
del self.displayBuffer[self.currentLine]
self.currentLine -= 1
self.currentCol = prev_line_length # Переходим в конец предыдущей строки
def MoveLeft(self) -> None:
if self.currentCol > 0:
self.currentCol -= 1
elif self.currentLine > 0:
self.currentLine -= 1
self.currentCol = len(self.displayBuffer[self.currentLine])
def MoveRight(self) -> None:
if self.currentCol < len(self.displayBuffer[self.currentLine]):
self.currentCol += 1
elif self.currentLine < len(self.displayBuffer) - 1:
self.currentLine += 1
self.currentCol = 0
def MoveUp(self) -> None:
if self.currentLine > 0:
self.currentLine -= 1
self.currentCol = min(self.currentCol, len(self.displayBuffer[self.currentLine]))
def MoveDown(self) -> None:
if self.currentLine < len(self.displayBuffer) - 1:
self.currentLine += 1
self.currentCol = min(self.currentCol, len(self.displayBuffer[self.currentLine]))
def LoadFile(self, file_path) -> None:
"""Загрузка файла для редактирования"""
self.file_path = file_path
try:
with open(file_path, "r") as file:
self.displayBuffer = [list(line.rstrip('\n')) for line in file.readlines()]
except FileNotFoundError:
print(f"File {file_path} not found. Starting with empty buffer.")
self.displayBuffer = []
def SaveFile(self) -> None:
"""Сохранение файла"""
try:
with open(self.file_path, "w") as file:
for line in self.displayBuffer:
file.write(''.join(line) + '\n')
print(f"File {self.file_path} saved successfully.")
except Exception as e:
print(f"Error saving file: {str(e)}")