refactoring
This commit is contained in:
+53
-62
@@ -25,17 +25,6 @@ pub enum Language {
|
||||
}
|
||||
|
||||
impl Language {
|
||||
/// Все поддерживаемые языки.
|
||||
#[allow(dead_code)]
|
||||
// Часть публичного интерфейса модуля: удобно для перебора языков снаружи.
|
||||
pub const ALL: [Self; 5] = [
|
||||
Self::Rust,
|
||||
Self::Go,
|
||||
Self::JavaScript,
|
||||
Self::Json,
|
||||
Self::Python,
|
||||
];
|
||||
|
||||
/// Разбирает название языка из API/сценария.
|
||||
///
|
||||
/// Основные имена — `rust`, `go`, `js`, `json`, `python`; короткие
|
||||
@@ -62,25 +51,12 @@ impl Language {
|
||||
}
|
||||
}
|
||||
|
||||
/// Имя языка для `EditorState::set_highlighter`.
|
||||
/// Полное имя языка для tree-sitter и LSP.
|
||||
///
|
||||
/// Здесь `js` разворачивается в `javascript`: так грамматика находится
|
||||
/// без дополнительных псевдонимов на нашей стороне.
|
||||
pub fn highlight_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Rust => "rust",
|
||||
Self::Go => "go",
|
||||
Self::JavaScript => "javascript",
|
||||
Self::Json => "json",
|
||||
Self::Python => "python",
|
||||
}
|
||||
}
|
||||
|
||||
/// Идентификатор языка для LSP (`languageId` в `textDocument/didOpen`).
|
||||
///
|
||||
/// Отличается от [`Self::name`] тем же, чем `highlight_name`: LSP ждёт
|
||||
/// `javascript`, а не `js`.
|
||||
pub fn lsp_name(self) -> &'static str {
|
||||
/// Отличается от [`Self::name`] только для JavaScript: грамматика и
|
||||
/// `languageId` используют `javascript`, а короткое `js` остаётся именем
|
||||
/// в Lua-API.
|
||||
pub fn syntax_name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Rust => "rust",
|
||||
Self::Go => "go",
|
||||
@@ -188,17 +164,35 @@ impl Document {
|
||||
/// по расширению имени вкладки — так LSP работает и без `set_language`.
|
||||
pub fn lsp_language_id(&self) -> String {
|
||||
if let Some(language) = self.language {
|
||||
return language.lsp_name().to_owned();
|
||||
return language.syntax_name().to_owned();
|
||||
}
|
||||
language_id_from_path(&self.name).to_owned()
|
||||
}
|
||||
|
||||
/// Имя иконки вкладки с учётом иконки по умолчанию.
|
||||
pub fn icon_name(&self) -> &str {
|
||||
self.icon
|
||||
.as_deref()
|
||||
.unwrap_or(crate::icons::DEFAULT_TAB_ICON)
|
||||
}
|
||||
|
||||
/// Помечает текущее содержимое сохранённым.
|
||||
pub fn mark_saved(&mut self) {
|
||||
self.saved_content = self.content.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// Что сделать с фокусом редактора после синхронизации модели с видом.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum FocusAfterSync {
|
||||
/// Оставить фокус там, где он был (`app:open` без позиции).
|
||||
Keep,
|
||||
/// Перевести фокус в редактор и, если задано, поставить курсор.
|
||||
///
|
||||
/// Смещение задаётся в байтах; `None` — оставить текущую позицию.
|
||||
Editor(Option<usize>),
|
||||
}
|
||||
|
||||
/// Открытые документы и активная вкладка.
|
||||
///
|
||||
/// Инвариант: `documents` никогда не пуст — редактор всегда показывает хотя бы
|
||||
@@ -207,6 +201,8 @@ pub struct Workspace {
|
||||
documents: Vec<Document>,
|
||||
active: usize,
|
||||
next_untitled: u32,
|
||||
/// Действие с фокусом, ожидающее ближайшей синхронизации вида.
|
||||
focus_after_sync: Option<FocusAfterSync>,
|
||||
/// Номер «поколения» состояния.
|
||||
///
|
||||
/// Вид перечитывает активный документ в редактор только когда ревизия
|
||||
@@ -218,12 +214,10 @@ pub struct Workspace {
|
||||
impl Default for Workspace {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
documents: vec![
|
||||
Document::new("main.rs", SAMPLE_RUST),
|
||||
Document::new("notes.md", SAMPLE_NOTES),
|
||||
],
|
||||
documents: vec![Document::new("untitled-1.txt", "")],
|
||||
active: 0,
|
||||
next_untitled: 0,
|
||||
next_untitled: 1,
|
||||
focus_after_sync: None,
|
||||
revision: 0,
|
||||
}
|
||||
}
|
||||
@@ -261,6 +255,16 @@ impl Workspace {
|
||||
self.revision
|
||||
}
|
||||
|
||||
/// Откладывает действие с фокусом до ближайшей синхронизации вида.
|
||||
pub fn set_focus_after_sync(&mut self, focus: FocusAfterSync) {
|
||||
self.focus_after_sync = Some(focus);
|
||||
}
|
||||
|
||||
/// Забирает отложенное действие с фокусом.
|
||||
pub fn take_focus_after_sync(&mut self) -> Option<FocusAfterSync> {
|
||||
self.focus_after_sync.take()
|
||||
}
|
||||
|
||||
/// Сообщает виду, что документы нужно перечитать.
|
||||
fn bump(&mut self) {
|
||||
self.revision = self.revision.wrapping_add(1);
|
||||
@@ -274,6 +278,17 @@ impl Workspace {
|
||||
}
|
||||
}
|
||||
|
||||
/// Переключает активную вкладку на `delta` позиций по кругу.
|
||||
///
|
||||
/// Возвращает новый индекс. Инвариант «хотя бы один документ» делает
|
||||
/// деление по модулю безопасным.
|
||||
pub fn cycle_active(&mut self, delta: isize) -> usize {
|
||||
let count = self.documents.len() as isize;
|
||||
let next = (self.active as isize + delta).rem_euclid(count) as usize;
|
||||
self.set_active(next);
|
||||
next
|
||||
}
|
||||
|
||||
/// Открывает пустую вкладку и делает её активной.
|
||||
pub fn open_new(&mut self) {
|
||||
self.next_untitled += 1;
|
||||
@@ -333,7 +348,9 @@ impl Workspace {
|
||||
|
||||
if self.documents.is_empty() {
|
||||
self.open_new();
|
||||
} else if index < self.active {
|
||||
return;
|
||||
}
|
||||
if index < self.active {
|
||||
self.active -= 1;
|
||||
} else if index == self.active {
|
||||
self.active = self.active.min(self.documents.len() - 1);
|
||||
@@ -417,30 +434,4 @@ impl Workspace {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Имя иконки вкладки, если оно назначено.
|
||||
pub fn tab_icon(&self, index: usize) -> Option<&str> {
|
||||
self.document(index)
|
||||
.and_then(|document| document.icon.as_deref())
|
||||
}
|
||||
}
|
||||
|
||||
const SAMPLE_RUST: &str = r#"//! Демонстрационный файл.
|
||||
// Правьте код прямо в редакторе: вкладки и нумерация строк уже здесь.
|
||||
|
||||
fn main() {
|
||||
let mut numbers = vec![3, 1, 2];
|
||||
numbers.sort();
|
||||
|
||||
for (i, n) in numbers.iter().enumerate() {
|
||||
println!("{i}: {n}");
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
const SAMPLE_NOTES: &str = r#"# Заметки
|
||||
|
||||
- «+» на панели вкладок открывает новый файл
|
||||
- крестик на вкладке закрывает её
|
||||
- Ctrl+S сохраняет вкладку в файл с её названием
|
||||
"#;
|
||||
|
||||
Reference in New Issue
Block a user