set separator by section

This commit is contained in:
Sergio Ribera 2024-06-17 17:50:41 -04:00
parent dbacaaddca
commit d1e955e38e
3 changed files with 162 additions and 6 deletions

View File

@ -79,6 +79,9 @@ ### `[editor.statusline]` Section
| `center` | A list of elements aligned to the middle of the statusline | `[]` |
| `right` | A list of elements aligned to the right of the statusline | `["diagnostics", "selections", "register", "position", "file-encoding"]` |
| `separator` | The character used to separate elements in the statusline | `"│"` |
| `separator.left` | The character used to separate elements in the left statusline | `"│"` |
| `separator.center` | The character used to separate elements in the center statusline | `"│"` |
| `separator.right` | The character used to separate elements in the right statusline | `"│"` |
| `mode.normal` | The text shown in the `mode` element for normal mode | `"NOR"` |
| `mode.insert` | The text shown in the `mode` element for insert mode | `"INS"` |
| `mode.select` | The text shown in the `mode` element for select mode | `"SEL"` |
@ -104,7 +107,7 @@ ### `[editor.statusline]` Section
| `primary-selection-length` | The number of characters currently in primary selection |
| `position` | The cursor position |
| `position-percentage` | The cursor position as a percentage of the total number of lines |
| `separator` | The string defined in `editor.statusline.separator` (defaults to `"│"`) |
| `separator` | The string defined in `editor.statusline.separator` (defaults to `"│"`) (`separator`/`separator.left`/`separator.center`/`separator.right`/) |
| `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) |
| `version-control` | The current branch name or detached commit hash of the opened workspace |
| `register` | The current selected register |

View File

@ -15,6 +15,7 @@
use tui::text::{Span, Spans};
pub struct RenderContext<'a> {
pub position: RenderPosition,
pub editor: &'a Editor,
pub doc: &'a Document,
pub view: &'a View,
@ -37,11 +38,20 @@ pub fn new(
view,
focused,
spinners,
position: RenderPosition::default(),
parts: RenderBuffer::default(),
}
}
}
#[derive(Default)]
pub enum RenderPosition {
#[default]
Left,
Center,
Right,
}
#[derive(Default)]
pub struct RenderBuffer<'a> {
pub left: Spans<'a>,
@ -76,7 +86,10 @@ pub fn render(context: &mut RenderContext, viewport: Rect, surface: &mut Surface
element_ids
.iter()
.map(|element_id| get_render_function(*element_id))
.for_each(|render| render(context, write_left));
.for_each(|render| {
context.position = RenderPosition::Left;
render(context, write_left)
});
surface.set_spans(
viewport.x,
@ -91,7 +104,10 @@ pub fn render(context: &mut RenderContext, viewport: Rect, surface: &mut Surface
element_ids
.iter()
.map(|element_id| get_render_function(*element_id))
.for_each(|render| render(context, write_right));
.for_each(|render| {
context.position = RenderPosition::Right;
render(context, write_right)
});
surface.set_spans(
viewport.x
@ -109,7 +125,10 @@ pub fn render(context: &mut RenderContext, viewport: Rect, surface: &mut Surface
element_ids
.iter()
.map(|element_id| get_render_function(*element_id))
.for_each(|render| render(context, write_center));
.for_each(|render| {
context.position = RenderPosition::Center;
render(context, write_center)
});
// Width of the empty space between the left and center area and between the center and right area.
let spacing = 1u16;
@ -495,6 +514,11 @@ fn render_separator<F>(context: &mut RenderContext, write: F)
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
{
let sep = &context.editor.config().statusline.separator;
let sep = match context.position {
RenderPosition::Left => &sep.left,
RenderPosition::Center => &sep.center,
RenderPosition::Right => &sep.right,
};
write(
context,

View File

@ -460,7 +460,7 @@ pub struct StatusLineConfig {
pub left: Vec<StatusLineElement>,
pub center: Vec<StatusLineElement>,
pub right: Vec<StatusLineElement>,
pub separator: String,
pub separator: StatusLineSeparator,
pub mode: ModeConfig,
}
@ -484,7 +484,7 @@ fn default() -> Self {
E::Position,
E::FileEncoding,
],
separator: String::from(""),
separator: StatusLineSeparator::default(),
mode: ModeConfig::default(),
}
}
@ -575,6 +575,135 @@ pub enum StatusLineElement {
Register,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "kebab-case", default)]
pub struct StatusLineSeparator {
pub left: String,
pub center: String,
pub right: String,
}
impl From<String> for StatusLineSeparator {
fn from(v: String) -> Self {
Self {
left: v.clone(),
center: v.clone(),
right: v,
}
}
}
impl Default for StatusLineSeparator {
fn default() -> Self {
String::from("").into()
}
}
enum StatusLineSeparatorField {
Left,
Center,
Right,
}
struct StatusLineSeparatorVisitor;
impl<'de> serde::de::Visitor<'de> for StatusLineSeparatorVisitor {
type Value = StatusLineSeparator;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string or a map with keys 'left', 'center', and 'right'")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(StatusLineSeparator::from(value.to_string()))
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: serde::de::MapAccess<'de>,
{
let mut left = None;
let mut center = None;
let mut right = None;
while let Some(key) = map.next_key()? {
match key {
StatusLineSeparatorField::Left => {
if left.is_some() {
return Err(serde::de::Error::duplicate_field("left"));
}
left = Some(map.next_value()?);
}
StatusLineSeparatorField::Center => {
if center.is_some() {
return Err(serde::de::Error::duplicate_field("center"));
}
center = Some(map.next_value()?);
}
StatusLineSeparatorField::Right => {
if right.is_some() {
return Err(serde::de::Error::duplicate_field("right"));
}
right = Some(map.next_value()?);
}
}
}
let left = left.unwrap_or_else(|| String::from(""));
let center = center.unwrap_or_else(|| String::from(""));
let right = right.unwrap_or_else(|| String::from(""));
Ok(StatusLineSeparator {
left,
center,
right,
})
}
}
impl<'de> Deserialize<'de> for StatusLineSeparatorField {
fn deserialize<D>(deserializer: D) -> Result<StatusLineSeparatorField, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = StatusLineSeparatorField;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("`left`, `center`, or `right`")
}
fn visit_str<E>(self, value: &str) -> Result<StatusLineSeparatorField, E>
where
E: serde::de::Error,
{
match value {
"left" => Ok(StatusLineSeparatorField::Left),
"center" => Ok(StatusLineSeparatorField::Center),
"right" => Ok(StatusLineSeparatorField::Right),
_ => Err(serde::de::Error::unknown_field(value, &["left", "center", "right"])),
}
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
impl<'de> Deserialize<'de> for StatusLineSeparator {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(StatusLineSeparatorVisitor)
}
}
// Cursor shape is read and used on every rendered frame and so needs
// to be fast. Therefore we avoid a hashmap and use an enum indexed array.
#[derive(Debug, Clone, PartialEq, Eq)]