Compare commits

...

10 Commits

8 changed files with 293 additions and 158 deletions

91
adapter.py Normal file
View File

@ -0,0 +1,91 @@
from abc import ABC, abstractmethod
import curses
class IController(ABC):
@abstractmethod
def GetChar(self) -> int:
pass
class IView(ABC):
@abstractmethod
def Refresh(self) -> None:
pass
@abstractmethod
def Cleanup(self) -> None:
pass
@abstractmethod
def SetCursor(self, x: int, y: int) -> None:
pass
@abstractmethod
def SetChar(self, x: int, y: int, code: int) -> None:
pass
@abstractmethod
def SetColorString(self, x: int, y: int, data: str, attr: int) -> None:
pass
@abstractmethod
def SetString(self, x: int, y: int, data: str) -> None:
pass
class CursesAdapter(IView, IController):
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_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 = 451
self.KEY_PG_DOWN = 457
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) -> None:
"""set cursor position (x, y)"""
self.screen.move(x, y)
def SetChar(self, x: int, y: int, code: int) -> None:
"""set char position (x, y)"""
self.screen.addch(x, y, code)
def SetColorString(self, x: int, y: int, data: str, attr: int) -> None:
self.screen.addstr(x, y, data, attr)
def SetString(self, x: int, y: int, data: str) -> None:
"""set string begin position (x, y)"""
self.screen.addstr(x, y, data)
def GetChar(self) -> int:
"""Wait users input"""
return self.screen.getch()

24
main.py
View File

@ -1,8 +1,9 @@
import sys
from mvc.models import VimModel
from mvc.views import VimView, CursesAdapter
from mvc.controllers import Controller, ReturnCode
from mvc.controllers import EditStrategy, CommandStrategy, NormalStrategy
from mvc.views import VimView
from adapter import CursesAdapter
from mvc.controllers import Controller
from mvc.controllers import NormalStrategy
import tools
def main():
@ -19,22 +20,7 @@ def main():
view.SetModel(model) # view subscribe model
while True:
model.notify()
symbolCode = view.curses_adapter.GetChar()
match controller.HandleInput(symbolCode):
case ReturnCode.SET_BASIC_MODE:
controller.SetStrategy(NormalStrategy(model, view.curses_adapter))
case ReturnCode.SET_COMMAND_MODE:
controller.SetStrategy(CommandStrategy(model, view.curses_adapter))
case ReturnCode.SET_EDIT_MODE:
controller.SetStrategy(EditStrategy(model, view.curses_adapter))
case ReturnCode.EXIT_CODE:
break
view.curses_adapter.Cleanup()
controller.Run()
if __name__ == "__main__":
main()

View File

@ -1,6 +1,6 @@
import tools
from abc import ABC, abstractmethod
from mvc.views import CursesAdapter
from adapter import CursesAdapter
from mvc.models import VimModel, ReturnCode
def CommonInput(HandleInputFunc):
@ -47,6 +47,23 @@ class Controller:
def HandleInput(self, symbolCode: int):
return self.strategy.HandleInput(symbolCode)
def Run(self) -> None:
while True:
self.strategy.model.notify()
symbolCode = self.strategy.adapter.GetChar()
match self.HandleInput(symbolCode):
case ReturnCode.SET_BASIC_MODE:
self.SetStrategy(NormalStrategy(self.strategy.model, self.strategy.adapter))
case ReturnCode.SET_COMMAND_MODE:
self.SetStrategy(CommandStrategy(self.strategy.model, self.strategy.adapter))
case ReturnCode.SET_EDIT_MODE:
self.SetStrategy(EditStrategy(self.strategy.model, self.strategy.adapter))
case ReturnCode.EXIT_CODE:
break
self.strategy.adapter.Cleanup()
class CommandStrategy(BaseStrategy):
"""command input mode"""
@ -111,7 +128,6 @@ class NormalStrategy(BaseStrategy):
case self.adapter.KEY_y:
if self.model.CombinationCheck("yy", 3): self.model.CopyLine()
case self.adapter.KEY_x: self.model.DeleteNext()
case self.adapter.KEY_U: self.model.RecoverLine()
case self.adapter.KEY_G:
num, ind = tools.ExtracrtNumBeforeLastKey(''.join([chr(item[0]) for item in self.model.keyLog]))
if num != None and ind != None: self.model.MoveToLine(num)

