1use std::{fmt::Alignment, marker::PhantomData};
14
15use duat_core::{prelude::*, text::Builder, ui::Constraint};
16
17pub struct LineNumbers<U: Ui> {
23 handle: Handle<File<U>, U>,
24 text: Text,
25 opts: LineNumbersCfg<U>,
27}
28
29impl<U: Ui> LineNumbers<U> {
30 pub fn absolute(&mut self) -> &mut Self {
32 self.opts = self.opts.absolute();
33 self
34 }
35
36 pub fn relative(&mut self) -> &mut Self {
38 self.opts = self.opts.relative();
39 self
40 }
41
42 pub fn rel_abs(&mut self) -> &mut Self {
50 self.opts = self.opts.rel_abs();
51 self
52 }
53
54 pub fn align_left(&mut self) -> &mut Self {
61 self.opts = self.opts.align_left();
62 self
63 }
64
65 pub fn align_center(&mut self) -> &mut Self {
72 self.opts = self.opts.align_center();
73 self
74 }
75
76 pub fn align_right(&mut self) -> &mut Self {
83 self.opts = self.opts.align_right();
84 self
85 }
86
87 pub fn align_main_left(&mut self) -> &mut Self {
89 self.opts = self.opts.align_main_left();
90 self
91 }
92
93 pub fn align_main_center(&mut self) -> &mut Self {
95 self.opts = self.opts.align_main_center();
96 self
97 }
98
99 pub fn align_main_right(&mut self) -> &mut Self {
101 self.opts = self.opts.align_main_right();
102 self
103 }
104
105 pub fn show_wraps(&mut self) -> &mut Self {
107 self.opts = self.opts.show_wraps();
108 self
109 }
110
111 pub fn hide_wraps(&mut self) -> &mut Self {
113 self.opts = self.opts.hide_wraps();
114 self
115 }
116
117 fn calculate_width(&self, pa: &Pass) -> f32 {
119 let len = self.handle.read(pa).text().len().line();
120 len.ilog10() as f32
121 }
122
123 fn form_text(&self, pa: &Pass) -> Text {
124 let (main_line, printed_lines) = {
125 let file = self.handle.read(pa);
126 let main_line = if file.selections().is_empty() {
127 usize::MAX
128 } else {
129 file.selections().get_main().unwrap().line()
130 };
131
132 (main_line, file.printed_lines().to_vec())
133 };
134
135 let mut builder = Text::builder();
136 align(&mut builder, self.opts.align);
137
138 for (index, (line, is_wrapped)) in printed_lines.iter().enumerate() {
139 if *line == main_line {
140 align(&mut builder, self.opts.main_align);
141 }
142
143 match (*line == main_line, is_wrapped) {
144 (false, false) => {}
145 (true, false) => {
146 let id = form::id_of!("linenum.main");
147 builder.push(id)
148 }
149 (false, true) => builder.push(form::id_of!("linenum.wrapped")),
150 (true, true) => builder.push(form::id_of!("linenum.wrapped.main")),
151 }
152
153 let is_wrapped = *is_wrapped && index > 0;
154 push_text(&mut builder, *line, main_line, is_wrapped, &self.opts);
155
156 if *line == main_line {
157 align(&mut builder, self.opts.align);
158 }
159 }
160
161 builder.build()
162 }
163}
164
165impl<U: Ui> Widget<U> for LineNumbers<U> {
166 type Cfg = LineNumbersCfg<U>;
167
168 fn update(pa: &mut Pass, handle: &Handle<Self, U>) {
169 let width = handle.read(pa).calculate_width(pa);
170 handle
171 .area(pa)
172 .constrain_hor([Constraint::Len(width + 1.0)])
173 .unwrap();
174
175 handle.write(pa).text = handle.read(pa).form_text(pa);
176 }
177
178 fn needs_update(&self, _: &Pass) -> bool {
179 self.handle.has_changed()
180 }
181
182 fn cfg() -> Self::Cfg {
183 Self::Cfg {
184 numbering: Numbering::Abs,
185 align: Alignment::Left,
186 main_align: Alignment::Right,
187 show_wraps: false,
188 specs: PushSpecs::left(),
189 _ghost: PhantomData,
190 }
191 }
192
193 fn text(&self) -> &Text {
194 &self.text
195 }
196
197 fn text_mut(&mut self) -> &mut Text {
198 &mut self.text
199 }
200
201 fn once() -> Result<(), Text> {
202 form::set_weak("linenum.main", Form::yellow());
203 form::set_weak("linenum.wrapped", Form::cyan().italic());
204 form::set_weak("linenum.wrapped.main", "linenum.wrapped");
205 Ok(())
206 }
207}
208
209#[derive(Debug, Clone)]
215pub struct LineNumbersCfg<_U> {
216 numbering: Numbering = Numbering::Abs,
217 align: Alignment = Alignment::Left,
218 main_align: Alignment = Alignment::Right,
219 show_wraps: bool = false,
220 specs: PushSpecs = PushSpecs::left(),
221 _ghost: PhantomData<_U>
222}
223
224impl<_U> LineNumbersCfg<_U> {
225 pub fn absolute(self) -> Self {
227 Self { numbering: Numbering::Abs, ..self }
228 }
229
230 pub fn relative(self) -> Self {
232 Self { numbering: Numbering::Rel, ..self }
233 }
234
235 pub fn rel_abs(self) -> Self {
243 Self { numbering: Numbering::RelAbs, ..self }
244 }
245
246 pub fn align_left(self) -> Self {
253 Self {
254 align: Alignment::Left,
255 main_align: Alignment::Left,
256 ..self
257 }
258 }
259
260 pub fn align_center(self) -> Self {
267 Self {
268 align: Alignment::Center,
269 main_align: Alignment::Center,
270 ..self
271 }
272 }
273
274 pub fn align_right(self) -> Self {
281 Self {
282 align: Alignment::Right,
283 main_align: Alignment::Right,
284 ..self
285 }
286 }
287
288 pub fn align_main_left(self) -> Self {
290 Self { main_align: Alignment::Left, ..self }
291 }
292
293 pub fn align_main_center(self) -> Self {
295 Self { main_align: Alignment::Center, ..self }
296 }
297
298 pub fn align_main_right(self) -> Self {
300 Self { main_align: Alignment::Right, ..self }
301 }
302
303 pub fn show_wraps(self) -> Self {
305 Self { show_wraps: true, ..self }
306 }
307
308 pub fn hide_wraps(self) -> Self {
310 Self { show_wraps: false, ..self }
311 }
312
313 pub fn on_the_right(self) -> Self {
317 Self { specs: self.specs.to_right(), ..self }
318 }
319}
320
321impl<U: Ui> WidgetCfg<U> for LineNumbersCfg<U> {
322 type Widget = LineNumbers<U>;
323
324 fn build(self, pa: &mut Pass, info: BuildInfo<U>) -> (Self::Widget, PushSpecs) {
325 let Some(handle) = info.file() else {
326 panic!("For now, you can't push LineNumbers to something that is not a File");
327 };
328 let specs = self.specs;
329
330 let mut widget = LineNumbers {
331 handle,
332 text: Text::default(),
333 opts: self,
334 };
335 widget.text = widget.form_text(pa);
336
337 (widget, specs)
338 }
339}
340
341impl<U: Ui> Copy for LineNumbersCfg<U> {}
342
343fn push_text<_U>(
345 b: &mut Builder,
346 line: usize,
347 main: usize,
348 is_wrapped: bool,
349 cfg: &LineNumbersCfg<_U>,
350) {
351 if (!is_wrapped || cfg.show_wraps) && main != usize::MAX {
352 let num = match cfg.numbering {
353 Numbering::Abs => line + 1,
354 Numbering::Rel => line.abs_diff(main),
355 Numbering::RelAbs => {
356 if line != main {
357 line.abs_diff(main)
358 } else {
359 line + 1
360 }
361 }
362 };
363 b.push(num);
364 }
365
366 b.push("\n");
367 b.push(form::DEFAULT_ID);
368}
369
370fn align(b: &mut Builder, alignment: Alignment) {
371 match alignment {
372 Alignment::Left => b.push(AlignLeft),
373 Alignment::Center => b.push(AlignCenter),
374 Alignment::Right => b.push(AlignRight),
375 }
376}
377
378#[derive(Default, Debug, Clone, Copy)]
380enum Numbering {
381 #[default]
382 Abs,
384 Rel,
387 RelAbs,
390}