1use std::fmt::Alignment;
14
15use duat_core::{
16 buffer::Buffer,
17 context::Handle,
18 data::Pass,
19 form,
20 mode::{MouseButton, MouseEvent, MouseEventKind},
21 text::{AlignCenter, AlignLeft, AlignRight, Builder, Text},
22 ui::{PushSpecs, Side, Widget},
23};
24
25pub struct LineNumbers {
38 buffer: Handle,
39 text: Text,
40 pub relative: bool,
44 pub align: Alignment,
48 pub main_align: Alignment,
52 pub show_wraps: bool,
56}
57
58impl LineNumbers {
59 pub fn builder() -> LineNumbersOpts {
62 LineNumbersOpts::default()
63 }
64
65 fn calculate_width(&self, pa: &Pass) -> f32 {
67 let len = self.buffer.read(pa).text().len().line();
68 len.ilog10() as f32
69 }
70
71 fn form_text(&self, pa: &Pass) -> Text {
72 let (main_line, printed_lines) = {
73 let buffer = self.buffer.read(pa);
74 let main_line = if buffer.selections().is_empty() {
75 usize::MAX
76 } else {
77 buffer.selections().get_main().unwrap().line()
78 };
79
80 (main_line, buffer.printed_lines().to_vec())
81 };
82
83 let mut builder = Text::builder();
84 align(&mut builder, self.align);
85
86 for (index, (line, is_wrapped)) in printed_lines.iter().enumerate() {
87 if *line == main_line {
88 align(&mut builder, self.main_align);
89 }
90
91 match (*line == main_line, is_wrapped) {
92 (false, false) => {}
93 (true, false) => builder.push(form::id_of!("linenum.main")),
94 (false, true) => builder.push(form::id_of!("linenum.wrapped")),
95 (true, true) => builder.push(form::id_of!("linenum.wrapped.main")),
96 }
97
98 let is_wrapped = *is_wrapped && index > 0;
99 push_text(&mut builder, *line, main_line, is_wrapped, self);
100
101 if *line == main_line {
102 align(&mut builder, self.align);
103 }
104 }
105
106 builder.build()
107 }
108}
109
110impl Widget for LineNumbers {
111 fn update(pa: &mut Pass, handle: &Handle<Self>) {
112 let width = handle.read(pa).calculate_width(pa);
113 handle.area().set_width(pa, width + 1.0).unwrap();
114
115 handle.write(pa).text = handle.read(pa).form_text(pa);
116 }
117
118 fn needs_update(&self, pa: &Pass) -> bool {
119 self.buffer.has_changed(pa)
120 }
121
122 fn text(&self) -> &Text {
123 &self.text
124 }
125
126 fn text_mut(&mut self) -> &mut Text {
127 &mut self.text
128 }
129
130 fn on_mouse_event(pa: &mut Pass, handle: &Handle<Self>, event: MouseEvent) {
131 let line = |buffer: &Buffer| {
132 let lines = buffer.printed_lines();
133 event
134 .points
135 .and_then(|tpp| lines.get(tpp.points().real.line()))
136 .map(|(line, _)| *line)
137 .unwrap_or(buffer.text().len().line())
138 };
139 match event.kind {
140 MouseEventKind::Down(MouseButton::Left) => {
141 let handle = handle.read(pa).buffer.clone();
142 let line = line(handle.read(pa));
143
144 handle.selections_mut(pa).remove_extras();
145 handle.edit_main(pa, |mut c| {
146 c.unset_anchor();
147 c.move_to_coords(line, 0)
148 })
149 }
150 MouseEventKind::Drag(MouseButton::Left) => {
151 let handle = handle.read(pa).buffer.clone();
152 let line = line(handle.read(pa));
153
154 handle.selections_mut(pa).remove_extras();
155 handle.edit_main(pa, |mut c| {
156 c.set_anchor_if_needed();
157 c.move_to_coords(line, 0)
158 })
159 }
160 MouseEventKind::ScrollDown => {
161 let handle = handle.read(pa).buffer.clone();
162 let opts = handle.opts(pa);
163 let (buffer, area) = handle.write_with_area(pa);
164
165 area.scroll_ver(buffer.text(), 3, opts);
166 }
167 MouseEventKind::ScrollUp => {
168 let handle = handle.read(pa).buffer.clone();
169 let opts = handle.opts(pa);
170 let (buffer, area) = handle.write_with_area(pa);
171
172 area.scroll_ver(buffer.text(), -3, opts);
173 }
174 _ => {}
175 }
176 }
177}
178
179#[derive(Clone, Copy, Debug)]
190pub struct LineNumbersOpts {
191 pub relative: bool,
195 pub align: Alignment,
199 pub main_align: Alignment,
203 pub show_wraps: bool,
207 pub on_the_right: bool,
211}
212
213impl LineNumbersOpts {
214 pub const fn new() -> Self {
216 Self {
217 relative: false,
218 align: Alignment::Left,
219 main_align: Alignment::Right,
220 show_wraps: false,
221 on_the_right: false,
222 }
223 }
224
225 pub fn push_on(self, pa: &mut Pass, handle: &Handle) -> Handle<LineNumbers> {
227 let mut line_numbers = LineNumbers {
228 buffer: handle.clone(),
229 text: Text::default(),
230 relative: self.relative,
231 align: self.align,
232 main_align: self.main_align,
233 show_wraps: self.show_wraps,
234 };
235 line_numbers.text = line_numbers.form_text(pa);
236
237 let specs = PushSpecs {
238 side: if self.on_the_right {
239 Side::Right
240 } else {
241 Side::Left
242 },
243 ..Default::default()
244 };
245
246 handle.push_outer_widget(pa, line_numbers, specs)
247 }
248}
249
250impl Default for LineNumbersOpts {
251 fn default() -> Self {
252 Self::new()
253 }
254}
255
256fn push_text(b: &mut Builder, line: usize, main: usize, is_wrapped: bool, opts: &LineNumbers) {
258 if (!is_wrapped || opts.show_wraps) && main != usize::MAX {
259 let num = if opts.relative {
260 if line != main {
261 line.abs_diff(main)
262 } else {
263 line + 1
264 }
265 } else {
266 line + 1
267 };
268 b.push(num);
269 }
270
271 b.push("\n");
272 b.push(form::DEFAULT_ID);
273}
274
275fn align(b: &mut Builder, alignment: Alignment) {
276 match alignment {
277 Alignment::Left => b.push(AlignLeft),
278 Alignment::Center => b.push(AlignCenter),
279 Alignment::Right => b.push(AlignRight),
280 }
281}