Skip to content

Commit

Permalink
Translate the whole project to the English
Browse files Browse the repository at this point in the history
  • Loading branch information
markmelix committed Dec 4, 2023
1 parent 3d0a4aa commit 5121fc5
Show file tree
Hide file tree
Showing 15 changed files with 210 additions and 267 deletions.
112 changes: 47 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,49 @@
# Tetra Editor

Модульный редактор кода с графическим интерфейсом. В редакторе можно создавать,
сохранять и переключатся между файлами с помощью таббара. Также имеется
подсветка кода и отключаемый статусбар, в котором расположена информация о
текущем открытом файле. Из коробки доступно 6 модулей, 3 из которых можно
настраивать.

Основные возможности редактора:
- Создание, сохранение и переключение между файлами.
- Подсветка кода, нумерация строк, подсветка текущей строки
- Выбор темы и настройка внешнего вида редактора
- Настройка отображаемых в статусбаре блоков
- Возможность экспортировать и импортировать настройки редактора
- Включение, отключение и настройка компонентов программы

## Описание реализации

Всего в проекте имеется порядка 30 классов, каждый из которых используется для реализации определенной функции.

### Описание основных классов

1. Core: Ядро редактора, которое управляет всеми процессами в редакторе и доступ
к которому имеют все модули
2. Event: Класс-перечисление, в котором указаны все возможные события
3. Module: Определяет каждый модуль как совокупность общих методов, позволяющих взаимодействовать с ним через общий интерфейс
4. Buffer: Представитель абстрактных текстовых буферов, с которыми пользователь может взаимодействовать с помощью графических буферов (см. GuiBuffer)
5. BufferManager: Менеджер-хранилище абстрактных буферов (Buffer)
6. GuiBuffer: Графический буфер редактирования, в котором происходит непосредственно набор и взаимодействие с текстом
7. Setting: Общий класс-представитель всех настроек модулей
8. Settings: Независимый виджет, через который пользователю предоставляется управление настройками модулей

### Система-событый

Как только в редакторе что-то происходит (например, открывается файл), сразу
после этого в специальный список (Core.events) добавляется новое событие и
подается сигнал на поверхностное обновление всех модулей редактора (метод
Module.refresh) для обработки этого события. Все эти процессы можно обобщить и
обобщенную систему назвать Event-системой, или системой событий. Система событий
не выражена отдельным классом, а встроена непосредственно в ядро редактора и
взаимодействие с ней происходит с помощью специальных методов.

Для того чтобы вызвать новое событие и оповестить об этом все модули,
используется метод Core.raise_event(event), где event - событие (один из
вариантов класса-перечисления Event). Также для удобства имеется декоратор
event.apply_event, который оборачивает нужный метод ядра так, чтобы после его
вызова сразу вызывалось указанное событие.

## Технологии, используемые в проекте

Проект написан целиком и полностью на Python с использованием PyQt5 в качестве
графической библиотеки.

Используемые библиотеки:
- PyQt5: Обертка Qt для создания GUI программ
- QScintilla: Обертка Scintilla для создания функциональных буферов
редактирования кода
- charset-normalizer: Библиотека, используемая для определения кодировки
открытого файла

## Автор проекта

Автор проекта: Меликсетян Марк <markmelix@gmail.com>

## Лицензия

[MIT](LICENSE)
Modular code editor with graphical user interface. In the editor you can create, save and switch between files using tabbar. There's also syntax highlighting and turnable statusbar displaying information about current opened file. There're 6 modules out-of-the box, 3 of which are customizable.

Main editor features:
- Creation, saving and switching between files.
- Syntax highlighting, line numbers, current line highlighting
- Theme choosing and editor appearance customization
- Configuration of the shown statusbar blocks
- Exporting and importing editor settings
- Enabling, disabling and configuring different program components

## Implementation

Now there're about 30 classes in the project. Every class's being used for implementation of a specific function.

### Description of the main classes

1. `Core`: editor core which controls all the editor processes. Every module has access to that class
2. `Event`: enumeration where all the possible events are specified
3. `Module`: determines every module as a set of the common methods giving an ability to interact with it through the common interface
4. `Buffer`: an abstract text buffer. The user can communicate with it using GuiBuffer
5. `BufferManager`: abstract buffer management-storage
6. `GuiBuffer`: a graphical text editing buffer. The user types text here
7. `Setting`: a common presenter of every module setting
8. `Settings`: independent widget through which the user can control module settings

### Event system

Whenever there's something to happen in the editor (e. g. a file gets opened), just after that a new event is going to be appended to the special list (`Core.events`) and all editor refresh methods (`Module.refresh`) get triggered. The system linking all these processes is an Event-system. The Event-system isn't represented as a separate class, but instead it's injected right into the editor core and one can interact with it through special Core methods.

To raise a new event and notify all the modules about that `Core.raise_event(event)` method's used, where event is one of the `Event` enumeration variants. Also there's a convenient decorator `Event.apply_event` which wraps the needed `Core` method so that after one gets called the applied events gets raised.

### Project stack

The project is written on the Python completely with the use of PyQt5 GUI library.

Used libraries:
- PyQt5: Qt wrapper for GUI programs creation
- QScintilla: Scintilla wrapper for functional text editing buffer creation
- charset-normalizer: library used to determine opened file encoding

## Project author

Mark Meliksetyan <markmelix@gmail.com>

## License

[MIT](License)
76 changes: 36 additions & 40 deletions src/buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,41 @@

from utils import FileType

DEFAULT_EMPTY_BUFFER_NAME = "Безымянный"
DEFAULT_EMPTY_BUFFER_NAME = "Unnamed"

