from abc import ABC, abstractmethod import curses import mvc.models from mystring import MyString as MyString class CursesAdapter: def __init__(self) -> None: self.KEY_BACKSPACE_2 = 8 self.KEY_ENTER = 10 self.KEY_CTRL_S = 19 self.KEY_ESCAPE = 27 self.KEY_DOLLAR = 36 self.KEY_NULL = 48 self.KEY_TWO_DOTS = 59 self.KEY_G = 71 self.KEY_U = 85 self.KEY_XOR = 94 self.KEY_b = 98 self.KEY_d = 100 self.KEY_g = 103 self.KEY_p = 112 self.KEY_w = 119 self.KEY_x = 120 self.KEY_y = 121 self.KEY_BACKSPACE_1 = 127 self.KEY_LEFT = curses.KEY_LEFT self.KEY_RIGHT = curses.KEY_RIGHT self.KEY_UP = curses.KEY_UP self.KEY_DOWN = curses.KEY_DOWN self.KEY_PG_UP = 450 self.KEY_PG_DOWN = 456 self.screen = curses.initscr() self.screen.keypad(True) self.cols = curses.COLS self.lines = curses.LINES curses.noecho() curses.curs_set(1) # Make cursor visible curses.start_color() curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_BLACK) # Фиолетовый текст на черном фоне curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE) # Фиолетовый текст на черном фоне self.colorPairs = [curses.color_pair(1), curses.color_pair(2)] 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 SetColorString(self, x: int, y: int, data: str, attr: int): self.screen.addstr(x, y, data, attr) 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() class Observer: @abstractmethod def Update(self): pass class VimView(Observer): def __init__(self, adapter: CursesAdapter) -> None: self.curses_adapter = adapter self.model = None def SetModel(self, model: mvc.models.VimModel): self.model = model model.attach(self) def Update(self): self.Render( self.model.displayBuffer, self.model.currentLine, self.model.currentCol, self.model.scrollX, self.model.scrollY, self.model.ModeBar(), self.model.showLineNumbers ) def Render(self, displayBuffer: list[list[MyString]], currentLine: int, currentCol: int, scrollX: int, scrollY: int, modeBarData: str, show_line_numbers: bool = False): """Отрисовка текущего состояния""" self.curses_adapter.screen.clear() # Ширина нумерации строк (6 символов + 1 пробел) line_number_width = 7 if show_line_numbers else 0 # Отображение видимой части текста for i in range(self.curses_adapter.lines - 1): if i + scrollY < len(displayBuffer): line = ''.join(item.data() for item in displayBuffer[i + scrollY]) # Если нужно отображать номера строк, добавляем их перед текстом if show_line_numbers: line_number = f"{i + scrollY + 1:6} " # 6 символов на номер строки # Выводим номер строки фиолетовым цветом self.curses_adapter.SetColorString(i, 0, line_number, self.curses_adapter.colorPairs[0]) # Выводим текст с учетом прокрутки и ширины нумерации visible_text_start = scrollX # Начало видимой части текста visible_text_end = scrollX + self.curses_adapter.cols - line_number_width # Конец видимой части текста visible_text = line[visible_text_start:visible_text_end] self.curses_adapter.SetString(i, line_number_width, visible_text) else: self.curses_adapter.SetString(i, 0, '') # Обновление панели режима self.SetModeBar(modeBarData) # Установка курсора с учетом прокрутки и нумерации строк cursor_x = currentLine - scrollY cursor_y = currentCol - scrollX + line_number_width # Проверка, чтобы курсор не вышел за пределы экрана if 0 <= cursor_x < self.curses_adapter.lines - 1 and 0 <= cursor_y < self.curses_adapter.cols: self.curses_adapter.SetCursor(cursor_x, cursor_y) self.curses_adapter.Refresh() def SetModeBar(self, modeBarData: str): """Print edit mode information panel""" if len(modeBarData) > self.curses_adapter.cols - 1: scrollX = len(modeBarData) - self.curses_adapter.cols self.curses_adapter.SetColorString(self.curses_adapter.lines - 1, 0, modeBarData[scrollX + 1:], self.curses_adapter.colorPairs[1]) else: self.curses_adapter.SetColorString(self.curses_adapter.lines - 1, 0, modeBarData, self.curses_adapter.colorPairs[1])