1use xi_rope::{Cursor, Rope};
17
18use crate::config::BufferItems;
19use crate::selection::SelRegion;
20use crate::view::View;
21use xi_unicode::*;
22
23#[allow(clippy::cognitive_complexity)]
24pub fn offset_for_delete_backwards(
25 view: &View,
26 region: &SelRegion,
27 text: &Rope,
28 config: &BufferItems,
29) -> usize {
30 if !region.is_caret() {
31 region.min()
32 } else {
33 let (_, c) = view.offset_to_line_col(&text, region.start);
35
36 let tab_off = c % config.tab_size;
37 let tab_size = config.tab_size;
38 let tab_size = if tab_off == 0 { tab_size } else { tab_off };
39 let tab_start = region.start.saturating_sub(tab_size);
40 let preceded_by_spaces =
41 region.start > 0 && (tab_start..region.start).all(|i| text.byte_at(i) == b' ');
42 if preceded_by_spaces && config.translate_tabs_to_spaces && config.use_tab_stops {
43 tab_start
44 } else {
45 #[derive(PartialEq)]
46 enum State {
47 Start,
48 Lf,
49 BeforeKeycap,
50 BeforeVsAndKeycap,
51 BeforeEmojiModifier,
52 BeforeVSAndEmojiModifier,
53 BeforeVS,
54 BeforeEmoji,
55 BeforeZwj,
56 BeforeVSAndZWJ,
57 OddNumberedRIS,
58 EvenNumberedRIS,
59 InTagSequence,
60 Finished,
61 };
62 let mut state = State::Start;
63 let mut tmp_offset = region.end;
64
65 let mut delete_code_point_count = 0;
66 let mut last_seen_vs_code_point_count = 0;
67
68 while state != State::Finished && tmp_offset > 0 {
69 let mut cursor = Cursor::new(&text, tmp_offset);
70 let code_point = cursor.prev_codepoint().unwrap_or('0');
71
72 tmp_offset = text.prev_codepoint_offset(tmp_offset).unwrap_or(0);
73
74 match state {
75 State::Start => {
76 delete_code_point_count = 1;
77 if code_point == '\n' {
78 state = State::Lf;
79 } else if is_variation_selector(code_point) {
80 state = State::BeforeVS;
81 } else if code_point.is_regional_indicator_symbol() {
82 state = State::OddNumberedRIS;
83 } else if code_point.is_emoji_modifier() {
84 state = State::BeforeEmojiModifier;
85 } else if code_point.is_emoji_combining_enclosing_keycap() {
86 state = State::BeforeKeycap;
87 } else if code_point.is_emoji() {
88 state = State::BeforeEmoji;
89 } else if code_point.is_emoji_cancel_tag() {
90 state = State::InTagSequence;
91 } else {
92 state = State::Finished;
93 }
94 }
95 State::Lf => {
96 if code_point == '\r' {
97 delete_code_point_count += 1;
98 }
99 state = State::Finished;
100 }
101 State::OddNumberedRIS => {
102 if code_point.is_regional_indicator_symbol() {
103 delete_code_point_count += 1;
104 state = State::EvenNumberedRIS
105 } else {
106 state = State::Finished
107 }
108 }
109 State::EvenNumberedRIS => {
110 if code_point.is_regional_indicator_symbol() {
111 delete_code_point_count -= 1;
112 state = State::OddNumberedRIS;
113 } else {
114 state = State::Finished;
115 }
116 }
117 State::BeforeKeycap => {
118 if is_variation_selector(code_point) {
119 last_seen_vs_code_point_count = 1;
120 state = State::BeforeVsAndKeycap;
121 } else {
122 if is_keycap_base(code_point) {
123 delete_code_point_count += 1;
124 }
125 state = State::Finished;
126 }
127 }
128 State::BeforeVsAndKeycap => {
129 if is_keycap_base(code_point) {
130 delete_code_point_count += last_seen_vs_code_point_count + 1;
131 }
132 state = State::Finished;
133 }
134 State::BeforeEmojiModifier => {
135 if is_variation_selector(code_point) {
136 last_seen_vs_code_point_count = 1;
137 state = State::BeforeVSAndEmojiModifier;
138 } else {
139 if code_point.is_emoji_modifier_base() {
140 delete_code_point_count += 1;
141 }
142 state = State::Finished;
143 }
144 }
145 State::BeforeVSAndEmojiModifier => {
146 if code_point.is_emoji_modifier_base() {
147 delete_code_point_count += last_seen_vs_code_point_count + 1;
148 }
149 state = State::Finished;
150 }
151 State::BeforeVS => {
152 if code_point.is_emoji() {
153 delete_code_point_count += 1;
154 state = State::BeforeEmoji;
155 } else {
156 if !is_variation_selector(code_point) {
157 delete_code_point_count += 1;
159 }
160 state = State::Finished;
161 }
162 }
163 State::BeforeEmoji => {
164 if code_point.is_zwj() {
165 state = State::BeforeZwj;
166 } else {
167 state = State::Finished;
168 }
169 }
170 State::BeforeZwj => {
171 if code_point.is_emoji() {
172 delete_code_point_count += 2;
173 state = if code_point.is_emoji_modifier() {
174 State::BeforeEmojiModifier
175 } else {
176 State::BeforeEmoji
177 };
178 } else if is_variation_selector(code_point) {
179 last_seen_vs_code_point_count = 1;
180 state = State::BeforeVSAndZWJ;
181 } else {
182 state = State::Finished;
183 }
184 }
185 State::BeforeVSAndZWJ => {
186 if code_point.is_emoji() {
187 delete_code_point_count += last_seen_vs_code_point_count + 2;
188 last_seen_vs_code_point_count = 0;
189 state = State::BeforeEmoji;
190 } else {
191 state = State::Finished;
192 }
193 }
194 State::InTagSequence => {
195 if code_point.is_tag_spec_char() {
196 delete_code_point_count += 1;
197 } else if code_point.is_emoji() {
198 delete_code_point_count += 1;
199 state = State::Finished;
200 } else {
201 delete_code_point_count = 1;
202 state = State::Finished;
203 }
204 }
205 State::Finished => {
206 break;
207 }
208 }
209 }
210
211 let mut start = region.end;
212 while delete_code_point_count > 0 {
213 start = text.prev_codepoint_offset(start).unwrap_or(0);
214 delete_code_point_count -= 1;
215 }
216 start
217 }
218 }
219}