# Перечисление возможных синхронизаций. Сихронизировать можно либо текст буфера
# с файлом (Sync.TO_FILE), либо содержимое файла с буфером (Sync.FROM_FILE)
# We can either sync text of the buffer with the file (Sync.TO_FILE) or file
# content with the buffer (Sync.FROM_FILE)
Sync = Enum("Sync", ["FROM_FILE", "TO_FILE"])


class BufferException:
"""Класс всех исключений, связанных с буферами"""
pass


class NoSyncFileError(BufferException):
"""Исключение, вызываемое при попытке синхронизировать текстовый буфер с не
указанным файлом синхронизации"""
"""Exception raised when trying syncing text buffer with unpointed sync
file"""


class EncodingGuessError(BufferException):
"""Исключение, вызываемое при неудачной попытке определить кодировку файла"""
"""Exception raised with failed attempt of file encoding guess"""


class Buffer:
"""Абстрактный текстовый буфер редактора"""
"""Abstract text editing buffer"""

def __init__(
self,
empty_name=DEFAULT_EMPTY_BUFFER_NAME,
sync_file=None,
text="",
):
"""Инициализирует буфер
"""Initializes a buffer
Параметры:
empty_name - название буфера, не имеющего файла для синхронизации
sync_file - файл для синхронизации
text - начальный текст буфера
Params:
empty_name - name of the buffer without file to sync with
sync_file - file to sync with
text - starting content of the buffer
"""

Expand All @@ -64,15 +64,12 @@ def set_sync_file(self, file):
self.file_encoding = self.determine_encoding()

def set_text(self, text):
"""Устанавливает текст буфера"""

self.text = text
self.desync()

def determine_encoding(self):
"""Пытается определить кодировку привязанного к буферу файла
сихнронизации. Вызывает исключение NoSyncFileError, если файл для
синхронизации не установлен"""
"""Tries to guess the encoding of the linked sync file. Raises
NoSyncFileError if there's no sync file linked within buffer"""

if self.file is None:
raise NoSyncFileError
Expand All @@ -85,10 +82,10 @@ def determine_encoding(self):
return guess.encoding

def sync(self, kind=Sync.TO_FILE):
"""Синхронизирует текст буфера с файлом (kind=Sync.TO_FILE), либо
содержимое файла с буфером (kind=Sync.FROM_FILE). Вызывает исключение
NoSyncFileError, если файл для синхронизации не установлен или
EncodingGuessError, если не удалось определить его кодировку"""
"""Synchronizes text of the buffer with a file content
(kind=Sync.TO_FILE) or file content with the text of the buffer
(kind=Sync.FROM_FILE). Raises NoSyncFileError if there's no linked sync
file or EncodingGuessError if failed to determine file encoding"""

if self.file is None:
raise NoSyncFileError
Expand Down Expand Up @@ -116,46 +113,45 @@ def sync(self, kind=Sync.TO_FILE):
self.refresh_name()

def _sync(self):
"""Устанавливает флаг синхронизации буфера с файлом синхронизации в
True. Данный метод должен быть использован только классом Buffer либо в
исключительных случаях, чтобы по особому обработать буфер"""
"""Sets buffer synchronization flag to True. This method should be used
only with Buffer class or in the special cases to handle buffer
specifically"""

self.synchronized = True

def desync(self):
"""Десинхронизирует буфер с файлом синхронизации"""
"""Desyncs buffer with the linked sync file"""
self.synchronized = False

def refresh_name(self):
"""Обновляет имя буфера в соответствии с именем файла синхронизации"""
"""Refreshes name of the buffer according to the name of the linked sync file"""
self.name = self.empty_name if self.file is None else self.file.split("/")[-1]

def file_type(self):
"""Возвращает вариант перечисления FileType в соответствии с расширением
прикрепленного к буферу файла синхронизации"""
"""Returns variant of the FileType enum according to the extension of
the linked sync file"""
try:
return FileType.from_ext(self.file.split(".")[-1])
except:
return None

def is_empty(self):
"""Возвращает True, если буфер не имеет прикрепленного файла и является
пустым, иначе - False"""
"""Returns whether the buffer has no linked file and is empty"""
return self.name == self.empty_name and self.text == ""


class BufManager:
"""Менеджер управления текстовыми буферами"""
"""Text buffer manager"""

def __init__(self):
self.buffers = {}

def add(self, gui_link, *args, **kwargs):
"""Добавить буфер с заданными параметрами.
"""Add a buffer with the defined params
Параметры:
gui_link - ссылка на графический буфер, с которым
необходимо связать созданный абстрактный буфер
Params:
gui_link - link to the graphical buffer to link just created
abstract buffer
"""

self.buffers[gui_link] = Buffer(*args, **kwargs)
Expand All @@ -168,11 +164,11 @@ def switch(self, gui_link):
self.current_link = gui_link

def add_empty(self, gui_link):
"""Добавить пустой буфер"""
"""Add empty buffer"""
self.add(gui_link)

def remove(self, gui_link, new_current=None):
"""Удаляет буфер"""
"""Removes buffer"""
del self.buffers[gui_link]

if gui_link != self.current_link:
Expand All @@ -187,12 +183,12 @@ def __len__(self):
return len(self.buffers)

def __getitem__(self, gui_link):
"""Возвращает буфер, привязанный к данному графическому буферу"""
"""Returns buffer linked with the current graphical one"""
return self.buffers[gui_link]


class GuiBuffer:
"""Общий класс графических буферов"""
"""General graphical buffer class"""

def __init__(self):
self.buffer = ""
Expand Down
Loading

0 comments on commit 5121fc5

Please sign in to comment.