View File

@ -1,5 +1,9 @@
from abc import ABC, abstractmethod
import tools
from enum import Enum
from typing import List
from mystring import MyString as MyString
class ReturnCode(Enum):
GOOD = -101
@ -9,33 +13,62 @@ class ReturnCode(Enum):
SET_EDIT_MODE = -97
CONTINUE = -96
class VimModel:
class Observable(ABC):
@abstractmethod
def attach(self, observer) -> None:
pass
@abstractmethod
def detach(self, observer) -> None:
pass
@abstractmethod
def notify(self) -> None:
pass
class VimModel(Observable):
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):
def attach(self, observer) -> None:
self.observers.append(observer)
def detach(self, observer):
def detach(self, observer) -> None:
self.observers.remove(observer)
def notify(self):
def notify(self) -> None:
for observer in self.observers:
observer.Update()
@ -46,7 +79,7 @@ class VimModel:
if len(self.keyLog) > 5000: self.keyLog.clear()
self.keyLog.append((symbolCode, tools.UnixSec()))
def CombinationCheck(self, comb: str, interval: int) -> None:
def CombinationCheck(self, comb: str, interval: int) -> bool:
"""Проверяет была ли нажата комбинация клавиш.
Принимает фактический ввод, команду с которой сравниваем и интервал времени на команду"""
if len(self.keyLog) > len(comb) - 1:
@ -59,7 +92,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 +114,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 +132,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 +141,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 +157,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]
self.exchangeBuffer = line[start_index:end_index+1]
self.exchangeBuffer = self.displayBuffer[self.currentLine].\
substr(start_index, end_index-start_index).data()
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
@ -170,10 +201,10 @@ class VimModel:
self.currentLine = numberLine
self.currentCol = 0
def EnterCommand(self):
def EnterCommand(self) -> None:
"""Обработка введенной команды"""
cmd = ''.join(self.commandBuffer)
self.commandBuffer.clear()
self.commandBuffer = ""
match cmd:
case "q": # Выход из программы
if self.displayBuffer == self.dump:
@ -195,7 +226,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 +236,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,16 +246,14 @@ 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:
self.currentLine = index
self.currentCol = 0
self.lastSearch = (self.lastSearch[0], (self.lastSearch[1]+1)%2)
case "e!":
self.displayBuffer = [sublist.copy() for sublist in self.dump]
case "set num":
self.showLineNumbers = not self.showLineNumbers
case _:
@ -239,14 +268,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 +284,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 +297,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 +306,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,11 +354,7 @@ class VimModel:
def Dump(self) -> None:
"""Обновляет дамп данных"""
self.dump = [sublist.copy() for sublist in self.displayBuffer]
def RecoverLine(self) -> None:
self.displayBuffer[self.currentLine] = self.dump[self.currentLine].copy()
self.currentCol = 0
self.dump = [line.substr(0) for line in self.displayBuffer]
def LoadFile(self, file_path) -> None:
"""Загрузка файла для редактирования"""
@ -338,12 +363,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 +379,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 +398,5 @@ class VimModel:
self.mode = ""
self.inputAfterCursor = False
self.keyLog = [] # лог нажатий клавиш (кортежи вида (код символа, юникс время нажатия))
self.commandBuffer = [] # буффер для команды
self.exchangeBuffer = [] # буффер обмена
self.commandBuffer = "" # буффер для команды
self.exchangeBuffer = "" # буффер обмена

View File

