fix: lead the bottom position indicator with one margin column
This commit is contained in:
@@ -21,7 +21,9 @@
|
|||||||
- [x] add `C` and `D` for deleting line from current cursor position (and entering insert mode: `C`); add `Y` for copying line from current cursor position; add `r` for replacing character at cursor position; add `s` for deleting character at current cursor position and entering insert mode; add `d<count>w` for deleting whole words; add `d<count>b` for deleting backwards (same as `d<count>w` but backwards).
|
- [x] add `C` and `D` for deleting line from current cursor position (and entering insert mode: `C`); add `Y` for copying line from current cursor position; add `r` for replacing character at cursor position; add `s` for deleting character at current cursor position and entering insert mode; add `d<count>w` for deleting whole words; add `d<count>b` for deleting backwards (same as `d<count>w` but backwards).
|
||||||
- [x] diff view: make foreground of etched areas (where no lines exist) the same dimmed color as the normal gutter text
|
- [x] diff view: make foreground of etched areas (where no lines exist) the same dimmed color as the normal gutter text
|
||||||
- [x] copying is pretty slow; optimize performance to reduce latency.
|
- [x] copying is pretty slow; optimize performance to reduce latency.
|
||||||
- [x] enable margins in bar elements (i.e. for top and bottom bars) and give the lsp symbol (next to language) a default margin of 1 column on either side.
|
- [x] enable margins in top/bottom bar elements; give the bottom bar's far-right position indicator a default leading margin of 1 column and no trailing margin.
|
||||||
|
- [ ] better fuzzy search when searching revisions and bookmarks (or the git alternatives) in before and after picker for diff view.
|
||||||
|
- [ ] buffer picker (with search).
|
||||||
- [ ] pull a pr description and display it in a buffer:
|
- [ ] pull a pr description and display it in a buffer:
|
||||||
- open a selector that allows you to pick a pr from remote
|
- open a selector that allows you to pick a pr from remote
|
||||||
- selector should be searchable
|
- selector should be searchable
|
||||||
|
|||||||
@@ -68,8 +68,7 @@ Vamp.configure do |config|
|
|||||||
left: [:mode, :path, :diagnostics, :selections, :count, :pending, :buffer],
|
left: [:mode, :path, :diagnostics, :selections, :count, :pending, :buffer],
|
||||||
center: [], right: [:language, :lsp, :position],
|
center: [], right: [:language, :lsp, :position],
|
||||||
mode: { padding: 1, leading_edge: "", trailing_edge: "▓░" },
|
mode: { padding: 1, leading_edge: "", trailing_edge: "▓░" },
|
||||||
lsp: { margin: 1 },
|
position: { padding: 1, leading_margin: 1, trailing_margin: 0, leading_edge: "░▓", trailing_edge: "" }
|
||||||
position: { padding: 1, leading_edge: "░▓", trailing_edge: "" }
|
|
||||||
}
|
}
|
||||||
config.bar.bottom = {
|
config.bar.bottom = {
|
||||||
left: [:tabs], center: [], right: [:battery],
|
left: [:tabs], center: [], right: [:battery],
|
||||||
@@ -92,7 +91,7 @@ Each segment's `title` is a literal template supporting only `{index}`, `{view}`
|
|||||||
|
|
||||||
`:lsp` displays only ready servers assigned to the focused buffer, in configured stack order joined with ` + `. Disabled, initializing, failed, or unassigned servers do not appear. Names come from the initialization reply's `serverInfo.name`, falling back to the configured executable basename; no arbitrary named label can make an inactive server appear active.
|
`:lsp` displays only ready servers assigned to the focused buffer, in configured stack order joined with ` + `. Disabled, initializing, failed, or unassigned servers do not appear. Names come from the initialization reply's `serverInfo.name`, falling back to the configured executable basename; no arbitrary named label can make an inactive server appear active.
|
||||||
|
|
||||||
`margin` adds unstyled space outside both edges of a segment, unlike the styled internal `padding`. It accepts integers from `0` through `64`, defaults to `0`, and defaults to `1` for `:lsp` in either bar. For example, `config.bar.bottom = { lsp: { margin: 0 } }` removes LSP's external spacing without changing its padding. Margins count toward anchor placement and collision handling; empty segments consume no margin, and margins yield along with padding and edges when only clipped content fits.
|
`margin` adds unstyled space outside both edges of a segment, unlike the styled internal `padding`. `leading_margin` and `trailing_margin` override their respective sides. All accept integers from `0` through `64`. Margins default to zero except the bottom bar's `:position`, which has one leading column and no trailing margin. LSP has no default margins. For example, `config.bar.bottom = { position: { leading_margin: 0 } }` removes position's external spacing without changing its padding. Margins count toward anchor placement and collision handling; empty segments consume no margin, and margins yield along with padding and edges when only clipped content fits.
|
||||||
|
|
||||||
`padding` adds spaces inside the segment's styling, on both sides of its title, and accepts integers from `0` through `64` (default `1`). `leading_edge` and `trailing_edge` are separate styled glyph strings, not padding; `""` removes an edge. Tabs default to `"░▓"` / `"▓░"` edges; mode has only the trailing `"▓░"` edge, position and battery only the leading `"░▓"` edge, and other segments have none. `first_leading_edge` optionally overrides the first occurrence's leading glyphs: for tabs this is the actual first tab, not the first tab remaining after clipping; other segments have one occurrence. Tabs default this override to `""`, suppressing the first tab's left fade. Unicode widths are measured in terminal cells. Edge strings must be single-line and contain no control characters. Unknown style keys, invalid templates, and wrong types reject configuration.
|
`padding` adds spaces inside the segment's styling, on both sides of its title, and accepts integers from `0` through `64` (default `1`). `leading_edge` and `trailing_edge` are separate styled glyph strings, not padding; `""` removes an edge. Tabs default to `"░▓"` / `"▓░"` edges; mode has only the trailing `"▓░"` edge, position and battery only the leading `"░▓"` edge, and other segments have none. `first_leading_edge` optionally overrides the first occurrence's leading glyphs: for tabs this is the actual first tab, not the first tab remaining after clipping; other segments have one occurrence. Tabs default this override to `""`, suppressing the first tab's left fade. Unicode widths are measured in terminal cells. Edge strings must be single-line and contain no control characters. Unknown style keys, invalid templates, and wrong types reject configuration.
|
||||||
|
|
||||||
|
|||||||
+23
-8
@@ -106,7 +106,7 @@ pub struct ChromeLine {
|
|||||||
pub struct SegmentConfig {
|
pub struct SegmentConfig {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub padding: usize,
|
pub padding: usize,
|
||||||
pub margin: usize,
|
pub margin: [usize; 2],
|
||||||
pub leading_edge: String,
|
pub leading_edge: String,
|
||||||
pub trailing_edge: String,
|
pub trailing_edge: String,
|
||||||
pub first_leading_edge: Option<String>,
|
pub first_leading_edge: Option<String>,
|
||||||
@@ -117,7 +117,7 @@ impl Default for SegmentConfig {
|
|||||||
Self {
|
Self {
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
padding: 1,
|
padding: 1,
|
||||||
margin: 0,
|
margin: [0; 2],
|
||||||
leading_edge: String::new(),
|
leading_edge: String::new(),
|
||||||
trailing_edge: String::new(),
|
trailing_edge: String::new(),
|
||||||
first_leading_edge: None,
|
first_leading_edge: None,
|
||||||
@@ -148,7 +148,13 @@ impl ChromeLine {
|
|||||||
right: vec!["language".into(), "lsp".into(), "position".into()],
|
right: vec!["language".into(), "lsp".into(), "position".into()],
|
||||||
segments: DEFAULT_BOTTOM_SEGMENTS
|
segments: DEFAULT_BOTTOM_SEGMENTS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| ((*name).into(), default_segment(name)))
|
.map(|name| {
|
||||||
|
let mut segment = default_segment(name);
|
||||||
|
if *name == "position" {
|
||||||
|
segment.margin[0] = 1;
|
||||||
|
}
|
||||||
|
((*name).into(), segment)
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +175,11 @@ impl ChromeLine {
|
|||||||
validate_name(name)?;
|
validate_name(name)?;
|
||||||
}
|
}
|
||||||
for (name, segment) in &self.segments {
|
for (name, segment) in &self.segments {
|
||||||
for (option, value) in [("padding", segment.padding), ("margin", segment.margin)] {
|
for (option, value) in [
|
||||||
|
("padding", segment.padding),
|
||||||
|
("leading_margin", segment.margin[0]),
|
||||||
|
("trailing_margin", segment.margin[1]),
|
||||||
|
] {
|
||||||
if value > 64 {
|
if value > 64 {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Chrome segment {name:?} {option} must be between 0 and 64"
|
"Chrome segment {name:?} {option} must be between 0 and 64"
|
||||||
@@ -202,9 +212,6 @@ impl ChromeLine {
|
|||||||
|
|
||||||
fn default_segment(name: &str) -> SegmentConfig {
|
fn default_segment(name: &str) -> SegmentConfig {
|
||||||
let mut segment = SegmentConfig::default();
|
let mut segment = SegmentConfig::default();
|
||||||
if name == "lsp" {
|
|
||||||
segment.margin = 1;
|
|
||||||
}
|
|
||||||
if matches!(name, "tabs" | "position" | "battery") {
|
if matches!(name, "tabs" | "position" | "battery") {
|
||||||
segment.leading_edge = "░▓".into();
|
segment.leading_edge = "░▓".into();
|
||||||
}
|
}
|
||||||
@@ -266,6 +273,8 @@ struct SegmentOptions {
|
|||||||
title: Option<String>,
|
title: Option<String>,
|
||||||
padding: Option<usize>,
|
padding: Option<usize>,
|
||||||
margin: Option<usize>,
|
margin: Option<usize>,
|
||||||
|
leading_margin: Option<usize>,
|
||||||
|
trailing_margin: Option<usize>,
|
||||||
leading_edge: Option<String>,
|
leading_edge: Option<String>,
|
||||||
trailing_edge: Option<String>,
|
trailing_edge: Option<String>,
|
||||||
first_leading_edge: Option<String>,
|
first_leading_edge: Option<String>,
|
||||||
@@ -313,7 +322,13 @@ fn deserialize_line<'de, D: Deserializer<'de>>(
|
|||||||
segment.padding = padding;
|
segment.padding = padding;
|
||||||
}
|
}
|
||||||
if let Some(margin) = options.margin {
|
if let Some(margin) = options.margin {
|
||||||
segment.margin = margin;
|
segment.margin = [margin; 2];
|
||||||
|
}
|
||||||
|
if let Some(margin) = options.leading_margin {
|
||||||
|
segment.margin[0] = margin;
|
||||||
|
}
|
||||||
|
if let Some(margin) = options.trailing_margin {
|
||||||
|
segment.margin[1] = margin;
|
||||||
}
|
}
|
||||||
if let Some(edge) = options.leading_edge {
|
if let Some(edge) = options.leading_edge {
|
||||||
segment.leading_edge = edge;
|
segment.leading_edge = edge;
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ module Vamp
|
|||||||
chrome_name!(name)
|
chrome_name!(name)
|
||||||
style = chrome_hash!(value).to_h do |option, item|
|
style = chrome_hash!(value).to_h do |option, item|
|
||||||
case option
|
case option
|
||||||
when "padding", "margin"
|
when "padding", "margin", "leading_margin", "trailing_margin"
|
||||||
unless item.is_a?(Integer) && item.between?(0, 64)
|
unless item.is_a?(Integer) && item.between?(0, 64)
|
||||||
raise ArgumentError, "#{option} must be an Integer between 0 and 64"
|
raise ArgumentError, "#{option} must be an Integer between 0 and 64"
|
||||||
end
|
end
|
||||||
|
|||||||
+50
-10
@@ -1244,7 +1244,8 @@ fn chrome_items<'a>(
|
|||||||
for item in &mut items {
|
for item in &mut items {
|
||||||
item.width = item.text.width()
|
item.width = item.text.width()
|
||||||
+ item.config.padding * 2
|
+ item.config.padding * 2
|
||||||
+ item.config.margin * 2
|
+ item.config.margin[0]
|
||||||
|
+ item.config.margin[1]
|
||||||
+ if item.tab.is_none() || item.accent {
|
+ if item.tab.is_none() || item.accent {
|
||||||
item.leading_edge().width() + item.config.trailing_edge.width()
|
item.leading_edge().width() + item.config.trailing_edge.width()
|
||||||
} else {
|
} else {
|
||||||
@@ -1359,11 +1360,12 @@ fn draw_chrome(frame: &mut Frame, mut groups: [Vec<ChromeItem<'_>>; 3], area: Re
|
|||||||
const PADDING: &str =
|
const PADDING: &str =
|
||||||
" ";
|
" ";
|
||||||
let padding = &PADDING[..item.config.padding];
|
let padding = &PADDING[..item.config.padding];
|
||||||
let margin = &PADDING[..item.config.margin];
|
let leading_margin = &PADDING[..item.config.margin[0]];
|
||||||
|
let trailing_margin = &PADDING[..item.config.margin[1]];
|
||||||
let edges = item.tab.is_none() || item.accent;
|
let edges = item.tab.is_none() || item.accent;
|
||||||
let edge = Style::default().fg(Color::Cyan);
|
let edge = Style::default().fg(Color::Cyan);
|
||||||
Line::from(vec![
|
Line::from(vec![
|
||||||
Span::styled(margin, Style::reset()),
|
Span::styled(leading_margin, Style::reset()),
|
||||||
Span::styled(if edges { item.leading_edge() } else { "" }, edge),
|
Span::styled(if edges { item.leading_edge() } else { "" }, edge),
|
||||||
Span::styled(padding, style),
|
Span::styled(padding, style),
|
||||||
Span::styled(item.text.as_ref(), style),
|
Span::styled(item.text.as_ref(), style),
|
||||||
@@ -1376,7 +1378,7 @@ fn draw_chrome(frame: &mut Frame, mut groups: [Vec<ChromeItem<'_>>; 3], area: Re
|
|||||||
},
|
},
|
||||||
edge,
|
edge,
|
||||||
),
|
),
|
||||||
Span::styled(margin, Style::reset()),
|
Span::styled(trailing_margin, Style::reset()),
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
let cells = item_width.min(available);
|
let cells = item_width.min(available);
|
||||||
@@ -5975,6 +5977,44 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bottom_position_has_only_a_leading_margin_and_lsp_has_none() {
|
||||||
|
let mut editor = Editor::open(None, vec![]).unwrap();
|
||||||
|
editor.set_lsp_names(vec!["server".into()]);
|
||||||
|
configure_chrome(&mut editor, |top, bottom| {
|
||||||
|
top.left.clear();
|
||||||
|
top.center.clear();
|
||||||
|
top.right = vec!["position".into()];
|
||||||
|
bottom.left.clear();
|
||||||
|
bottom.center.clear();
|
||||||
|
bottom.right = vec!["lsp".into(), "position".into()];
|
||||||
|
});
|
||||||
|
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(40, 2)).unwrap();
|
||||||
|
terminal
|
||||||
|
.draw(|frame| {
|
||||||
|
for (line, y) in [
|
||||||
|
(editor.top_bar_config(), 0),
|
||||||
|
(editor.bottom_bar_config(), 1),
|
||||||
|
] {
|
||||||
|
let groups = chrome_groups(&editor, line, &SyntaxCache::new());
|
||||||
|
let start = 40 - chrome_group_width(&groups[2]) as u16;
|
||||||
|
if y == 1 {
|
||||||
|
let gap = start + groups[2][0].width as u16;
|
||||||
|
draw_chrome(frame, groups, Rect::new(0, y, 40, 1));
|
||||||
|
assert_eq!(frame.buffer_mut()[(start, y)].bg, Color::Cyan);
|
||||||
|
assert_eq!(frame.buffer_mut()[(gap, y)].symbol(), " ");
|
||||||
|
assert_eq!(frame.buffer_mut()[(gap, y)].bg, Color::Reset);
|
||||||
|
assert_eq!(frame.buffer_mut()[(gap + 1, y)].symbol(), "░");
|
||||||
|
} else {
|
||||||
|
draw_chrome(frame, groups, Rect::new(0, y, 40, 1));
|
||||||
|
assert_eq!(frame.buffer_mut()[(start, y)].symbol(), "░");
|
||||||
|
}
|
||||||
|
assert_eq!(frame.buffer_mut()[(39, y)].bg, Color::Cyan);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bar_margins_surround_styling_and_keep_anchors_and_narrow_content() {
|
fn bar_margins_surround_styling_and_keep_anchors_and_narrow_content() {
|
||||||
let directory = Directory::new();
|
let directory = Directory::new();
|
||||||
@@ -5983,7 +6023,7 @@ mod tests {
|
|||||||
r#"
|
r#"
|
||||||
Vamp.configure do |config|
|
Vamp.configure do |config|
|
||||||
options = {left: [:badge], center: [:lsp], right: [:badge],
|
options = {left: [:badge], center: [:lsp], right: [:badge],
|
||||||
badge: {padding: 0, margin: 2, leading_edge: "<", trailing_edge: ">"}, lsp: {padding: 0}}
|
badge: {padding: 0, margin: 2, leading_edge: "<", trailing_edge: ">"}, lsp: {padding: 0, margin: 1}}
|
||||||
config.bar.top = options
|
config.bar.top = options
|
||||||
config.bar.bottom = options
|
config.bar.bottom = options
|
||||||
end
|
end
|
||||||
@@ -6036,7 +6076,7 @@ end
|
|||||||
line.center.clear();
|
line.center.clear();
|
||||||
line.right.clear();
|
line.right.clear();
|
||||||
let lsp = line.segments.get_mut("lsp").unwrap();
|
let lsp = line.segments.get_mut("lsp").unwrap();
|
||||||
lsp.margin = 64;
|
lsp.margin = [64; 2];
|
||||||
lsp.padding = 64;
|
lsp.padding = 64;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -6749,7 +6789,7 @@ end
|
|||||||
SegmentConfig {
|
SegmentConfig {
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
padding: 0,
|
padding: 0,
|
||||||
margin: 0,
|
margin: [0; 2],
|
||||||
leading_edge: String::new(),
|
leading_edge: String::new(),
|
||||||
trailing_edge: String::new(),
|
trailing_edge: String::new(),
|
||||||
first_leading_edge: None,
|
first_leading_edge: None,
|
||||||
@@ -6913,7 +6953,7 @@ end
|
|||||||
SegmentConfig {
|
SegmentConfig {
|
||||||
title: "{index}".into(),
|
title: "{index}".into(),
|
||||||
padding: 2,
|
padding: 2,
|
||||||
margin: 0,
|
margin: [0; 2],
|
||||||
leading_edge: "界▌".into(),
|
leading_edge: "界▌".into(),
|
||||||
trailing_edge: "▐界".into(),
|
trailing_edge: "▐界".into(),
|
||||||
first_leading_edge: Some("<".into()),
|
first_leading_edge: Some("<".into()),
|
||||||
@@ -6924,7 +6964,7 @@ end
|
|||||||
SegmentConfig {
|
SegmentConfig {
|
||||||
title: String::new(),
|
title: String::new(),
|
||||||
padding: 2,
|
padding: 2,
|
||||||
margin: 0,
|
margin: [0; 2],
|
||||||
leading_edge: "界(".into(),
|
leading_edge: "界(".into(),
|
||||||
trailing_edge: ")界".into(),
|
trailing_edge: ")界".into(),
|
||||||
first_leading_edge: None,
|
first_leading_edge: None,
|
||||||
@@ -7021,7 +7061,7 @@ end
|
|||||||
SegmentConfig {
|
SegmentConfig {
|
||||||
title: "Not visible without a value".into(),
|
title: "Not visible without a value".into(),
|
||||||
padding: 64,
|
padding: 64,
|
||||||
margin: 0,
|
margin: [0; 2],
|
||||||
leading_edge: "░▒▓".into(),
|
leading_edge: "░▒▓".into(),
|
||||||
trailing_edge: "▓▒░".into(),
|
trailing_edge: "▓▒░".into(),
|
||||||
first_leading_edge: None,
|
first_leading_edge: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user