set separator by section

pull/10980/head
Sergio Ribera 5 months ago
parent dbacaaddca
commit d1e955e38e

@ -79,6 +79,9 @@ The `[editor.statusline]` key takes the following sub-keys:
| `center` | A list of elements aligned to the middle of the statusline | `[]` | | `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"]` | | `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` | 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.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.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"` | | `mode.select` | The text shown in the `mode` element for select mode | `"SEL"` |
@ -104,7 +107,7 @@ The following statusline elements can be configured:
| `primary-selection-length` | The number of characters currently in primary selection | | `primary-selection-length` | The number of characters currently in primary selection |
| `position` | The cursor position | | `position` | The cursor position |
| `position-percentage` | The cursor position as a percentage of the total number of lines | | `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) | | `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 | | `version-control` | The current branch name or detached commit hash of the opened workspace |
| `register` | The current selected register | | `register` | The current selected register |

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

@ -460,7 +460,7 @@ pub struct StatusLineConfig {
pub left: Vec<StatusLineElement>, pub left: Vec<StatusLineElement>,
pub center: Vec<StatusLineElement>, pub center: Vec<StatusLineElement>,
pub right: Vec<StatusLineElement>, pub right: Vec<StatusLineElement>,
pub separator: String, pub separator: StatusLineSeparator,
pub mode: ModeConfig, pub mode: ModeConfig,
} }
@ -484,7 +484,7 @@ impl Default for StatusLineConfig {
E::Position, E::Position,
E::FileEncoding, E::FileEncoding,
], ],
separator: String::from("│"), separator: StatusLineSeparator::default(),
mode: ModeConfig::default(), mode: ModeConfig::default(),
} }
} }
@ -575,6 +575,135 @@ pub enum StatusLineElement {
Register, 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 // 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. // to be fast. Therefore we avoid a hashmap and use an enum indexed array.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

Loading…
Cancel
Save