@ -1,75 +1,11 @@
from abc import ABC, abstractmethod
import curses
from adapter import CursesAdapter
import mvc.models
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()
from mystring import MyString as MyString
class Observer:
class Observer(ABC):
@abstractmethod
def Update(self):
def Update(self) -> None:
pass
class VimView(Observer):
@ -77,11 +13,11 @@ class VimView(Observer):
self.curses_adapter = adapter
self.model = None
def SetModel(self, model: mvc.models.VimModel):
def SetModel(self, model: mvc.models.VimModel) -> None:
self.model = model
model.attach(self)
def Update(self):
def Update(self) -> None:
self.Render(
self.model.displayBuffer,
self.model.currentLine,
@ -93,11 +29,11 @@ class VimView(Observer):
)
def Render(self,
displayBuffer: list[list[str]],
displayBuffer: list[MyString],
currentLine: int, currentCol: int,
scrollX: int, scrollY: int,
modeBarData: str,
show_line_numbers: bool = False):
show_line_numbers: bool = False) -> None:
"""Отрисовка текущего состояния"""
self.curses_adapter.screen.clear()
@ -108,7 +44,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 символов на номер строки
@ -136,7 +72,7 @@ class VimView(Observer):
self.curses_adapter.Refresh()
def SetModeBar(self, modeBarData: str):
def SetModeBar(self, modeBarData: str) -> None:
"""Print edit mode information panel"""
if len(modeBarData) > self.curses_adapter.cols - 1:
scrollX = len(modeBarData) - self.curses_adapter.cols

79
test.py Normal file
View File

@ -0,0 +1,79 @@
# python -m unittest test.py
import unittest
from mvc.models import VimModel
from mystring import MyString as MyString
class TestVimModel(unittest.TestCase):
def setUp(self):
self.model = VimModel(displayLinesCount=10, displayColsCount=80)
self.model.displayBuffer = [MyString("line 1"), MyString("line 2")]
def test_initial_state(self):
self.assertEqual(self.model.currentLine, 0)
self.assertEqual(self.model.currentCol, 0)
self.assertEqual(len(self.model.displayBuffer), 2)
def test_insert_symbol(self):
self.model.InsertSymbol(ord('a'))
self.assertEqual(self.model.displayBuffer[0].data(), "aline 1")
self.assertEqual(self.model.currentCol, 1)
def test_move_right(self):
self.model.currentCol = 5
self.model.MoveRight()
self.assertEqual(self.model.currentCol, 6)
def test_move_left(self):
self.model.currentCol = 5
self.model.MoveLeft()
self.assertEqual(self.model.currentCol, 4)
def test_move_up(self):
self.model.currentLine = 1
self.model.MoveUp()
self.assertEqual(self.model.currentLine, 0)
def test_move_down(self):
self.model.MoveDown()
self.assertEqual(self.model.currentLine, 1)
def test_backspace(self):
self.model.currentCol = 5
self.model.Backspace()
self.assertEqual(self.model.displayBuffer[0].data(), "line1")
self.assertEqual(self.model.currentCol, 4)
def test_enter(self):
self.model.currentCol = 5
self.model.Enter()
self.assertEqual(len(self.model.displayBuffer), 3)
self.assertEqual(self.model.displayBuffer[0].data(), "line ")
self.assertEqual(self.model.displayBuffer[1].data(), "1")
self.assertEqual(self.model.currentLine, 1)
self.assertEqual(self.model.currentCol, 0)
def test_to_line_start(self):
self.model.currentCol = 5
self.model.ToLineStart()
self.assertEqual(self.model.currentCol, 0)
def test_to_line_end(self):
self.model.ToLineEnd()
self.assertEqual(self.model.currentCol, len(self.model.displayBuffer[0]))
def test_save_file(self):
self.model.SaveFile()
with open("testfile.txt", "r") as file:
lines = file.readlines()
self.assertEqual(lines[0].strip(), "line 1")
self.assertEqual(lines[1].strip(), "line 2")
def test_load_file(self):
self.model.LoadFile("testfile.txt")
self.assertEqual(len(self.model.displayBuffer), 2)
self.assertEqual(self.model.displayBuffer[0].data(), "line 1")
self.assertEqual(self.model.displayBuffer[1].data(), "line 2")
if __name__ == '__main__':
unittest.main()

2
testfile.txt Normal file
View File

@ -0,0 +1,2 @@
line 1
line 2

View File

@ -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