gutter and keys
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
//! Иконки на поле редактора (gutter).
|
||||
//!
|
||||
//! Gutter — узкая колонка слева от редактора. Сценарий ставит на строку
|
||||
//! иконку через `app:set_gutter_icon`, а вид рисует её на той же высоте, что и
|
||||
//! строка в редакторе, учитывая прокрутку.
|
||||
//!
|
||||
//! Состояние принадлежит документу: у каждой вкладки свои иконки, и при
|
||||
//! переключении вкладок показываются иконки активного документа. Номера строк
|
||||
//! нумеруются с нуля и не сдвигаются автоматически при правке текста:
|
||||
//! сценарий сам решает, когда переставить иконку.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use gpui_kit::{Hsla, Pixels};
|
||||
|
||||
/// Иконка одной строки.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct GutterMarker {
|
||||
/// Каноническое имя иконки Lucide.
|
||||
pub icon: String,
|
||||
/// Цвет иконки; `None` — цвет текста темы.
|
||||
pub color: Option<Hsla>,
|
||||
}
|
||||
|
||||
/// Иконки gutter документа по номеру строки.
|
||||
///
|
||||
/// Одна строка несёт не больше одной иконки: повторный вызов заменяет её.
|
||||
/// `BTreeMap` держит строки отсортированными, поэтому вид рисует их сверху
|
||||
/// вниз без дополнительной сортировки.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct GutterState {
|
||||
markers: BTreeMap<usize, GutterMarker>,
|
||||
}
|
||||
|
||||
impl GutterState {
|
||||
/// Создаёт пустое поле.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Ставит иконку на строку `line`, заменяя прежнюю.
|
||||
///
|
||||
/// Имя иконки проверяет API: здесь хранится уже каноническое имя.
|
||||
pub fn set(&mut self, line: usize, icon: impl Into<String>, color: Option<Hsla>) {
|
||||
self.markers.insert(
|
||||
line,
|
||||
GutterMarker {
|
||||
icon: icon.into(),
|
||||
color,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Убирает иконку со строки.
|
||||
///
|
||||
/// Возвращает `false`, если на этой строке иконки не было.
|
||||
pub fn remove(&mut self, line: usize) -> bool {
|
||||
self.markers.remove(&line).is_some()
|
||||
}
|
||||
|
||||
/// Убирает все иконки.
|
||||
pub fn clear(&mut self) {
|
||||
self.markers.clear();
|
||||
}
|
||||
|
||||
/// Иконка строки, если она есть.
|
||||
pub fn get(&self, line: usize) -> Option<&GutterMarker> {
|
||||
self.markers.get(&line)
|
||||
}
|
||||
|
||||
/// Иконки в порядке возрастания номеров строк.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (usize, &GutterMarker)> {
|
||||
self.markers.iter().map(|(line, marker)| (*line, marker))
|
||||
}
|
||||
}
|
||||
|
||||
/// Вертикальный отступ видимой строки `row` от верхнего края gutter.
|
||||
///
|
||||
/// `scroll_y` — смещение прокрутки редактора (отрицательное при прокрутке
|
||||
/// вниз), `line_height` — высота строки из раскладки редактора. `row` — не
|
||||
/// номер строки буфера, а номер видимой строки: свёрнутые строки не занимают
|
||||
/// места.
|
||||
pub fn row_offset(row: usize, line_height: Pixels, scroll_y: Pixels) -> Pixels {
|
||||
scroll_y + line_height * row as f32
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use gpui_kit::{hsla, px};
|
||||
|
||||
#[test]
|
||||
fn markers_are_keyed_by_line_and_sorted() {
|
||||
let mut gutter = GutterState::new();
|
||||
assert_eq!(gutter.iter().count(), 0);
|
||||
|
||||
gutter.set(5, "file", None);
|
||||
gutter.set(1, "play", Some(hsla(0.5, 1., 0.5, 1.)));
|
||||
let lines: Vec<usize> = gutter.iter().map(|(line, _)| line).collect();
|
||||
assert_eq!(lines, [1, 5]);
|
||||
assert_eq!(
|
||||
gutter.get(1).map(|marker| marker.icon.as_str()),
|
||||
Some("play")
|
||||
);
|
||||
|
||||
assert!(gutter.remove(1));
|
||||
assert!(!gutter.remove(1));
|
||||
assert!(gutter.get(1).is_none());
|
||||
assert_eq!(gutter.iter().count(), 1);
|
||||
|
||||
gutter.clear();
|
||||
assert_eq!(gutter.iter().count(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_offset_follows_scroll_and_line_height() {
|
||||
assert_eq!(row_offset(0, px(20.), px(0.)), px(0.));
|
||||
assert_eq!(row_offset(3, px(20.), px(-40.)), px(20.));
|
||||
assert_eq!(row_offset(1, px(18.), px(-9.)), px(9.));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user