1use crate::_private::NonExhaustive;
6use crate::upos_type;
7use format_num_pattern::NumberFormat;
8use rat_event::util::MouseFlags;
9use ratatui::buffer::Buffer;
10use ratatui::layout::Rect;
11use ratatui::prelude::BlockExt;
12use ratatui::style::Style;
13use ratatui::text::Line;
14use ratatui::widgets::StatefulWidget;
15use ratatui::widgets::{Block, Widget};
16
17#[derive(Debug, Default, Clone)]
23pub struct LineNumbers<'a> {
24 start: upos_type,
25 end: Option<upos_type>,
26 cursor: upos_type,
27 relative: bool,
28 flags: Vec<Line<'a>>,
29
30 flag_width: Option<u16>,
31 margin: (u16, u16),
32
33 format: Option<NumberFormat>,
34 style: Style,
35 cursor_style: Option<Style>,
36
37 block: Option<Block<'a>>,
38}
39
40#[derive(Debug, Clone)]
42pub struct LineNumberStyle {
43 pub flag_width: Option<u16>,
44 pub margin: Option<(u16, u16)>,
45 pub format: Option<NumberFormat>,
46 pub style: Style,
47 pub cursor: Option<Style>,
48 pub block: Option<Block<'static>>,
49
50 pub non_exhaustive: NonExhaustive,
51}
52
53#[derive(Debug, Clone)]
55pub struct LineNumberState {
56 pub area: Rect,
57 pub inner: Rect,
58
59 pub start: upos_type,
60
61 pub mouse: MouseFlags,
63
64 pub non_exhaustive: NonExhaustive,
65}
66
67impl<'a> LineNumbers<'a> {
68 pub fn new() -> Self {
69 Self::default()
70 }
71
72 pub fn start(mut self, start: upos_type) -> Self {
74 self.start = start;
75 self
76 }
77
78 pub fn end(mut self, end: upos_type) -> Self {
80 self.end = Some(end);
81 self
82 }
83
84 pub fn cursor(mut self, cursor: upos_type) -> Self {
86 self.cursor = cursor;
87 self
88 }
89
90 pub fn relative(mut self, relative: bool) -> Self {
92 self.relative = relative;
93 self
94 }
95
96 pub fn flags(mut self, flags: Vec<Line<'a>>) -> Self {
98 self.flags = flags;
99 self
100 }
101
102 pub fn flag_width(mut self, width: u16) -> Self {
104 self.flag_width = Some(width);
105 self
106 }
107
108 pub fn margin(mut self, margin: (u16, u16)) -> Self {
110 self.margin = margin;
111 self
112 }
113
114 pub fn format(mut self, format: NumberFormat) -> Self {
116 self.format = Some(format);
117 self
118 }
119
120 pub fn styles(mut self, styles: LineNumberStyle) -> Self {
122 self.style = styles.style;
123 if let Some(flag_width) = styles.flag_width {
124 self.flag_width = Some(flag_width);
125 }
126 if let Some(margin) = styles.margin {
127 self.margin = margin;
128 }
129 if let Some(format) = styles.format {
130 self.format = Some(format);
131 }
132 if let Some(cursor_style) = styles.cursor {
133 self.cursor_style = Some(cursor_style);
134 }
135 if let Some(block) = styles.block {
136 self.block = Some(block);
137 }
138 self.block = self.block.map(|v| v.style(self.style));
139 self
140 }
141
142 pub fn style(mut self, style: Style) -> Self {
144 self.style = style;
145 self.block = self.block.map(|v| v.style(style));
146 self
147 }
148
149 pub fn cursor_style(mut self, style: Style) -> Self {
151 self.cursor_style = Some(style);
152 self
153 }
154
155 pub fn block(mut self, block: Block<'a>) -> Self {
157 self.block = Some(block.style(self.style));
158 self
159 }
160
161 pub fn width(&self) -> u16 {
163 let nr_width = if let Some(end) = self.end {
164 end.ilog10() as u16 + 1
165 } else {
166 (self.start + 100).ilog10() as u16 + 1
167 };
168 let flag_width = if let Some(flag_width) = self.flag_width {
169 flag_width
170 } else {
171 self.flags
172 .iter()
173 .map(|v| v.width() as u16)
174 .max()
175 .unwrap_or_default()
176 };
177 let block_width = {
178 let area = self.block.inner_if_some(Rect::new(0, 0, 2, 2));
179 2 - area.width
180 };
181
182 nr_width + flag_width + self.margin.0 + self.margin.1 + block_width + 1
183 }
184}
185
186impl Default for LineNumberStyle {
187 fn default() -> Self {
188 Self {
189 flag_width: None,
190 margin: None,
191 format: None,
192 style: Default::default(),
193 cursor: None,
194 block: None,
195 non_exhaustive: NonExhaustive,
196 }
197 }
198}
199
200impl StatefulWidget for LineNumbers<'_> {
201 type State = LineNumberState;
202
203 fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
204 state.area = area;
205 state.inner = self.block.inner_if_some(area);
206 state.start = self.start;
207 let end = self.end.unwrap_or(upos_type::MAX);
208
209 let nr_width = if let Some(end) = self.end {
210 end.ilog10() as u16 + 1
211 } else {
212 (self.start + 100).ilog10() as u16 + 1
213 };
214
215 let flag_width = if let Some(flag_width) = self.flag_width {
216 flag_width
217 } else {
218 self.flags
219 .iter()
220 .map(|v| v.width() as u16)
221 .max()
222 .unwrap_or_default()
223 };
224
225 let format = if let Some(format) = self.format {
226 format
227 } else {
228 let mut f = "#".repeat(nr_width.saturating_sub(1) as usize);
229 f.push('0');
230 NumberFormat::new(f).expect("valid")
231 };
232
233 let cursor_style = if let Some(cursor_style) = self.cursor_style {
234 cursor_style
235 } else {
236 self.style
237 };
238
239 if let Some(block) = self.block {
240 block.render(area, buf);
241 } else {
242 buf.set_style(area, self.style);
243 }
244
245 let mut tmp = String::new();
246 for y in state.inner.top()..state.inner.bottom() {
247 let (nr, is_cursor) = if self.relative {
248 let pos = self.start + (y - state.inner.y) as upos_type;
249 (pos.abs_diff(self.cursor), pos == self.cursor)
250 } else {
251 let pos = self.start + (y - state.inner.y) as upos_type;
252 (pos, pos == self.cursor)
253 };
254
255 tmp.clear();
256 if nr < end {
257 _ = format.fmt_to(nr, &mut tmp);
258 }
259
260 let style = if is_cursor { cursor_style } else { self.style };
261
262 let nr_area = Rect::new(
263 state.inner.x + self.margin.0, y,
265 nr_width,
266 1,
267 )
268 .intersection(area);
269 buf.set_stringn(nr_area.x, nr_area.y, &tmp, nr_area.width as usize, style);
270
271 if let Some(flags) = self.flags.get((y - state.inner.y) as usize) {
272 flags.render(
273 Rect::new(
274 state.inner.x + self.margin.0 + nr_width + 1,
275 y,
276 flag_width,
277 1,
278 ),
279 buf,
280 );
281 }
282 }
283 }
284}
285
286impl Default for LineNumberState {
287 fn default() -> Self {
288 Self {
289 area: Default::default(),
290 inner: Default::default(),
291 start: 0,
292 mouse: Default::default(),
293 non_exhaustive: NonExhaustive,
294 }
295 }
296}
297
298impl LineNumberState {
299 pub fn new() -> Self {
300 Self::default()
301 }
302}