Compare commits
No commits in common. "4b5579c7269077c7098027caa9980243b3f0b257" and "18751bb6c33d7ec60c62388db15a709aa71c266d" have entirely different histories.
4b5579c726
...
18751bb6c3
8 changed files with 73 additions and 201 deletions
|
|
@ -25,4 +25,4 @@
|
||||||
|
|
||||||
## Issues to fix
|
## Issues to fix
|
||||||
|
|
||||||
- Undo history changes should not join so easily
|
- New file creation should work with non-existent directories
|
||||||
|
|
@ -198,7 +198,7 @@ impl Highlighter {
|
||||||
pub fn glsl(self) -> Self {
|
pub fn glsl(self) -> Self {
|
||||||
self
|
self
|
||||||
// Keywords
|
// Keywords
|
||||||
.with(TokenKind::Keyword, r"\b[(struct)(if)(while)(for)(else)(break)(continue)(const)(return)(layout)(uniform)(set)(binding)(location)(inout)(in)(case)(switch)(default)]\b")
|
.with(TokenKind::Keyword, r"\b[(struct)(if)(while)(for)(else)(break)(continue)(const)(return)(layout)(uniform)(set)(binding)(location)(in)]\b")
|
||||||
// Primitives
|
// Primitives
|
||||||
.with(TokenKind::Type, r"\b[(u?int)(float)(double)(bool)(void)([ui]?vec[1-4]*)([ui]?mat[1-4]*)(texture[(2D)(3D)]?(Cube)?)([ui]?sampler[(2D)(3D)]?(Shadow)?)]\b")
|
.with(TokenKind::Type, r"\b[(u?int)(float)(double)(bool)(void)([ui]?vec[1-4]*)([ui]?mat[1-4]*)(texture[(2D)(3D)]?(Cube)?)([ui]?sampler[(2D)(3D)]?(Shadow)?)]\b")
|
||||||
// Builtins
|
// Builtins
|
||||||
|
|
|
||||||
|
|
@ -223,11 +223,10 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&mut self) -> Result<(), Error> {
|
pub fn save(&mut self) -> Result<(), Error> {
|
||||||
let path = self.path.as_ref().expect("buffer must have path to save");
|
std::fs::write(
|
||||||
if let Some(parent) = path.parent() {
|
self.path.as_ref().expect("buffer must have path to save"),
|
||||||
std::fs::create_dir_all(parent)?;
|
self.text.to_string(),
|
||||||
}
|
)?;
|
||||||
std::fs::write(path, self.text.to_string())?;
|
|
||||||
self.diverged = false;
|
self.diverged = false;
|
||||||
self.opened_at = Some(SystemTime::now());
|
self.opened_at = Some(SystemTime::now());
|
||||||
self.unsaved = false;
|
self.unsaved = false;
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ impl Area {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Rect<'a> {
|
pub struct Rect<'a> {
|
||||||
pub fg: Color,
|
fg: Color,
|
||||||
pub bg: Color,
|
bg: Color,
|
||||||
area: Area,
|
area: Area,
|
||||||
fb: &'a mut Framebuffer,
|
fb: &'a mut Framebuffer,
|
||||||
has_focus: bool,
|
has_focus: bool,
|
||||||
|
|
@ -177,11 +177,6 @@ impl<'a> Rect<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `with_bg`, but only if background color is not already set.
|
|
||||||
pub fn with_bg_preference(&mut self, bg: Color) -> Rect<'_> {
|
|
||||||
self.with_bg(if self.bg == Color::Reset { bg } else { self.bg })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_focus(&mut self, focus: bool) -> Rect<'_> {
|
pub fn with_focus(&mut self, focus: bool) -> Rect<'_> {
|
||||||
Rect {
|
Rect {
|
||||||
fg: self.fg,
|
fg: self.fg,
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,11 @@ enum Mode {
|
||||||
Doc,
|
Doc,
|
||||||
Prompt,
|
Prompt,
|
||||||
Filter,
|
Filter,
|
||||||
SearchResult,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
line_offset: usize,
|
|
||||||
// x/y location in the buffer that the pane is trying to focus on
|
// x/y location in the buffer that the pane is trying to focus on
|
||||||
pub focus: [isize; 2],
|
pub focus: [isize; 2],
|
||||||
// Remember the last area for things like scrolling
|
// Remember the last area for things like scrolling
|
||||||
|
|
@ -40,14 +38,6 @@ impl Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_result(line_offset: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
mode: Mode::SearchResult,
|
|
||||||
line_offset,
|
|
||||||
..Self::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn focus(&mut self, coord: [isize; 2]) {
|
pub fn focus(&mut self, coord: [isize; 2]) {
|
||||||
for i in 0..2 {
|
for i in 0..2 {
|
||||||
self.focus[i] = self.focus[i]
|
self.focus[i] = self.focus[i]
|
||||||
|
|
@ -128,11 +118,7 @@ impl Input {
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
Some(Action::GotoLine(line)) => {
|
Some(Action::GotoLine(line)) => {
|
||||||
buffer.goto_cursor(
|
buffer.goto_cursor(cursor_id, [0, line], true);
|
||||||
cursor_id,
|
|
||||||
[0, (line - self.line_offset as isize).max(0)],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
self.refocus(buffer, cursor_id);
|
self.refocus(buffer, cursor_id);
|
||||||
Ok(Resp::handled(None))
|
Ok(Resp::handled(None))
|
||||||
}
|
}
|
||||||
|
|
@ -251,30 +237,20 @@ impl Input {
|
||||||
outer_frame: &mut Rect,
|
outer_frame: &mut Rect,
|
||||||
) {
|
) {
|
||||||
// Add frame
|
// Add frame
|
||||||
let mut frame = if matches!(self.mode, Mode::SearchResult) {
|
let mut frame = outer_frame.with_border(
|
||||||
outer_frame.rect([0; 2], [!0; 2])
|
if outer_frame.has_focus() {
|
||||||
} else {
|
&state.theme.focus_border
|
||||||
outer_frame.with_border(
|
} else {
|
||||||
if outer_frame.has_focus() {
|
&state.theme.border
|
||||||
&state.theme.focus_border
|
},
|
||||||
} else {
|
title.as_deref(),
|
||||||
&state.theme.border
|
);
|
||||||
},
|
|
||||||
title.as_deref(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (line_num_w, margin_w) = match self.mode {
|
let line_num_w = buffer.text.lines().count().max(1).ilog10() as usize + 1;
|
||||||
Mode::Prompt => (2, 0),
|
let margin_w = match self.mode {
|
||||||
Mode::Filter => (0, 0),
|
Mode::Prompt => 2,
|
||||||
Mode::Doc => {
|
Mode::Filter => 0,
|
||||||
let line_num_w = (self.line_offset + buffer.text.lines().count())
|
Mode::Doc => line_num_w + 2,
|
||||||
.max(1)
|
|
||||||
.ilog10() as usize
|
|
||||||
+ 1;
|
|
||||||
(line_num_w, line_num_w + 2)
|
|
||||||
}
|
|
||||||
Mode::SearchResult => (4, 6),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.last_area = frame.rect([margin_w, 0], [!0, !0]).area();
|
self.last_area = frame.rect([margin_w, 0], [!0, !0]).area();
|
||||||
|
|
@ -303,19 +279,16 @@ impl Input {
|
||||||
Mode::Filter => frame.rect([0, 0], frame.size()),
|
Mode::Filter => frame.rect([0, 0], frame.size()),
|
||||||
Mode::Prompt => frame
|
Mode::Prompt => frame
|
||||||
.rect([0, i], [1, 1])
|
.rect([0, i], [1, 1])
|
||||||
.with_bg_preference(state.theme.margin_bg)
|
.with_bg(state.theme.margin_bg)
|
||||||
.with_fg(state.theme.margin_line_num)
|
.with_fg(state.theme.margin_line_num)
|
||||||
.fill(' ')
|
.fill(' ')
|
||||||
.text([0, 0], ">"),
|
.text([0, 0], ">"),
|
||||||
Mode::Doc | Mode::SearchResult => frame
|
Mode::Doc => frame
|
||||||
.rect([0, i], [margin_w, 1])
|
.rect([0, i], [margin_w, 1])
|
||||||
.with_bg_preference(state.theme.margin_bg)
|
.with_bg(state.theme.margin_bg)
|
||||||
.with_fg(state.theme.margin_line_num)
|
.with_fg(state.theme.margin_line_num)
|
||||||
.fill(' ')
|
.fill(' ')
|
||||||
.text(
|
.text([1, 0], &format!("{:>line_num_w$}", line_num + 1)),
|
||||||
[1, 0],
|
|
||||||
&format!("{:>line_num_w$}", self.line_offset + line_num + 1),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let line_highlight_selected = matches!(self.mode, Mode::Doc)
|
let line_highlight_selected = matches!(self.mode, Mode::Doc)
|
||||||
|
|
@ -364,7 +337,7 @@ impl Input {
|
||||||
} else if line_highlight_selected && frame.has_focus() {
|
} else if line_highlight_selected && frame.has_focus() {
|
||||||
state.theme.line_select_bg
|
state.theme.line_select_bg
|
||||||
} else {
|
} else {
|
||||||
frame.bg
|
Color::Reset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ impl<T> Options<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Element<T> for Options<T> {
|
impl<T: Clone> Element<T> for Options<T> {
|
||||||
fn handle(&mut self, state: &mut State, event: Event) -> Result<Resp<T>, Event> {
|
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())) {
|
match event.to_action(|e| e.to_go().or_else(|| e.to_move())) {
|
||||||
Some(Action::Move(
|
Some(Action::Move(
|
||||||
|
|
@ -216,7 +216,7 @@ impl<T> Element<T> for Options<T> {
|
||||||
Some(Action::Go) => {
|
Some(Action::Go) => {
|
||||||
if self.selected < self.ranking.len() {
|
if self.selected < self.ranking.len() {
|
||||||
Ok(Resp::end_with(
|
Ok(Resp::end_with(
|
||||||
self.options.remove(self.ranking[self.selected]),
|
self.options[self.ranking[self.selected]].clone(),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -244,24 +244,16 @@ impl Element<()> for Switcher {
|
||||||
)
|
)
|
||||||
.map(Resp::into_can_end);
|
.map(Resp::into_can_end);
|
||||||
// Score entries
|
// Score entries
|
||||||
let filter = self.buffer.text.to_string().to_lowercase();
|
let filter = self.buffer.text.to_string();
|
||||||
self.options.apply_scoring(|b| {
|
self.options.apply_scoring(|b| {
|
||||||
let Some(buffer) = state.buffers.get(*b) else {
|
let Some(buffer) = state.buffers.get(*b) else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let name = buffer.name().as_deref().unwrap_or("").to_lowercase();
|
let name = buffer.name()?;
|
||||||
let parent = buffer
|
|
||||||
.path
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|p| Some(p.parent()?.to_str()?.to_lowercase()));
|
|
||||||
if name.starts_with(&filter) {
|
if name.starts_with(&filter) {
|
||||||
Some(1)
|
Some(1)
|
||||||
} else if name.contains(&filter) {
|
} else if name.contains(&filter) {
|
||||||
Some(2)
|
Some(2)
|
||||||
} else if let Some(parent) = parent
|
|
||||||
&& parent.contains(&filter)
|
|
||||||
{
|
|
||||||
Some(3)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -292,18 +284,7 @@ impl Visual for BufferId {
|
||||||
let Some(buffer) = state.buffers.get(*self) else {
|
let Some(buffer) = state.buffers.get(*self) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
frame
|
frame.text([0, 0], buffer.name().as_deref().unwrap_or("<unknown>"));
|
||||||
.with_fg(state.theme.option_file)
|
|
||||||
.text([0, 0], buffer.name().as_deref().unwrap_or("<unknown>"));
|
|
||||||
let path_x = (frame.size()[0] as isize / 3).max(32);
|
|
||||||
frame.with_fg(state.theme.option_dir).text(
|
|
||||||
[path_x, 0],
|
|
||||||
buffer
|
|
||||||
.path
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|p| p.parent()?.to_str())
|
|
||||||
.unwrap_or("<unknown>"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,18 +388,7 @@ impl Opener {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(err) => self.options.set_options(
|
Err(err) => self.options.set_options::<_, ()>(Vec::new(), |_| None),
|
||||||
if filter != "" {
|
|
||||||
vec![FileOption {
|
|
||||||
path: [dir, &file_name].into_iter().collect(),
|
|
||||||
kind: FileKind::New,
|
|
||||||
is_link: false,
|
|
||||||
}]
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
},
|
|
||||||
|_| Some(()),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
145
src/ui/search.rs
145
src/ui/search.rs
|
|
@ -1,42 +1,35 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::state::{Buffer, CursorId};
|
use crate::state::{Buffer, CursorId};
|
||||||
use std::{fs, path::Path};
|
use std::fs;
|
||||||
|
|
||||||
pub struct Searcher {
|
pub struct Searcher {
|
||||||
options: Options<SearchResult>,
|
options: Options<SearchResult>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
search_path: PathBuf,
|
|
||||||
needle: String,
|
needle: String,
|
||||||
// Filter
|
// Filter
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
cursor_id: CursorId,
|
cursor_id: CursorId,
|
||||||
input: Input,
|
input: Input,
|
||||||
preview: Option<(Buffer, CursorId, Input, SearchLoc)>,
|
preview: Option<(Buffer, CursorId, Input, SearchResult)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Searcher {
|
impl Searcher {
|
||||||
pub fn new(path: PathBuf, needle: String) -> Self {
|
pub fn new(mut path: PathBuf, needle: String) -> Self {
|
||||||
let mut search_path = path.clone();
|
let path = loop {
|
||||||
let search_path = loop {
|
if let Ok(mut entries) = fs::read_dir(&path)
|
||||||
if let Ok(mut entries) = fs::read_dir(&search_path)
|
|
||||||
&& entries.any(|e| {
|
&& entries.any(|e| {
|
||||||
e.map_or(false, |e| {
|
e.map_or(false, |e| {
|
||||||
e.file_name() == ".git" && e.file_type().map_or(false, |t| t.is_dir())
|
e.file_name() == ".git" && e.file_type().map_or(false, |t| t.is_dir())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
break search_path;
|
break path;
|
||||||
} else if !search_path.pop() {
|
} else if !path.pop() {
|
||||||
break std::env::current_dir().expect("No cwd");
|
break std::env::current_dir().expect("No cwd");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn search_in(
|
fn search_in(path: &PathBuf, needle: &str, results: &mut Vec<SearchResult>) {
|
||||||
search_path: &Path,
|
|
||||||
path: &Path,
|
|
||||||
needle: &str,
|
|
||||||
results: &mut Vec<SearchResult>,
|
|
||||||
) {
|
|
||||||
// Cap reached!
|
// Cap reached!
|
||||||
if results.len() < 500 {
|
if results.len() < 500 {
|
||||||
// Skip hidden files
|
// Skip hidden files
|
||||||
|
|
@ -58,31 +51,16 @@ impl Searcher {
|
||||||
for (line_idx, line_text) in
|
for (line_idx, line_text) in
|
||||||
s.lines().enumerate().filter(|(_, l)| l.contains(needle))
|
s.lines().enumerate().filter(|(_, l)| l.contains(needle))
|
||||||
{
|
{
|
||||||
let mut line_buffer = Buffer::new(
|
|
||||||
false,
|
|
||||||
line_text.trim().chars().collect(),
|
|
||||||
path.to_path_buf(),
|
|
||||||
);
|
|
||||||
results.push(SearchResult {
|
results.push(SearchResult {
|
||||||
loc: SearchLoc {
|
path: path.clone(),
|
||||||
path: path.to_path_buf(),
|
line_idx,
|
||||||
line_idx,
|
line_text: line_text.trim().to_string(),
|
||||||
},
|
|
||||||
rdir: format!(
|
|
||||||
"./{}",
|
|
||||||
path.parent()
|
|
||||||
.and_then(|p| p.strip_prefix(search_path).ok()?.to_str())
|
|
||||||
.unwrap_or("unknown")
|
|
||||||
),
|
|
||||||
line_input: Input::search_result(line_idx),
|
|
||||||
line_cursor: line_buffer.start_session(),
|
|
||||||
line_buffer,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if let Ok(entries) = fs::read_dir(path) {
|
} else if let Ok(entries) = fs::read_dir(path) {
|
||||||
// Special case, ignore Rust target dir to prevent searching too many places
|
// Special case, ignore Rust target dir to prevent searching too many places
|
||||||
{
|
{
|
||||||
let mut path = path.to_path_buf();
|
let mut path = path.clone();
|
||||||
path.push("CACHEDIR.TAG");
|
path.push("CACHEDIR.TAG");
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
return;
|
return;
|
||||||
|
|
@ -91,30 +69,27 @@ impl Searcher {
|
||||||
|
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
let Ok(entry) = entry else { continue };
|
let Ok(entry) = entry else { continue };
|
||||||
search_in(search_path, &entry.path(), needle, results);
|
search_in(&entry.path(), needle, results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
search_in(&search_path, &search_path, &needle, &mut results);
|
search_in(&path, &needle, &mut results);
|
||||||
|
|
||||||
let mut buffer = Buffer::default();
|
let mut buffer = Buffer::default();
|
||||||
let cursor_id = buffer.start_session();
|
let cursor_id = buffer.start_session();
|
||||||
|
|
||||||
let mut this = Self {
|
Self {
|
||||||
options: Options::new(results),
|
options: Options::new(results),
|
||||||
path,
|
path,
|
||||||
search_path,
|
|
||||||
needle,
|
needle,
|
||||||
cursor_id,
|
cursor_id,
|
||||||
buffer,
|
buffer,
|
||||||
input: Input::filter(),
|
input: Input::filter(),
|
||||||
preview: None,
|
preview: None,
|
||||||
};
|
}
|
||||||
this.update_completions();
|
|
||||||
this
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requested_height(&self) -> usize {
|
pub fn requested_height(&self) -> usize {
|
||||||
|
|
@ -125,34 +100,13 @@ impl Searcher {
|
||||||
fn update_completions(&mut self) {
|
fn update_completions(&mut self) {
|
||||||
let filter = self.buffer.text.to_string().to_lowercase();
|
let filter = self.buffer.text.to_string().to_lowercase();
|
||||||
self.options.apply_scoring(|e| {
|
self.options.apply_scoring(|e| {
|
||||||
let name = e
|
let name = format!("{}", e.path.display()).to_lowercase();
|
||||||
.loc
|
|
||||||
.path
|
|
||||||
.file_name()
|
|
||||||
.and_then(|f| f.to_str())
|
|
||||||
.unwrap_or("<unknown>")
|
|
||||||
.to_lowercase();
|
|
||||||
let parent = e
|
|
||||||
.loc
|
|
||||||
.path
|
|
||||||
.parent()
|
|
||||||
.and_then(|f| Some(f.to_str()?.to_lowercase()));
|
|
||||||
let unshared_components = e
|
|
||||||
.loc
|
|
||||||
.path
|
|
||||||
.ancestors()
|
|
||||||
.map(|a| if self.path.starts_with(a) { -1 } else { 1 })
|
|
||||||
.sum::<i32>();
|
|
||||||
if name == filter {
|
if name == filter {
|
||||||
Some((0, unshared_components))
|
Some((0, name.chars().count()))
|
||||||
} else if name.starts_with(&filter) {
|
} else if name.starts_with(&filter) {
|
||||||
Some((1, unshared_components))
|
Some((1, name.chars().count()))
|
||||||
} else if name.contains(&filter) {
|
} else if name.contains(&filter) {
|
||||||
Some((2, unshared_components))
|
Some((2, name.chars().count()))
|
||||||
} else if let Some(parent) = parent
|
|
||||||
&& parent.contains(&filter)
|
|
||||||
{
|
|
||||||
Some((3, unshared_components))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -169,8 +123,8 @@ impl Element<()> for Searcher {
|
||||||
_ => match self.options.handle(state, event).map(Resp::into_ended) {
|
_ => match self.options.handle(state, event).map(Resp::into_ended) {
|
||||||
// Selecting a directory enters the directory
|
// Selecting a directory enters the directory
|
||||||
Ok(Some(result)) => Ok(Resp::end(Some(Event::Action(Action::OpenFile(
|
Ok(Some(result)) => Ok(Resp::end(Some(Event::Action(Action::OpenFile(
|
||||||
result.loc.path,
|
result.path,
|
||||||
result.loc.line_idx,
|
result.line_idx,
|
||||||
))))),
|
))))),
|
||||||
Ok(None) => Ok(Resp::handled(None)),
|
Ok(None) => Ok(Resp::handled(None)),
|
||||||
Err(event) => {
|
Err(event) => {
|
||||||
|
|
@ -208,46 +162,27 @@ impl Element<()> for Searcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
struct SearchLoc {
|
pub struct SearchResult {
|
||||||
path: PathBuf,
|
pub path: PathBuf,
|
||||||
line_idx: usize,
|
pub line_idx: usize,
|
||||||
}
|
pub line_text: String,
|
||||||
|
|
||||||
struct SearchResult {
|
|
||||||
loc: SearchLoc,
|
|
||||||
rdir: String,
|
|
||||||
line_input: Input,
|
|
||||||
line_cursor: CursorId,
|
|
||||||
line_buffer: Buffer,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visual for SearchResult {
|
impl Visual for SearchResult {
|
||||||
fn render(&mut self, state: &State, frame: &mut Rect) {
|
fn render(&mut self, state: &State, frame: &mut Rect) {
|
||||||
let name = match self.loc.path.file_name().and_then(|n| n.to_str()) {
|
let name = match self.path.file_name().and_then(|n| n.to_str()) {
|
||||||
Some(name) => format!("{name}"),
|
Some(name) => format!("{name}"),
|
||||||
None => format!("Unknown"),
|
None => format!("Unknown"),
|
||||||
};
|
};
|
||||||
let col_a = (frame.size()[0] / 5).max(20);
|
|
||||||
let col_b = frame.size()[0] / 3;
|
|
||||||
// Filename
|
|
||||||
frame
|
frame
|
||||||
.rect([0, 0], [col_a, !0])
|
|
||||||
.with_fg(state.theme.option_file)
|
.with_fg(state.theme.option_file)
|
||||||
.text([0, 0], &name);
|
.text([0, 0], &format!("{name}:{}", self.line_idx + 1));
|
||||||
// Path
|
frame.with_fg(state.theme.margin_line_num).with(|f| {
|
||||||
frame
|
f.text(
|
||||||
.rect([col_a, 0], [col_b, !0])
|
[f.size()[0] as isize / 3, 0],
|
||||||
.with_fg(state.theme.option_dir)
|
&format!("{}", self.line_text),
|
||||||
.text([0, 0], &self.rdir);
|
);
|
||||||
// Code snippet
|
});
|
||||||
self.line_input.render(
|
|
||||||
state,
|
|
||||||
None,
|
|
||||||
&self.line_buffer,
|
|
||||||
self.line_cursor,
|
|
||||||
None,
|
|
||||||
&mut frame.rect([col_a + col_b, 0], [!0, !0]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,18 +200,18 @@ impl Visual for Searcher {
|
||||||
self.preview = self.options.selected().and_then(|result| {
|
self.preview = self.options.selected().and_then(|result| {
|
||||||
self.preview
|
self.preview
|
||||||
.take()
|
.take()
|
||||||
.filter(|(_, _, _, loc)| loc == &result.loc)
|
.filter(|(_, _, _, r)| r == result)
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let mut buffer = Buffer::open(result.loc.path.clone()).ok()?;
|
let mut buffer = Buffer::open(result.path.clone()).ok()?;
|
||||||
let cursor_id = buffer.start_session();
|
let cursor_id = buffer.start_session();
|
||||||
let mut input = Input::default();
|
let mut input = Input::default();
|
||||||
buffer.goto_cursor(cursor_id, [0, result.loc.line_idx as isize], true);
|
buffer.goto_cursor(cursor_id, [0, result.line_idx as isize], true);
|
||||||
input.focus([0, result.loc.line_idx as isize - preview_sz as isize / 2]);
|
input.focus([0, result.line_idx as isize - preview_sz as isize / 2]);
|
||||||
Some((buffer, cursor_id, input, result.loc.clone()))
|
Some((buffer, cursor_id, input, result.clone()))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((buffer, cursor_id, input, _)) = &mut self.preview {
|
if let Some((buffer, cursor_id, input, result)) = &mut self.preview {
|
||||||
frame.rect([0, 0], [frame.size()[0], preview_sz]).with(|f| {
|
frame.rect([0, 0], [frame.size()[0], preview_sz]).with(|f| {
|
||||||
input.render(state, buffer.name().as_deref(), buffer, *cursor_id, None, f)
|
input.render(state, buffer.name().as_deref(), buffer, *cursor_id, None, f)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue