Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_text/src/highlight.rs

4.3 KiB, 5 runs

created by r1870400018:1099, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::{
2 lines::{
3 LineRange,
4 },
5};
6
7use oxedyne_fe2o3_core::prelude::*;
8use oxedyne_fe2o3_geom::{
9 dim::{
10 Dim,
11 },
12};
13
14use std::fmt;
15
16
17#[derive(Clone, Debug, Default)]
18pub struct Highlight<D: Clone + fmt::Debug + Default> {
19 pub range: LineRange,
20 pub level: u8,
21 pub data: Option<D>,
22}
23
24impl<
25 D: Clone + fmt::Debug + Default
26>
27 Highlight<D>
28{
29 pub fn new(
30 range: LineRange,
31 level: u8,
32 data: Option<D>,
33 )
34 -> Self
35 {
36 Self {
37 range,
38 level,
39 data,
40 }
41 }
42
43 pub fn get_range(&self) -> &LineRange {
44 &self.range
45 }
46 pub fn get_level(&self) -> u8 {
47 self.level
48 }
49 pub fn get_data(&self) -> Option<&D> {
50 self.data.as_ref()
51 }
52}
53
54/// Build and manage line focus highlighting for some text.
55#[derive(Clone, Debug, Default)]
56pub struct Highlighter<D: Clone + fmt::Debug + Default> {
57 pub ranges: Vec<Highlight<D>>,
58 pub focus: usize,
59}
60
61impl<
62 D: Clone + fmt::Debug + Default
63>
64 Highlighter<D>
65{
66 pub fn new(
67 ranges: Vec<Highlight<D>>,
68 focus: Option<usize>,
69 )
70 -> Self
71 {
72 let focus = if let Some(focus) = focus {
73 if ranges.is_empty() {
74 0
75 } else {
76 if focus < ranges.len() {
77 focus
78 } else {
79 ranges.len() - 1
80 }
81 }
82 } else {
83 0
84 };
85
86 Self {
87 ranges,
88 focus,
89 ..Default::default()
90 }
91 }
92
93 pub fn is_empty(&self) -> bool {
94 self.ranges.is_empty()
95 }
96
97 pub fn len(&self) -> usize {
98 self.ranges.len()
99 }
100
101 pub fn get_focus(&self) -> usize {
102 self.focus
103 }
104
105 pub fn get_highlighted(&self) -> Option<&Highlight<D>> {
106 if !self.is_empty() {
107 if self.focus > self.ranges.len() - 1 {
108 Some(&self.ranges[self.ranges.len() - 1])
109 } else {
110 Some(&self.ranges[self.focus])
111 }
112 } else {
113 None
114 }
115 }
116
117 pub fn inc_focus(&mut self) {
118 if !self.is_empty() {
119 if self.focus >= self.ranges.len() - 1 {
120 self.focus = 0;
121 } else {
122 self.focus += 1;
123 }
124 } else {
125 self.focus = 0;
126 }
127 }
128
129 pub fn dec_focus(&mut self) {
130 if !self.is_empty() {
131 if self.focus == 0 {
132 self.focus = self.ranges.len() - 1;
133 } else {
134 self.focus -= 1;
135 }
136 } else {
137 self.focus = 0;
138 }
139 }
140
141 pub fn normalise_focus(&mut self) {
142 if self.focus > self.ranges.len() - 1 {
143 self.focus = self.ranges.len() - 1;
144 }
145 }
146
147 pub fn insert(&mut self, new_highlight: Highlight<D>) {
148 let insert_pos = self.ranges.binary_search_by(|highlight| {
149 highlight.range.line.cmp(&new_highlight.range.line)
150 .then(highlight.range.span.start().cmp(&new_highlight.range.span.start()))
151 }).unwrap_or_else(|pos| pos);
152 self.ranges.insert(insert_pos, new_highlight);
153 }
154
155 pub fn delete_highlight_enclosing(&mut self, (x, y): (Dim, Dim)) {
156 self.ranges.retain(|highlight| {
157 let (span_x1, span_x2) = highlight.range.span.tup();
158 !(highlight.range.line == y && x > span_x1 && x < span_x2)
159 });
160 self.normalise_focus();
161 }
162
163 pub fn delete_highlight_line(&mut self, y: usize) {
164 self.ranges.retain(|highlight| {
165 highlight.range.line != y
166 });
167 self.normalise_focus();
168 }
169
170 pub fn inc_highlight_lines(&mut self, y: usize) {
171 self.ranges.iter_mut().for_each(|highlight| {
172 if highlight.range.line.as_index() >= y {
173 highlight.range.line += 1;
174 }
175 });
176 if self.focus >= y {
177 self.inc_focus();
178 }
179 }
180
181 pub fn dec_highlight_lines(&mut self, y: usize) {
182 self.ranges.iter_mut().for_each(|highlight| {
183 if highlight.range.line.as_index() >= y {
184 highlight.range.line -= 1;
185 }
186 });
187 if self.focus >= y {
188 self.dec_focus();
189 }
190 }
191}