2025-02-04 18:36:12 +03:00
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
|
|
class ReturnCode(Enum):
|
|
|
|
|
GOOD = -101
|
|
|
|
|
EXIT_CODE = -100
|
|
|
|
|
SET_BASIC_MODE = -99
|
|
|
|
|
SET_COMMAND_MODE = -98
|
2025-02-04 18:46:16 +03:00
|
|
|
|
SET_EDIT_MODE = -97
|
2025-02-04 19:06:51 +03:00
|
|
|
|
CONTINUE = -96
|
2025-02-04 18:36:12 +03:00
|
|
|
|
|
2025-02-04 13:16:23 +03:00
|
|
|
|
class VimModel:
|
2025-02-04 20:28:41 +03:00
|
|
|
|
def __init__(self):
|
2025-02-04 13:16:23 +03:00
|
|
|
|
self.displayBuffer = [] # буфер для хранения всех строк
|
|
|
|
|
self.currentLine = 0 # текущий индекс строки
|
|
|
|
|
self.currentCol = 0 # текущий индекс колонки
|
|
|
|
|
self.scrollY = 0 # вертикальная прокрутка
|
|
|
|
|
self.scrollX = 0 # горизонтальная прокрутка
|
|
|
|
|
self.file_path = "" # путь к файлу
|
2025-02-04 18:36:12 +03:00
|
|
|
|
self.mode = ""
|
|
|
|
|
self.commandBuffer = [] # буффер для команды
|
2025-02-04 22:05:12 +03:00
|
|
|
|
self.exchangeBuffer = [] # буффер обмена
|
2025-02-04 18:36:12 +03:00
|
|
|
|
|
|
|
|
|
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
|
2025-02-04 13:16:23 +03:00
|
|
|
|
|
2025-02-04 14:14:46 +03:00
|
|
|
|
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
|
|
|
|
|
|
2025-02-04 18:36:12 +03:00
|
|
|
|
def InsertCommandSymbol(self, symbolCode: int) -> None:
|
|
|
|
|
self.commandBuffer.append(chr(symbolCode))
|
|
|
|
|
|
2025-02-04 14:14:46 +03:00
|
|
|
|
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
|
|
|
|
|
|
2025-02-04 18:36:12 +03:00
|
|
|
|
def EnterCommand(self):
|
2025-02-04 20:01:25 +03:00
|
|
|
|
"""Обработка введенной команды"""
|
2025-02-04 18:36:12 +03:00
|
|
|
|
cmd = ''.join(self.commandBuffer)
|
|
|
|
|
self.commandBuffer.clear()
|
|
|
|
|
match cmd:
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "$": # Перемещение в конец строки
|
|
|
|
|
self.currentCol = len(self.displayBuffer[self.currentLine])
|
|
|
|
|
case "^" | "0": # Перемещение в начало строки
|
|
|
|
|
self.currentCol = 0
|
|
|
|
|
case "q": # Выход из программы
|
2025-02-04 18:36:12 +03:00
|
|
|
|
return ReturnCode.EXIT_CODE
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "i": # Вход в режим редактирования
|
2025-02-04 20:01:25 +03:00
|
|
|
|
return ReturnCode.SET_EDIT_MODE
|
2025-02-04 21:45:10 +03:00
|
|
|
|
case "I": # Переходит в начало строки и начинает ввод текста
|
|
|
|
|
self.currentCol = 0
|
|
|
|
|
return ReturnCode.SET_EDIT_MODE
|
2025-02-04 21:48:05 +03:00
|
|
|
|
case "A": # Переходит в конец строки и начинает ввод текста
|
|
|
|
|
self.currentCol = len(self.displayBuffer[self.currentLine])
|
|
|
|
|
return ReturnCode.SET_EDIT_MODE
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "S": # Удаление строки на которой курсор и вход в режим редактирования
|
2025-02-04 20:28:41 +03:00
|
|
|
|
self.currentCol = 0
|
2025-02-04 18:46:16 +03:00
|
|
|
|
self.displayBuffer[self.currentLine] = []
|
|
|
|
|
return ReturnCode.SET_EDIT_MODE
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "RIGHT": # Перемещение курсора на 1 позицию вправо
|
2025-02-04 20:28:41 +03:00
|
|
|
|
self.currentCol += 1
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "LEFT": # Перемещение курсора на 1 позицию влево
|
2025-02-04 20:28:41 +03:00
|
|
|
|
if self.currentCol > 0:
|
|
|
|
|
self.currentCol -= 1
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "UP": # Перемещение курсора на 1 пизицию вверх
|
2025-02-04 20:01:25 +03:00
|
|
|
|
if self.currentLine > 0:
|
|
|
|
|
self.currentLine -= 1
|
|
|
|
|
if self.currentCol > len(self.displayBuffer[self.currentLine]):
|
2025-02-04 20:28:41 +03:00
|
|
|
|
self.currentCol = len(self.displayBuffer[self.currentLine])
|
2025-02-04 20:50:47 +03:00
|
|
|
|
case "DOWN": # Перемещение курсора на 1 пизицию вниз
|
2025-02-04 20:01:25 +03:00
|
|
|
|
if self.currentLine < len(self.displayBuffer) - 1:
|
|
|
|
|
self.currentLine += 1
|
|
|
|
|
if self.currentCol > len(self.displayBuffer[self.currentLine]):
|
2025-02-04 20:28:41 +03:00
|
|
|
|
self.currentCol = len(self.displayBuffer[self.currentLine])
|
2025-02-04 20:50:47 +03:00
|
|
|
|
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])
|
2025-02-04 21:08:48 +03:00
|
|
|
|
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
|
2025-02-04 21:26:55 +03:00
|
|
|
|
case "gg": # Переход в начало файла
|
|
|
|
|
self.currentLine = 0
|
|
|
|
|
self.currentCol = 0
|
2025-02-04 21:30:06 +03:00
|
|
|
|
case "G": # Переход в конец файла
|
|
|
|
|
self.currentLine = len(self.displayBuffer) - 1
|
|
|
|
|
self.currentCol = len(self.displayBuffer[self.currentLine])
|
2025-02-04 21:35:14 +03:00
|
|
|
|
case "x": # Удаляет символ после курсора
|
|
|
|
|
if self.currentCol + 1 < len(self.displayBuffer[self.currentLine]):
|
|
|
|
|
del self.displayBuffer[self.currentLine][self.currentCol + 1]
|
2025-02-04 22:05:12 +03:00
|
|
|
|
case "yy": # копирует текущую строку
|
|
|
|
|
self.exchangeBuffer = self.displayBuffer[self.currentLine].copy()
|
2025-02-04 22:49:50 +03:00
|
|
|
|
case "yw": # Копирует слово под курсором
|
|
|
|
|
line = ''.join(self.displayBuffer[self.currentLine])
|
|
|
|
|
start_index = line.rfind(' ', 0, self.currentCol)
|
|
|
|
|
start_index = 0 if start_index == -1 else start_index + 1
|
|
|
|
|
end_index = line.find(' ', self.currentCol)
|
|
|
|
|
end_index = len(line) if end_index == -1 else end_index
|
|
|
|
|
self.exchangeBuffer = self.displayBuffer[self.currentLine][start_index:end_index].copy()
|
2025-02-04 22:10:38 +03:00
|
|
|
|
case "dd": # вырезает текущую строку
|
|
|
|
|
self.exchangeBuffer = self.displayBuffer[self.currentLine].copy()
|
|
|
|
|
del self.displayBuffer[self.currentLine]
|
2025-02-04 22:05:12 +03:00
|
|
|
|
case "p": # вставить после курсора
|
|
|
|
|
self.displayBuffer[self.currentLine][self.currentCol+1:self.currentCol+1] = self.exchangeBuffer
|
|
|
|
|
|
2025-02-04 21:26:55 +03:00
|
|
|
|
return ReturnCode.GOOD
|
2025-02-04 18:36:12 +03:00
|
|
|
|
|
2025-02-04 13:49:40 +03:00
|
|
|
|
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 # Сбрасываем индекс колонки
|
|
|
|
|
|
2025-02-04 18:36:12 +03:00
|
|
|
|
def BackspaceCommand(self) -> None:
|
|
|
|
|
if len(self.commandBuffer) > 0:
|
|
|
|
|
self.commandBuffer.pop()
|
|
|
|
|
|
2025-02-04 13:49:40 +03:00
|
|
|
|
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:
|
2025-02-04 13:37:47 +03:00
|
|
|
|
if self.currentCol > 0:
|
|
|
|
|
self.currentCol -= 1
|
|
|
|
|
elif self.currentLine > 0:
|
|
|
|
|
self.currentLine -= 1
|
|
|
|
|
self.currentCol = len(self.displayBuffer[self.currentLine])
|
|
|
|
|
|
2025-02-04 13:49:40 +03:00
|
|
|
|
def MoveRight(self) -> None:
|
2025-02-04 13:37:47 +03:00
|
|
|
|
if self.currentCol < len(self.displayBuffer[self.currentLine]):
|
|
|
|
|
self.currentCol += 1
|
|
|
|
|
elif self.currentLine < len(self.displayBuffer) - 1:
|
|
|
|
|
self.currentLine += 1
|
|
|
|
|
self.currentCol = 0
|
|
|
|
|
|
2025-02-04 13:49:40 +03:00
|
|
|
|
def MoveUp(self) -> None:
|
2025-02-04 13:37:47 +03:00
|
|
|
|
if self.currentLine > 0:
|
|
|
|
|
self.currentLine -= 1
|
|
|
|
|
self.currentCol = min(self.currentCol, len(self.displayBuffer[self.currentLine]))
|
|
|
|
|
|
2025-02-04 13:49:40 +03:00
|
|
|
|
def MoveDown(self) -> None:
|
2025-02-04 13:37:47 +03:00
|
|
|
|
if self.currentLine < len(self.displayBuffer) - 1:
|
|
|
|
|
self.currentLine += 1
|
|
|
|
|
self.currentCol = min(self.currentCol, len(self.displayBuffer[self.currentLine]))
|
|
|
|
|
|
2025-02-04 14:14:46 +03:00
|
|
|
|
def LoadFile(self, file_path) -> None:
|
2025-02-04 13:16:23 +03:00
|
|
|
|
"""Загрузка файла для редактирования"""
|
|
|
|
|
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 = []
|
|
|
|
|
|
2025-02-04 14:14:46 +03:00
|
|
|
|
def SaveFile(self) -> None:
|
2025-02-04 13:16:23 +03:00
|
|
|
|
"""Сохранение файла"""
|
|
|
|
|
try:
|
|
|
|
|
with open(self.file_path, "w") as file:
|
|
|
|
|
for line in self.displayBuffer:
|
|
|
|
|
file.write(''.join(line) + '\n')
|
2025-02-04 14:14:46 +03:00
|
|
|
|
print(f"File {self.file_path} saved successfully.")
|
2025-02-04 13:16:23 +03:00
|
|
|
|
except Exception as e:
|
2025-02-04 14:14:46 +03:00
|
|
|
|
print(f"Error saving file: {str(e)}")
|