Added previews to opener and searcher
This commit is contained in:
parent
3e8dcdfb11
commit
0337006986
7 changed files with 141 additions and 30 deletions
|
|
@ -21,7 +21,7 @@ impl LangPack {
|
|||
highlighter: Highlighter::default().markdown().git(),
|
||||
comment_syntax: None,
|
||||
},
|
||||
(_, "toml") => Self {
|
||||
("Cargo.lock", _) | (_, "toml") => Self {
|
||||
highlighter: Highlighter::default().toml().git(),
|
||||
comment_syntax: Some(vec!['#', ' ']),
|
||||
},
|
||||
|
|
|
|||
15
src/state.rs
15
src/state.rs
|
|
@ -834,6 +834,16 @@ impl Buffer {
|
|||
pub fn end_session(&mut self, cursor_id: CursorId) {
|
||||
self.cursors.remove(cursor_id);
|
||||
}
|
||||
|
||||
pub fn is_same_path(&self, path: &Path) -> bool {
|
||||
self.path
|
||||
.as_ref()
|
||||
.and_then(|p| p.canonicalize().ok())
|
||||
.as_ref()
|
||||
.map_or(false, |p| {
|
||||
path.canonicalize().ok().map_or(false, |path| *p == path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CLassify the character by property
|
||||
|
|
@ -882,10 +892,7 @@ impl State {
|
|||
}
|
||||
|
||||
pub fn open_or_get(&mut self, path: PathBuf) -> Result<BufferId, Error> {
|
||||
let true_path = path.canonicalize()?;
|
||||
if let Some((buffer_id, _)) = self.buffers.iter().find(|(_, b)| {
|
||||
b.path.as_ref().and_then(|p| p.canonicalize().ok()).as_ref() == Some(&true_path)
|
||||
}) {
|
||||
if let Some((buffer_id, _)) = self.buffers.iter().find(|(_, b)| b.is_same_path(&path)) {
|
||||
Ok(buffer_id)
|
||||
} else {
|
||||
Ok(self.buffers.insert(Buffer::from_file(path)?))
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ impl Default for Theme {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
ui_bg: Color::AnsiValue(235),
|
||||
select_bg: Color::AnsiValue(23),
|
||||
select_bg: Color::AnsiValue(8),
|
||||
line_select_bg: Color::AnsiValue(238),
|
||||
unfocus_select_bg: Color::AnsiValue(240),
|
||||
search_result_bg: Color::AnsiValue(60),
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ impl Input {
|
|||
event: Event,
|
||||
) -> Result<Resp, Event> {
|
||||
buffer.begin_action();
|
||||
let is_doc = matches!(self.mode, Mode::Doc);
|
||||
match event.to_action(|e| {
|
||||
e.to_char()
|
||||
.map(Action::Char)
|
||||
|
|
@ -84,7 +85,9 @@ impl Input {
|
|||
self.refocus(buffer, cursor_id);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Move(dir, dist, retain_base, word)) => {
|
||||
Some(Action::Move(dir, dist, retain_base, word))
|
||||
if matches!(dir, Dir::Left | Dir::Right) || is_doc =>
|
||||
{
|
||||
let dist = match dist {
|
||||
Dist::Char => [1, 1],
|
||||
Dist::Page => self.last_area.size().map(|s| s.saturating_sub(3).max(1)),
|
||||
|
|
@ -95,7 +98,7 @@ impl Input {
|
|||
self.refocus(buffer, cursor_id);
|
||||
Ok(Resp::handled(None))
|
||||
}
|
||||
Some(Action::Pan(dir, dist)) => {
|
||||
Some(Action::Pan(dir, dist)) if is_doc => {
|
||||
let dist = match dist {
|
||||
Dist::Char => [1, 1],
|
||||
Dist::Page => self.last_area.size().map(|s| s.saturating_sub(3).max(1)),
|
||||
|
|
@ -247,11 +250,6 @@ impl Input {
|
|||
title.as_deref(),
|
||||
);
|
||||
|
||||
let Some(cursor) = buffer.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
let cursor_coord = buffer.text.to_coord(cursor.pos);
|
||||
|
||||
let line_num_w = buffer.text.lines().count().max(1).ilog10() as usize + 1;
|
||||
let margin_w = match self.mode {
|
||||
Mode::Prompt => 2,
|
||||
|
|
@ -261,6 +259,11 @@ impl Input {
|
|||
|
||||
self.last_area = frame.rect([margin_w, 0], [!0, !0]).area();
|
||||
|
||||
let Some(cursor) = buffer.cursors.get(cursor_id) else {
|
||||
return;
|
||||
};
|
||||
let cursor_coord = buffer.text.to_coord(cursor.pos);
|
||||
|
||||
let mut pos = 0;
|
||||
for (i, (line_num, (line_pos, line))) in buffer
|
||||
.text
|
||||
|
|
|
|||
|
|
@ -128,6 +128,10 @@ impl<T> Options<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn selected(&self) -> Option<&T> {
|
||||
self.options.get(*self.ranking.get(self.selected)?)
|
||||
}
|
||||
|
||||
pub fn set_options<F: FnMut(&T) -> Option<S>, S: Ord + Copy>(
|
||||
&mut self,
|
||||
options: impl IntoIterator<Item = T>,
|
||||
|
|
@ -157,10 +161,10 @@ impl<T> Options<T> {
|
|||
impl<T: Clone> Element<T> for Options<T> {
|
||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<T>, Event> {
|
||||
match event.to_action(|e| e.to_go().or_else(|| e.to_move())) {
|
||||
Some(Action::Move(dir, dist, false, false)) => {
|
||||
Some(Action::Move(dir, dist @ (Dist::Char | Dist::Doc), false, false)) => {
|
||||
let dist = match dist {
|
||||
Dist::Char => 1,
|
||||
Dist::Page => self.last_height.saturating_sub(1).min(self.ranking.len()),
|
||||
Dist::Page => unimplemented!(),
|
||||
Dist::Doc => self.ranking.len(),
|
||||
};
|
||||
match dir {
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ pub struct Opener {
|
|||
pub buffer: Buffer,
|
||||
pub cursor_id: CursorId,
|
||||
pub input: Input,
|
||||
preview: Option<(Buffer, CursorId, Input)>,
|
||||
}
|
||||
|
||||
impl Opener {
|
||||
|
|
@ -297,13 +298,15 @@ impl Opener {
|
|||
cursor_id,
|
||||
buffer,
|
||||
input: Input::filter(),
|
||||
preview: None,
|
||||
};
|
||||
this.update_completions();
|
||||
this
|
||||
}
|
||||
|
||||
pub fn requested_height(&self) -> usize {
|
||||
self.options.requested_height() + 3
|
||||
!0
|
||||
// self.options.requested_height() * 2 + 3
|
||||
}
|
||||
|
||||
fn set_string(&mut self, s: &str) {
|
||||
|
|
@ -378,7 +381,7 @@ impl Opener {
|
|||
impl Element<()> for Opener {
|
||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<()>, Event> {
|
||||
let path_str = self.buffer.text.to_string();
|
||||
match event.to_action(|e| e.to_cancel().or_else(|| e.to_char().map(Action::Char))) {
|
||||
let res = match event.to_action(|e| e.to_cancel().or_else(|| e.to_char().map(Action::Char))) {
|
||||
Some(Action::Cancel) => Ok(Resp::end(None)),
|
||||
// Backspace removes the entire path segment!
|
||||
// Only works if we're at the end of the string
|
||||
|
|
@ -409,15 +412,27 @@ impl Element<()> for Opener {
|
|||
}
|
||||
Ok(None) => Ok(Resp::handled(None)),
|
||||
Err(event) => {
|
||||
let res = self
|
||||
let res = match self
|
||||
.input
|
||||
.handle(&mut self.buffer, self.cursor_id, event)
|
||||
.map(Resp::into_can_end);
|
||||
self.update_completions();
|
||||
.map(Resp::into_can_end)
|
||||
{
|
||||
Ok(x) => Ok(x),
|
||||
Err(event) => if let Some((buffer, cursor_id, input)) = &mut self.preview {
|
||||
input.handle(buffer, *cursor_id, event).map(Resp::into_can_end)
|
||||
} else {
|
||||
Err(event)
|
||||
},
|
||||
};
|
||||
res
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if self.buffer.text.to_string() != path_str {
|
||||
self.update_completions();
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -465,11 +480,40 @@ impl Visual for FileOption {
|
|||
|
||||
impl Visual for Opener {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
self.preview = self.options.selected().and_then(|f| {
|
||||
self.preview
|
||||
.take()
|
||||
.filter(|(b, _, _)| b.is_same_path(&f.path))
|
||||
.or_else(|| {
|
||||
let mut buffer = Buffer::from_file(f.path.clone()).ok()?;
|
||||
let cursor_id = buffer.start_session();
|
||||
Some((buffer, cursor_id, Input::default()))
|
||||
})
|
||||
});
|
||||
|
||||
let path_input_sz = 3;
|
||||
let remaining_sz = frame.size()[1].saturating_sub(path_input_sz);
|
||||
let (preview_sz, options_sz) = if remaining_sz > 12 {
|
||||
let preview_sz = remaining_sz / 2;
|
||||
(preview_sz, remaining_sz - preview_sz)
|
||||
} else {
|
||||
(0, remaining_sz)
|
||||
};
|
||||
|
||||
if let Some((buffer, cursor_id, input)) = &mut self.preview {
|
||||
frame.rect([0, 0], [frame.size()[0], preview_sz]).with(|f| {
|
||||
input.render(state, buffer.name().as_deref(), buffer, *cursor_id, None, f)
|
||||
});
|
||||
}
|
||||
|
||||
frame
|
||||
.rect([0, 0], [frame.size()[0], frame.size()[1].saturating_sub(3)])
|
||||
.rect([0, preview_sz], [frame.size()[0], options_sz])
|
||||
.with(|f| self.options.render(state, f));
|
||||
frame
|
||||
.rect([0, frame.size()[1].saturating_sub(3)], [frame.size()[0], 3])
|
||||
.rect(
|
||||
[0, preview_sz + options_sz],
|
||||
[frame.size()[0], path_input_sz],
|
||||
)
|
||||
.with(|f| {
|
||||
self.input
|
||||
.render(state, None, &self.buffer, self.cursor_id, None, f)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub struct Searcher {
|
|||
buffer: Buffer,
|
||||
cursor_id: CursorId,
|
||||
input: Input,
|
||||
preview: Option<(Buffer, CursorId, Input, SearchResult)>,
|
||||
}
|
||||
|
||||
impl Searcher {
|
||||
|
|
@ -87,11 +88,13 @@ impl Searcher {
|
|||
cursor_id,
|
||||
buffer,
|
||||
input: Input::filter(),
|
||||
preview: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn requested_height(&self) -> usize {
|
||||
self.options.requested_height() + 3
|
||||
!0
|
||||
// self.options.requested_height() + 3
|
||||
}
|
||||
|
||||
fn update_completions(&mut self) {
|
||||
|
|
@ -113,7 +116,9 @@ impl Searcher {
|
|||
|
||||
impl Element<()> for Searcher {
|
||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<()>, Event> {
|
||||
match event.to_action(|e| e.to_cancel().or_else(|| e.to_char().map(Action::Char))) {
|
||||
let filter_str = self.buffer.text.to_string();
|
||||
let res = match event.to_action(|e| e.to_cancel().or_else(|| e.to_char().map(Action::Char)))
|
||||
{
|
||||
Some(Action::Cancel) => Ok(Resp::end(None)),
|
||||
_ => match self.options.handle(state, event).map(Resp::into_ended) {
|
||||
// Selecting a directory enters the directory
|
||||
|
|
@ -123,19 +128,35 @@ impl Element<()> for Searcher {
|
|||
))))),
|
||||
Ok(None) => Ok(Resp::handled(None)),
|
||||
Err(event) => {
|
||||
let res = self
|
||||
let res = match self
|
||||
.input
|
||||
.handle(&mut self.buffer, self.cursor_id, event)
|
||||
.map(Resp::into_can_end);
|
||||
self.update_completions();
|
||||
.map(Resp::into_can_end)
|
||||
{
|
||||
Ok(x) => Ok(x),
|
||||
Err(event) => {
|
||||
if let Some((buffer, cursor_id, input, _)) = &mut self.preview {
|
||||
input
|
||||
.handle(buffer, *cursor_id, event)
|
||||
.map(Resp::into_can_end)
|
||||
} else {
|
||||
Err(event)
|
||||
}
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if self.buffer.text.to_string() != filter_str {
|
||||
self.update_completions();
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct SearchResult {
|
||||
pub path: PathBuf,
|
||||
pub line_idx: usize,
|
||||
|
|
@ -162,11 +183,43 @@ impl Visual for SearchResult {
|
|||
|
||||
impl Visual for Searcher {
|
||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||
let path_input_sz = 3;
|
||||
let remaining_sz = frame.size()[1].saturating_sub(path_input_sz);
|
||||
let (preview_sz, options_sz) = if remaining_sz > 12 {
|
||||
let preview_sz = remaining_sz / 2;
|
||||
(preview_sz, remaining_sz - preview_sz)
|
||||
} else {
|
||||
(0, remaining_sz)
|
||||
};
|
||||
|
||||
self.preview = self.options.selected().and_then(|result| {
|
||||
self.preview
|
||||
.take()
|
||||
.filter(|(_, _, _, r)| r == result)
|
||||
.or_else(|| {
|
||||
let mut buffer = Buffer::from_file(result.path.clone()).ok()?;
|
||||
let cursor_id = buffer.start_session();
|
||||
let mut input = Input::default();
|
||||
buffer.goto_cursor(cursor_id, [0, result.line_idx as isize], true);
|
||||
input.focus([0, result.line_idx as isize - preview_sz as isize / 2]);
|
||||
Some((buffer, cursor_id, input, result.clone()))
|
||||
})
|
||||
});
|
||||
|
||||
if let Some((buffer, cursor_id, input, result)) = &mut self.preview {
|
||||
frame.rect([0, 0], [frame.size()[0], preview_sz]).with(|f| {
|
||||
input.render(state, buffer.name().as_deref(), buffer, *cursor_id, None, f)
|
||||
});
|
||||
}
|
||||
|
||||
frame
|
||||
.rect([0, 0], [frame.size()[0], frame.size()[1].saturating_sub(3)])
|
||||
.rect([0, preview_sz], [frame.size()[0], options_sz])
|
||||
.with(|f| self.options.render(state, f));
|
||||
frame
|
||||
.rect([0, frame.size()[1].saturating_sub(3)], [frame.size()[0], 3])
|
||||
.rect(
|
||||
[0, preview_sz + options_sz],
|
||||
[frame.size()[0], path_input_sz],
|
||||
)
|
||||
.with(|f| {
|
||||
let title = format!(
|
||||
"{} of {} results for '{}' in {}/",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue