use std::ops::Range;
use gpui::{Context, EntityInputHandler, UTF16Selection, Window};
use markdown::{Cursor, Selection};
use ui::input::{composition_selection, offset_to_utf16, range_from_utf16, range_to_utf16};
use crate::editor::Editor;
impl EntityInputHandler for Editor {
fn text_for_range(
&mut self,
range: Range<usize>,
adjusted: &mut Option<Range<usize>>,
_: &mut Window,
_: &mut Context<Self>,
) -> Option<String> {
let text = self.caret_text()?;
let range = range_from_utf16(&text.text, range);
*adjusted = Some(range_to_utf16(&text.text, range.clone()));
Some(text.text.get(range)?.to_string())
}
fn selected_text_range(
&mut self,
_: bool,
_: &mut Window,
_: &mut Context<Self>,
) -> Option<UTF16Selection> {
let (start, end) = self.selection.ordered();
let spans_one = start.block == end.block && start.part == end.part;
let head = self.selection.head;
let range = if spans_one {
start.offset..end.offset
} else {
head.offset..head.offset
};
Some(UTF16Selection {
reversed: spans_one && self.selection.head == start,
range: range_to_utf16(&self.caret_text()?.text, range),
})
}
fn marked_text_range(&self, _: &mut Window, _: &mut Context<Self>) -> Option<Range<usize>> {
Some(range_to_utf16(
&self.caret_text()?.text,
self.marked.clone()?,
))
}
fn unmark_text(&mut self, _: &mut Window, _: &mut Context<Self>) {
self.marked = None;
}
fn replace_text_in_range(
&mut self,
range: Option<Range<usize>>,
text: &str,
_: &mut Window,
cx: &mut Context<Self>,
) {
if range.is_none() && self.marked.is_none() && markdown::is_url(text.trim()) {
return self.paste_url(text.trim().to_string(), cx);
}
if let Some(range) = range
.and_then(|range| {
self.caret_text()
.map(|text| range_from_utf16(&text.text, range))
})
.or_else(|| self.marked.clone())
{
let at = self.cursor();
self.selection = Selection::new(
Cursor {
offset: range.start,
..at
},
Cursor {
offset: range.end,
..at
},
);
}
self.marked = None;
self.insert(text, cx);
}
fn replace_and_mark_text_in_range(
&mut self,
range: Option<Range<usize>>,
text: &str,
marked: Option<Range<usize>>,
_: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(range) = range
.and_then(|range| {
self.caret_text()
.map(|text| range_from_utf16(&text.text, range))
})
.or_else(|| self.marked.clone())
{
let at = self.cursor();
self.doc.edit_at(at, |body| body.remove(range.clone()));
self.place(Cursor {
offset: range.start,
..at
});
}
let at = self.cursor();
let start = at.offset;
self.doc.edit_at(at, |body| body.insert(start, text));
self.marked = (!text.is_empty()).then_some(start..start + text.len());
let selected = composition_selection(text, start, marked);
self.selection = Selection::new(
Cursor {
offset: selected.start,
..at
},
Cursor {
offset: selected.end,
..at
},
);
self.reveal = true;
self.caret_moved();
cx.notify();
}
fn bounds_for_range(
&mut self,
range: Range<usize>,
_: gpui::Bounds<gpui::Pixels>,
_: &mut Window,
_: &mut Context<Self>,
) -> Option<gpui::Bounds<gpui::Pixels>> {
let range = range_from_utf16(&self.caret_text()?.text, range);
let at = self.cursor();
let start = Cursor {
offset: range.start,
..at
};
let (origin, line_height) = self.layouts.position(start)?;
let end = self
.layouts
.position(Cursor {
offset: range.end,
..at
})
.map(|(point, _)| point)
.filter(|point| point.y == origin.y);
let width = end.map_or(gpui::px(0.0), |point| point.x - origin.x);
Some(gpui::Bounds::new(origin, gpui::size(width, line_height)))
}
fn character_index_for_point(
&mut self,
point: gpui::Point<gpui::Pixels>,
_: &mut Window,
_: &mut Context<Self>,
) -> Option<usize> {
let hit = self.layouts.hit(point)?;
let at = self.cursor();
(hit.block == at.block && hit.part == at.part)
.then_some(offset_to_utf16(&self.caret_text()?.text, hit.offset))
}
}