use crate::theme::ActiveTheme as _;
use crate::{Context, HighlightStyle, Hsla};
use super::super::InputState;
use super::super::decorations::{TextDecoration, normalize};
use super::super::rope_ext::RopeExt as _;
fn current_line_color(cx: &crate::App) -> Hsla {
cx.theme().tokens.accent.color.opacity(0.08)
}
impl InputState {
pub(crate) fn refresh_current_line(&mut self, cx: &mut Context<Self>) {
if !self.current_line_highlight || !self.mode.is_multi_line() {
self.clear_current_line(cx);
return;
}
let cursor = self.cursor();
let row = self.core.text.offset_to_point(cursor).row;
let raw = self.core.text.line_start_offset(row)..self.core.text.line_end_offset(row);
if raw.is_empty() {
self.clear_current_line(cx);
return;
}
let style = HighlightStyle {
background_color: Some(current_line_color(cx)),
..Default::default()
};
let decorations = vec![TextDecoration::new(raw, style)];
if let Some(collection) = self.current_line_collection.clone() {
let decorations = normalize(&self.core.text, decorations);
if collection.set_in_place(&mut self.core.decorations, decorations) {
cx.notify();
}
} else {
self.current_line_collection =
Some(self.create_decorations_collection(decorations, cx));
}
}
pub(crate) fn clear_current_line(&mut self, cx: &mut Context<Self>) {
if let Some(collection) = self.current_line_collection.clone() {
if collection.clear_in_place(&mut self.core.decorations) {
cx.notify();
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::AppContext as _;
use crate::{Entity, Window};
struct Probe {
state: Entity<InputState>,
}
impl crate::Render for Probe {
fn render(
&mut self,
_window: &mut Window,
_cx: &mut Context<Self>,
) -> impl crate::IntoElement {
crate::div()
}
}
#[rgpui::test]
fn current_line_follows_cursor(cx: &mut crate::TestAppContext) {
cx.update(crate::input_ui::init);
cx.update(crate::theme::init);
let (probe, cx) = cx.add_window_view(|window, cx| {
let state = cx.new(|cx| InputState::new(window, cx).multi_line(true));
state.update(cx, |state, cx| state.replace("ab\ncde\nf", window, cx));
Probe { state }
});
let state = probe.read_with(cx, |probe, _| probe.state.clone());
cx.update(|_, cx| {
state.update(cx, |state, cx| state.set_selected_range(4..4, cx));
});
let ranges = state.read_with(cx, |state, cx| {
state
.current_line_collection
.as_ref()
.map(|collection| collection.get_ranges(cx))
.unwrap_or_default()
});
assert_eq!(ranges, vec![3..6]);
cx.update(|_, cx| {
state.update(cx, |state, cx| state.set_current_line_highlight(false, cx));
});
let ranges = state.read_with(cx, |state, cx| {
state
.current_line_collection
.as_ref()
.map(|collection| collection.get_ranges(cx))
.unwrap_or_default()
});
assert!(ranges.is_empty());
}
}