xi_core_lib/
backspace.rs

1// Copyright 2018 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Calc start of a backspace delete interval
16use 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        // backspace deletes max(1, tab_size) contiguous spaces
34        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                                //TODO: UCharacter.getCombiningClass(codePoint) == 0
158                                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}