use super::*;
pub fn offset_from_utf16(text: &str, offset: usize) -> usize {
let mut utf8_offset = 0;
let mut utf16_count = 0;
for ch in text.chars() {
if utf16_count >= offset {
break;
}
utf16_count += ch.len_utf16();
utf8_offset += ch.len_utf8();
}
utf8_offset
}
pub fn offset_to_utf16(text: &str, offset: usize) -> usize {
let mut utf16_offset = 0;
let mut utf8_count = 0;
for ch in text.chars() {
if utf8_count >= offset {
break;
}
utf8_count += ch.len_utf8();
utf16_offset += ch.len_utf16();
}
utf16_offset
}
pub fn range_from_utf16(text: &str, range: Range<usize>) -> Range<usize> {
offset_from_utf16(text, range.start)..offset_from_utf16(text, range.end)
}
pub fn range_to_utf16(text: &str, range: Range<usize>) -> Range<usize> {
offset_to_utf16(text, range.start)..offset_to_utf16(text, range.end)
}
pub fn composition_selection(
text: &str,
start: usize,
selection: Option<Range<usize>>,
) -> Range<usize> {
let range = selection
.map(|range| range_from_utf16(text, range))
.unwrap_or(text.len()..text.len());
start + range.start..start + range.end
}
pub fn previous_boundary(text: &str, offset: usize) -> usize {
text.grapheme_indices(true)
.rev()
.find_map(|(idx, _)| (idx < offset).then_some(idx))
.unwrap_or(0)
}
pub fn next_boundary(text: &str, offset: usize) -> usize {
text.grapheme_indices(true)
.find_map(|(idx, _)| (idx > offset).then_some(idx))
.unwrap_or(text.len())
}
pub fn joins_group(last: Option<(EditKind, usize)>, kind: EditKind, at: usize) -> bool {
last.is_some_and(|(last_kind, offset)| last_kind == kind && at == offset)
}
pub fn normalize(text: &str, shape: Shape) -> String {
let text = text.replace("\r\n", "\n").replace('\r', "\n");
if shape.is_multiline() {
text
} else {
text.replace('\n', " ")
}
}
pub fn line_start(text: &str, offset: usize) -> usize {
text[..offset].rfind('\n').map_or(0, |at| at + 1)
}
pub fn line_end(text: &str, offset: usize) -> usize {
text[offset..]
.find('\n')
.map_or(text.len(), |at| offset + at)
}
pub(super) fn is_word(segment: &str) -> bool {
segment.chars().any(char::is_alphanumeric)
}
pub fn previous_word_boundary(text: &str, offset: usize) -> usize {
text.split_word_bound_indices()
.filter(|(start, _)| *start < offset)
.rfind(|(_, segment)| is_word(segment))
.map(|(start, _)| start)
.unwrap_or(0)
}
pub fn next_word_boundary(text: &str, offset: usize) -> usize {
text.split_word_bound_indices()
.filter(|(start, segment)| start + segment.len() > offset)
.find(|(_, segment)| is_word(segment))
.map(|(start, segment)| start + segment.len())
.unwrap_or(text.len())
}