первичное разбиение на MVC
parent
c9d258cdfc
commit
50595ba6f6
|
@ -1,5 +1,4 @@
|
||||||
example.txt
|
example.txt
|
||||||
boost_python312-vc143-mt-x64-1_87.dll
|
boost_python312-vc143-mt-x64-1_87.dll
|
||||||
mystring.pyd
|
mystring.pyd
|
||||||
views.py
|
|
||||||
__pycache__
|
__pycache__
|
|
@ -0,0 +1,105 @@
|
||||||
|
import curses
|
||||||
|
|
||||||
|
class EditController:
|
||||||
|
def __init__(self, model, view):
|
||||||
|
self.model = model
|
||||||
|
self.view = view
|
||||||
|
|
||||||
|
def handle_input(self, symbolCode):
|
||||||
|
"""Обработка ввода пользователя"""
|
||||||
|
if symbolCode == 27: # EXIT
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif symbolCode == curses.KEY_LEFT:
|
||||||
|
if self.model.currentCol > 0:
|
||||||
|
self.model.currentCol -= 1
|
||||||
|
elif self.model.currentLine > 0:
|
||||||
|
self.model.currentLine -= 1
|
||||||
|
self.model.currentCol = len(self.model.displayBuffer[self.model.currentLine])
|
||||||
|
|
||||||
|
elif symbolCode == curses.KEY_RIGHT:
|
||||||
|
if self.model.currentCol < len(self.model.displayBuffer[self.model.currentLine]):
|
||||||
|
self.model.currentCol += 1
|
||||||
|
elif self.model.currentLine < len(self.model.displayBuffer) - 1:
|
||||||
|
self.model.currentLine += 1
|
||||||
|
self.model.currentCol = 0
|
||||||
|
|
||||||
|
elif symbolCode == curses.KEY_UP:
|
||||||
|
if self.model.currentLine > 0:
|
||||||
|
self.model.currentLine -= 1
|
||||||
|
self.model.currentCol = min(self.model.currentCol, len(self.model.displayBuffer[self.model.currentLine]))
|
||||||
|
|
||||||
|
elif symbolCode == curses.KEY_DOWN:
|
||||||
|
if self.model.currentLine < len(self.model.displayBuffer) - 1:
|
||||||
|
self.model.currentLine += 1
|
||||||
|
self.model.currentCol = min(self.model.currentCol, len(self.model.displayBuffer[self.model.currentLine]))
|
||||||
|
|
||||||
|
# BACKSPACE
|
||||||
|
elif symbolCode in (127, 8): # Backspace
|
||||||
|
if self.model.currentCol > 0: # Если символ существует в текущей строке
|
||||||
|
self.model.currentCol -= 1
|
||||||
|
del self.model.displayBuffer[self.model.currentLine][self.model.currentCol] # Удаляем символ
|
||||||
|
elif self.model.currentLine > 0: # Если текущая строка не первая
|
||||||
|
# Объединяем текущую строку с предыдущей
|
||||||
|
prev_line_length = len(self.model.displayBuffer[self.model.currentLine - 1])
|
||||||
|
self.model.displayBuffer[self.model.currentLine - 1].extend(self.model.displayBuffer[self.model.currentLine])
|
||||||
|
del self.model.displayBuffer[self.model.currentLine]
|
||||||
|
self.model.currentLine -= 1
|
||||||
|
self.model.currentCol = prev_line_length # Переходим в конец предыдущей строки
|
||||||
|
|
||||||
|
# ENTER
|
||||||
|
elif symbolCode == 10: # Enter
|
||||||
|
# Разделяем текущую строку на две части
|
||||||
|
new_line = self.model.displayBuffer[self.model.currentLine][self.model.currentCol:]
|
||||||
|
self.model.displayBuffer[self.model.currentLine] = self.model.displayBuffer[self.model.currentLine][:self.model.currentCol]
|
||||||
|
self.model.currentLine += 1 # Переходим на следующую строку
|
||||||
|
self.model.displayBuffer.insert(self.model.currentLine, new_line) # Вставляем новую строку
|
||||||
|
self.model.currentCol = 0 # Сбрасываем индекс колонки
|
||||||
|
|
||||||
|
# Сохранение файла (Ctrl+S)
|
||||||
|
elif symbolCode == 19: # Ctrl+S
|
||||||
|
result = self.model.save_file()
|
||||||
|
if result is True:
|
||||||
|
self.view.screen.addstr(self.view.lines - 1, 0, f"File {self.model.file_path} saved successfully.")
|
||||||
|
else:
|
||||||
|
self.view.screen.addstr(self.view.lines - 1, 0, f"Error saving file: {result}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
if self.model.currentCol <= len(self.model.displayBuffer[self.model.currentLine]): # проверяем, не превышает ли индекс колонки длину строки
|
||||||
|
self.model.displayBuffer[self.model.currentLine].insert(self.model.currentCol, chr(symbolCode))
|
||||||
|
self.model.currentCol += 1
|
||||||
|
|
||||||
|
# Прокрутка экрана
|
||||||
|
if self.model.currentLine < self.model.scrollY:
|
||||||
|
self.model.scrollY = self.model.currentLine
|
||||||
|
elif self.model.currentLine >= self.model.scrollY + self.view.lines - 1:
|
||||||
|
self.model.scrollY = self.model.currentLine - self.view.lines + 2
|
||||||
|
|
||||||
|
if self.model.currentCol < self.model.scrollX:
|
||||||
|
self.model.scrollX = self.model.currentCol
|
||||||
|
elif self.model.currentCol >= self.model.scrollX + self.view.cols:
|
||||||
|
self.model.scrollX = self.model.currentCol - self.view.cols + 1
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
"""Отрисовка текущего состояния"""
|
||||||
|
self.view.screen.clear()
|
||||||
|
|
||||||
|
# Отображение видимой части текста
|
||||||
|
for i in range(self.view.lines - 1):
|
||||||
|
if i + self.model.scrollY < len(self.model.displayBuffer):
|
||||||
|
line = ''.join(self.model.displayBuffer[i + self.model.scrollY])
|
||||||
|
if self.model.scrollX < len(line):
|
||||||
|
self.view.SetString(i, 0, line[self.model.scrollX:self.model.scrollX + self.view.cols])
|
||||||
|
else:
|
||||||
|
self.view.SetString(i, 0, '')
|
||||||
|
else:
|
||||||
|
self.view.SetString(i, 0, '')
|
||||||
|
|
||||||
|
# Отображение панели режима
|
||||||
|
self.view.EditModeBar(self.model.currentLine, len(self.model.displayBuffer), self.model.file_path)
|
||||||
|
|
||||||
|
# Установка курсора
|
||||||
|
self.view.SetCursor(self.model.currentLine - self.model.scrollY, self.model.currentCol - self.model.scrollX)
|
||||||
|
self.view.Refresh()
|
|
@ -0,0 +1,23 @@
|
||||||
|
from models import VimModel
|
||||||
|
from views import CursesAdapter
|
||||||
|
from controllers import EditController
|
||||||
|
|
||||||
|
def main():
|
||||||
|
model = VimModel()
|
||||||
|
view = CursesAdapter()
|
||||||
|
controller = EditController(model, view)
|
||||||
|
|
||||||
|
# Загрузка файла для редактирования
|
||||||
|
file_path = "example.txt" # Укажите путь к файлу
|
||||||
|
model.load_file(file_path)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
controller.render()
|
||||||
|
symbolCode = view.GetChar()
|
||||||
|
if not controller.handle_input(symbolCode):
|
||||||
|
break
|
||||||
|
|
||||||
|
view.Cleanup()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -0,0 +1,28 @@
|
||||||
|
class VimModel:
|
||||||
|
def __init__(self):
|
||||||
|
self.displayBuffer = [] # буфер для хранения всех строк
|
||||||
|
self.currentLine = 0 # текущий индекс строки
|
||||||
|
self.currentCol = 0 # текущий индекс колонки
|
||||||
|
self.scrollY = 0 # вертикальная прокрутка
|
||||||
|
self.scrollX = 0 # горизонтальная прокрутка
|
||||||
|
self.file_path = "" # путь к файлу
|
||||||
|
|
||||||
|
def load_file(self, file_path):
|
||||||
|
"""Загрузка файла для редактирования"""
|
||||||
|
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 save_file(self):
|
||||||
|
"""Сохранение файла"""
|
||||||
|
try:
|
||||||
|
with open(self.file_path, "w") as file:
|
||||||
|
for line in self.displayBuffer:
|
||||||
|
file.write(''.join(line) + '\n')
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
173
test.py
173
test.py
|
@ -1,173 +0,0 @@
|
||||||
import curses
|
|
||||||
|
|
||||||
class CursesAdapter:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.screen = curses.initscr()
|
|
||||||
self.screen.keypad(True)
|
|
||||||
self.cols = curses.COLS
|
|
||||||
self.lines = curses.LINES
|
|
||||||
# init color system
|
|
||||||
curses.start_color()
|
|
||||||
# green color pair
|
|
||||||
curses.init_pair(1, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
|
||||||
curses.curs_set(1) # Make cursor visible
|
|
||||||
|
|
||||||
def Refresh(self) -> None:
|
|
||||||
"""Apply changes"""
|
|
||||||
self.screen.refresh()
|
|
||||||
|
|
||||||
def Cleanup(self) -> None:
|
|
||||||
curses.endwin()
|
|
||||||
|
|
||||||
def SetCursor(self, x: int, y: int):
|
|
||||||
"""set cursor position (x, y)"""
|
|
||||||
self.screen.move(x, y)
|
|
||||||
|
|
||||||
def SetChar(self, x: int, y: int, code: int):
|
|
||||||
"""set char position (x, y)"""
|
|
||||||
self.screen.addch(x, y, code)
|
|
||||||
|
|
||||||
def SetString(self, x: int, y: int, data: str):
|
|
||||||
"""set string begin position (x, y)"""
|
|
||||||
self.screen.addstr(x, y, data)
|
|
||||||
|
|
||||||
def GetChar(self) -> int:
|
|
||||||
"""Wait users input"""
|
|
||||||
return self.screen.getch()
|
|
||||||
|
|
||||||
def EditModeBar(self, currentLine, totalLines, fileName):
|
|
||||||
"""Print edit mode information panel"""
|
|
||||||
self.screen.addstr(self.lines - 1, 0, ' ' * (self.cols - 1)) # Очистка строки
|
|
||||||
self.screen.addstr(self.lines - 1, 0, "FILE: ")
|
|
||||||
self.screen.addstr(fileName, curses.color_pair(1)) # Имя файла
|
|
||||||
self.screen.addstr(" | MODE:")
|
|
||||||
self.screen.addstr(" EDIT", curses.color_pair(1))
|
|
||||||
self.screen.addstr(" | LINE: ")
|
|
||||||
self.screen.addstr(str(currentLine + 1), curses.color_pair(1))
|
|
||||||
self.screen.addstr("/")
|
|
||||||
self.screen.addstr(str(totalLines), curses.color_pair(1)) # Общее количество строк
|
|
||||||
|
|
||||||
def main():
|
|
||||||
adapter = CursesAdapter()
|
|
||||||
|
|
||||||
currentCol = 0 # текущий индекс колонки
|
|
||||||
currentLine = 0 # текущий индекс строки
|
|
||||||
scrollY = 0 # вертикальная прокрутка
|
|
||||||
scrollX = 0 # горизонтальная прокрутка
|
|
||||||
displayBuffer = [] # буфер для хранения всех строк
|
|
||||||
|
|
||||||
mode = "EDIT"
|
|
||||||
|
|
||||||
# Загрузка файла для редактирования
|
|
||||||
file_path = "example.txt" # Укажите путь к файлу
|
|
||||||
try:
|
|
||||||
with open(file_path, "r") as file:
|
|
||||||
displayBuffer = [list(line.rstrip('\n')) for line in file.readlines()]
|
|
||||||
except FileNotFoundError:
|
|
||||||
print(f"File {file_path} not found. Starting with empty buffer.")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# Очистка экрана
|
|
||||||
adapter.screen.clear()
|
|
||||||
|
|
||||||
# Отображение видимой части текста
|
|
||||||
for i in range(adapter.lines - 1):
|
|
||||||
if i + scrollY < len(displayBuffer):
|
|
||||||
line = ''.join(displayBuffer[i + scrollY])
|
|
||||||
if scrollX < len(line):
|
|
||||||
adapter.SetString(i, 0, line[scrollX:scrollX + adapter.cols])
|
|
||||||
else:
|
|
||||||
adapter.SetString(i, 0, '')
|
|
||||||
else:
|
|
||||||
adapter.SetString(i, 0, '')
|
|
||||||
|
|
||||||
# Отображение панели режима
|
|
||||||
adapter.EditModeBar(currentLine, len(displayBuffer), file_path)
|
|
||||||
|
|
||||||
# Установка курсора
|
|
||||||
adapter.SetCursor(currentLine - scrollY, currentCol - scrollX)
|
|
||||||
adapter.Refresh()
|
|
||||||
|
|
||||||
# Ожидание ввода
|
|
||||||
if mode == "EDIT":
|
|
||||||
symbolCode = adapter.GetChar()
|
|
||||||
|
|
||||||
if symbolCode == 27: # EXIT
|
|
||||||
break
|
|
||||||
|
|
||||||
elif symbolCode == curses.KEY_LEFT:
|
|
||||||
if currentCol > 0:
|
|
||||||
currentCol -= 1
|
|
||||||
elif currentLine > 0:
|
|
||||||
currentLine -= 1
|
|
||||||
currentCol = len(displayBuffer[currentLine])
|
|
||||||
|
|
||||||
elif symbolCode == curses.KEY_RIGHT:
|
|
||||||
if currentCol < len(displayBuffer[currentLine]):
|
|
||||||
currentCol += 1
|
|
||||||
elif currentLine < len(displayBuffer) - 1:
|
|
||||||
currentLine += 1
|
|
||||||
currentCol = 0
|
|
||||||
|
|
||||||
elif symbolCode == curses.KEY_UP:
|
|
||||||
if currentLine > 0:
|
|
||||||
currentLine -= 1
|
|
||||||
currentCol = min(currentCol, len(displayBuffer[currentLine]))
|
|
||||||
|
|
||||||
elif symbolCode == curses.KEY_DOWN:
|
|
||||||
if currentLine < len(displayBuffer) - 1:
|
|
||||||
currentLine += 1
|
|
||||||
currentCol = min(currentCol, len(displayBuffer[currentLine]))
|
|
||||||
|
|
||||||
# BACKSPACE
|
|
||||||
elif symbolCode in (127, 8): # Backspace
|
|
||||||
if currentCol > 0: # Если символ существует в текущей строке
|
|
||||||
currentCol -= 1
|
|
||||||
del displayBuffer[currentLine][currentCol] # Удаляем символ
|
|
||||||
elif currentLine > 0: # Если текущая строка не первая
|
|
||||||
# Объединяем текущую строку с предыдущей
|
|
||||||
prev_line_length = len(displayBuffer[currentLine - 1])
|
|
||||||
displayBuffer[currentLine - 1].extend(displayBuffer[currentLine])
|
|
||||||
del displayBuffer[currentLine]
|
|
||||||
currentLine -= 1
|
|
||||||
currentCol = prev_line_length # Переходим в конец предыдущей строки
|
|
||||||
|
|
||||||
# ENTER
|
|
||||||
elif symbolCode == 10: # Enter
|
|
||||||
# Разделяем текущую строку на две части
|
|
||||||
new_line = displayBuffer[currentLine][currentCol:]
|
|
||||||
displayBuffer[currentLine] = displayBuffer[currentLine][:currentCol]
|
|
||||||
currentLine += 1 # Переходим на следующую строку
|
|
||||||
displayBuffer.insert(currentLine, new_line) # Вставляем новую строку
|
|
||||||
currentCol = 0 # Сбрасываем индекс колонки
|
|
||||||
|
|
||||||
# Сохранение файла (Ctrl+S)
|
|
||||||
elif symbolCode == 19: # Ctrl+S
|
|
||||||
try:
|
|
||||||
with open(file_path, "w") as file:
|
|
||||||
for line in displayBuffer:
|
|
||||||
file.write(''.join(line) + '\n')
|
|
||||||
adapter.screen.addstr(adapter.lines - 1, 0, f"File {file_path} saved successfully.")
|
|
||||||
except Exception as e:
|
|
||||||
adapter.screen.addstr(adapter.lines - 1, 0, f"Error saving file: {e}")
|
|
||||||
|
|
||||||
else:
|
|
||||||
if currentCol <= len(displayBuffer[currentLine]): # проверяем, не превышает ли индекс колонки длину строки
|
|
||||||
displayBuffer[currentLine].insert(currentCol, chr(symbolCode))
|
|
||||||
currentCol += 1
|
|
||||||
|
|
||||||
# Прокрутка экрана
|
|
||||||
if currentLine < scrollY:
|
|
||||||
scrollY = currentLine
|
|
||||||
elif currentLine >= scrollY + adapter.lines - 1:
|
|
||||||
scrollY = currentLine - adapter.lines + 2
|
|
||||||
|
|
||||||
if currentCol < scrollX:
|
|
||||||
scrollX = currentCol
|
|
||||||
elif currentCol >= scrollX + adapter.cols:
|
|
||||||
scrollX = currentCol - adapter.cols + 1
|
|
||||||
|
|
||||||
adapter.Cleanup()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import curses
|
||||||
|
|
||||||
|
class CursesAdapter:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.screen = curses.initscr()
|
||||||
|
self.screen.keypad(True)
|
||||||
|
self.cols = curses.COLS
|
||||||
|
self.lines = curses.LINES
|
||||||
|
# init color system
|
||||||
|
curses.start_color()
|
||||||
|
# green color pair
|
||||||
|
curses.init_pair(1, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
||||||
|
curses.curs_set(1) # Make cursor visible
|
||||||
|
|
||||||
|
def Refresh(self) -> None:
|
||||||
|
"""Apply changes"""
|
||||||
|
self.screen.refresh()
|
||||||
|
|
||||||
|
def Cleanup(self) -> None:
|
||||||
|
curses.endwin()
|
||||||
|
|
||||||
|
def SetCursor(self, x: int, y: int):
|
||||||
|
"""set cursor position (x, y)"""
|
||||||
|
self.screen.move(x, y)
|
||||||
|
|
||||||
|
def SetChar(self, x: int, y: int, code: int):
|
||||||
|
"""set char position (x, y)"""
|
||||||
|
self.screen.addch(x, y, code)
|
||||||
|
|
||||||
|
def SetString(self, x: int, y: int, data: str):
|
||||||
|
"""set string begin position (x, y)"""
|
||||||
|
self.screen.addstr(x, y, data)
|
||||||
|
|
||||||
|
def GetChar(self) -> int:
|
||||||
|
"""Wait users input"""
|
||||||
|
return self.screen.getch()
|
||||||
|
|
||||||
|
def EditModeBar(self, currentLine, totalLines, fileName):
|
||||||
|
"""Print edit mode information panel"""
|
||||||
|
self.screen.addstr(self.lines - 1, 0, ' ' * (self.cols - 1)) # Очистка строки
|
||||||
|
self.screen.addstr(self.lines - 1, 0, "FILE: ")
|
||||||
|
self.screen.addstr(fileName, curses.color_pair(1)) # Имя файла
|
||||||
|
self.screen.addstr(" | MODE:")
|
||||||
|
self.screen.addstr(" EDIT", curses.color_pair(1))
|
||||||
|
self.screen.addstr(" | LINE: ")
|
||||||
|
self.screen.addstr(str(currentLine + 1), curses.color_pair(1))
|
||||||
|
self.screen.addstr("/")
|
||||||
|
self.screen.addstr(str(totalLines), curses.color_pair(1)) # Общее количество строк
|
Loading…
Reference in New Issue