mirror of https://github.com/bobwen-dev/hunter
minibuffer Widgetified
This commit is contained in:
parent
a6c147442c
commit
7fc77f8605
|
@ -172,9 +172,11 @@ impl FileBrowser {
|
||||||
if self.left_widget().is_err() {
|
if self.left_widget().is_err() {
|
||||||
let file = self.selected_file()?.clone();
|
let file = self.selected_file()?.clone();
|
||||||
if let Some(grand_parent) = file.grand_parent() {
|
if let Some(grand_parent) = file.grand_parent() {
|
||||||
|
let (coords, _, _) = self.columns.calculate_coordinates();
|
||||||
let mut left_view = WillBeWidget::new(Box::new(move |_| {
|
let mut left_view = WillBeWidget::new(Box::new(move |_| {
|
||||||
let mut view
|
let mut view
|
||||||
= ListView::new(Files::new_from_path(&grand_parent)?);
|
= ListView::new(Files::new_from_path(&grand_parent)?);
|
||||||
|
view.set_coordinates(&coords);
|
||||||
Ok(view)
|
Ok(view)
|
||||||
}));
|
}));
|
||||||
self.columns.prepend_widget(left_view);
|
self.columns.prepend_widget(left_view);
|
||||||
|
@ -232,18 +234,30 @@ impl FileBrowser {
|
||||||
let dir = self.minibuffer("cd: ");
|
let dir = self.minibuffer("cd: ");
|
||||||
|
|
||||||
match dir {
|
match dir {
|
||||||
Some(dir) => {
|
Ok(dir) => {
|
||||||
self.columns.widgets.widgets.clear();
|
self.columns.widgets.widgets.clear();
|
||||||
let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?;
|
let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?;
|
||||||
self.cwd = cwd;
|
self.cwd = cwd;
|
||||||
|
let dir = std::path::PathBuf::from(&dir);
|
||||||
|
let left_dir = std::path::PathBuf::from(&dir);
|
||||||
|
let (left_coords, main_coords, _) = self.columns.calculate_coordinates();
|
||||||
|
|
||||||
let middle = WillBeWidget::new(Box::new(move |_| {
|
let middle = WillBeWidget::new(Box::new(move |_| {
|
||||||
let files = Files::new_from_path(&std::path::PathBuf::from(&dir))?;
|
let files = Files::new_from_path(&dir.clone())?;
|
||||||
let listview = ListView::new(files);
|
let mut listview = ListView::new(files);
|
||||||
|
listview.set_coordinates(&main_coords);
|
||||||
Ok(listview)
|
Ok(listview)
|
||||||
}));
|
}));
|
||||||
|
let left = WillBeWidget::new(Box::new(move |_| {
|
||||||
|
let files = Files::new_from_path(&left_dir.parent()?)?;
|
||||||
|
let mut listview = ListView::new(files);
|
||||||
|
listview.set_coordinates(&left_coords);
|
||||||
|
Ok(listview)
|
||||||
|
}));
|
||||||
|
self.columns.push_widget(left);
|
||||||
self.columns.push_widget(middle);
|
self.columns.push_widget(middle);
|
||||||
},
|
},
|
||||||
None => {}
|
Err(_) => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,8 +342,10 @@ impl ListView<Files>
|
||||||
let file_names
|
let file_names
|
||||||
= selected_files.iter().map(|f| f.name.clone()).collect::<Vec<String>>();
|
= selected_files.iter().map(|f| f.name.clone()).collect::<Vec<String>>();
|
||||||
|
|
||||||
match self.minibuffer("exec ($s for selected file(s))") {
|
let cmd = self.minibuffer("exec:");
|
||||||
Some(cmd) => {
|
|
||||||
|
match cmd {
|
||||||
|
Ok(cmd) => {
|
||||||
self.show_status(&format!("Running: \"{}\"", &cmd));
|
self.show_status(&format!("Running: \"{}\"", &cmd));
|
||||||
|
|
||||||
let filename = self.selected_file().name.clone();
|
let filename = self.selected_file().name.clone();
|
||||||
|
@ -375,7 +377,7 @@ impl ListView<Files>
|
||||||
cmd, err)),
|
cmd, err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => self.show_status(""),
|
Err(_) => self.show_status(""),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,8 @@ mod hbox;
|
||||||
mod tabview;
|
mod tabview;
|
||||||
mod async_widget;
|
mod async_widget;
|
||||||
mod fail;
|
mod fail;
|
||||||
|
mod minibuffer;
|
||||||
|
|
||||||
|
|
||||||
use window::Window;
|
use window::Window;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
use termion::event::Key;
|
||||||
|
use termion::input::TermRead;
|
||||||
|
|
||||||
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
|
use crate::coordinates::{Coordinates};
|
||||||
|
use crate::widget::Widget;
|
||||||
|
use crate::window::{send_event, Events};
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
|
pub struct MiniBuffer {
|
||||||
|
coordinates: Coordinates,
|
||||||
|
query: String,
|
||||||
|
input: String,
|
||||||
|
done: bool,
|
||||||
|
position: usize,
|
||||||
|
history: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MiniBuffer {
|
||||||
|
pub fn new() -> MiniBuffer {
|
||||||
|
let xsize = crate::term::xsize();
|
||||||
|
let ysize = crate::term::ysize();
|
||||||
|
let coordinates = Coordinates::new_at(xsize, 1, 1, ysize);
|
||||||
|
MiniBuffer {
|
||||||
|
coordinates: coordinates,
|
||||||
|
query: String::new(),
|
||||||
|
input: String::new(),
|
||||||
|
done: false,
|
||||||
|
position: 0,
|
||||||
|
history: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query(&mut self, query: &str) -> HResult<String> {
|
||||||
|
self.query = query.to_string();
|
||||||
|
self.input.clear();
|
||||||
|
self.done = false;
|
||||||
|
self.position = 0;
|
||||||
|
|
||||||
|
send_event(Events::ExclusiveInput(true))?;
|
||||||
|
|
||||||
|
self.draw()?;
|
||||||
|
write!(stdout(), "{}{}",
|
||||||
|
termion::cursor::Show,
|
||||||
|
termion::cursor::Save)?;
|
||||||
|
stdout().flush()?;
|
||||||
|
|
||||||
|
|
||||||
|
for event in stdin().events() {
|
||||||
|
let event = event?;
|
||||||
|
self.on_event(event);
|
||||||
|
if self.done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
self.draw()?;
|
||||||
|
|
||||||
|
write!(stdout(), "{}", termion::cursor::Restore)?;
|
||||||
|
if self.position != 0 {
|
||||||
|
write!(stdout(),
|
||||||
|
"{}",
|
||||||
|
termion::cursor::Right(self.position as u16))?;
|
||||||
|
}
|
||||||
|
stdout().flush()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.done = false;
|
||||||
|
|
||||||
|
send_event(Events::ExclusiveInput(false))?;
|
||||||
|
|
||||||
|
Ok(self.input.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_bins(comp_name: &str) -> Vec<String> {
|
||||||
|
let paths = std::env::var_os("PATH").unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
.split(":")
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
paths.iter().map(|path| {
|
||||||
|
std::fs::read_dir(path).unwrap().flat_map(|file| {
|
||||||
|
let file = file.unwrap();
|
||||||
|
let name = file.file_name().into_string().unwrap();
|
||||||
|
if name.starts_with(comp_name) {
|
||||||
|
Some(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect::<Vec<String>>()
|
||||||
|
}).flatten().collect::<Vec<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_files(mut comp_name: String) -> Vec<String> {
|
||||||
|
let mut path = std::path::PathBuf::from(&comp_name);
|
||||||
|
|
||||||
|
let dir = if comp_name.starts_with("/") {
|
||||||
|
comp_name = path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
|
path.pop();
|
||||||
|
path.to_string_lossy().to_string()
|
||||||
|
} else {
|
||||||
|
std::env::current_dir().unwrap().to_string_lossy().to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let reader = std::fs::read_dir(dir.clone());
|
||||||
|
if reader.is_err() { return vec![] }
|
||||||
|
let reader = reader.unwrap();
|
||||||
|
|
||||||
|
reader.flat_map(|file| {
|
||||||
|
let file = file.unwrap();
|
||||||
|
let name = file.file_name().into_string().unwrap();
|
||||||
|
if name.starts_with(&comp_name) {
|
||||||
|
if file.file_type().unwrap().is_dir() {
|
||||||
|
Some(format!("{}/{}/", &dir, name))
|
||||||
|
} else {
|
||||||
|
Some(format!("/{}/", name))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).collect::<Vec<String>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for MiniBuffer {
|
||||||
|
fn get_coordinates(&self) -> &Coordinates {
|
||||||
|
&self.coordinates
|
||||||
|
}
|
||||||
|
fn set_coordinates(&mut self, coordinates: &Coordinates) {
|
||||||
|
self.coordinates = coordinates.clone();
|
||||||
|
self.refresh();
|
||||||
|
}
|
||||||
|
fn render_header(&self) -> String {
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
fn refresh(&mut self) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_drawlist(&self) -> String {
|
||||||
|
let (xpos, ypos) = self.get_coordinates().u16position();
|
||||||
|
format!("{}{}{}: {}",
|
||||||
|
crate::term::goto_xy(xpos, ypos),
|
||||||
|
termion::clear::CurrentLine,
|
||||||
|
self.query,
|
||||||
|
self.input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_key(&mut self, key: Key) {
|
||||||
|
match key {
|
||||||
|
Key::Esc | Key::Ctrl('c') => { self.input.clear(); self.done = true; },
|
||||||
|
Key::Char('\n') => {
|
||||||
|
if self.input != "" {
|
||||||
|
self.history.push(self.input.clone());
|
||||||
|
}
|
||||||
|
self.done = true;
|
||||||
|
}
|
||||||
|
Key::Char('\t') => {
|
||||||
|
if !self.input.ends_with(" ") {
|
||||||
|
let part = self.input.rsplitn(2, " ").take(1)
|
||||||
|
.map(|s| s.to_string()).collect::<String>();
|
||||||
|
let completions = find_files(part.clone());
|
||||||
|
if !completions.is_empty() {
|
||||||
|
self.input
|
||||||
|
= self.input[..self.input.len() - part.len()].to_string();
|
||||||
|
self.input.push_str(&completions[0]);
|
||||||
|
self.position += &completions[0].len() - part.len();
|
||||||
|
} else {
|
||||||
|
let completions = find_bins(&part);
|
||||||
|
if !completions.is_empty() {
|
||||||
|
self.input = self.input[..self.input.len()
|
||||||
|
- part.len()].to_string();
|
||||||
|
self.input.push_str(&completions[0]);
|
||||||
|
self.position += &completions[0].len() - part.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.input += "$s";
|
||||||
|
self.position += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Backspace => {
|
||||||
|
if self.position != 0 {
|
||||||
|
self.input.remove(self.position - 1);
|
||||||
|
self.position -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Delete | Key::Ctrl('d') => {
|
||||||
|
if self.position != self.input.len() {
|
||||||
|
self.input.remove(self.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Left | Key::Ctrl('b') => {
|
||||||
|
if self.position != 0 {
|
||||||
|
self.position -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Right | Key::Ctrl('f') => {
|
||||||
|
if self.position != self.input.len() {
|
||||||
|
self.position += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Key::Ctrl('a') => { self.position = 0 },
|
||||||
|
Key::Ctrl('e') => { self.position = self.input.len(); },
|
||||||
|
Key::Char(key) => {
|
||||||
|
self.input.insert(self.position, key);
|
||||||
|
self.position += 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use termion::event::{Event, Key, MouseEvent};
|
use termion::event::{Event, Key, MouseEvent};
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
use crate::coordinates::{Coordinates, Position, Size};
|
||||||
|
use crate::fail::HResult;
|
||||||
|
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ pub trait Widget {
|
||||||
crate::window::show_status(status);
|
crate::window::show_status(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn minibuffer(&self, query: &str) -> Option<String> {
|
fn minibuffer(&self, query: &str) -> HResult<String> {
|
||||||
crate::window::minibuffer(query)
|
crate::window::minibuffer(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +116,15 @@ pub trait Widget {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn draw(&self) -> HResult<()> {
|
||||||
|
let drawlist = self.get_drawlist();
|
||||||
|
let mut bufout = BufWriter::new(std::io::stdout());
|
||||||
|
|
||||||
|
write!(bufout, "{}", drawlist)?;
|
||||||
|
bufout.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn animate_slide_up(&mut self) {
|
fn animate_slide_up(&mut self) {
|
||||||
let coords = self.get_coordinates().clone();
|
let coords = self.get_coordinates().clone();
|
||||||
let xpos = coords.position().x();
|
let xpos = coords.position().x();
|
||||||
|
|
128
src/window.rs
128
src/window.rs
|
@ -11,17 +11,20 @@ use crate::term::ScreenExt;
|
||||||
|
|
||||||
use crate::coordinates::{Coordinates, Position, Size};
|
use crate::coordinates::{Coordinates, Position, Size};
|
||||||
use crate::widget::Widget;
|
use crate::widget::Widget;
|
||||||
|
use crate::minibuffer::MiniBuffer;
|
||||||
use crate::fail::HResult;
|
use crate::fail::HResult;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
|
static ref TX_EVENT: Arc<Mutex<Option<Sender<Events>>>> = { Arc::new(Mutex::new(None)) };
|
||||||
|
static ref MINIBUFFER: Arc<Mutex<MiniBuffer>>
|
||||||
|
= Arc::new(Mutex::new(MiniBuffer::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Events {
|
pub enum Events {
|
||||||
InputEvent(Event),
|
InputEvent(Event),
|
||||||
WidgetReady
|
WidgetReady,
|
||||||
|
ExclusiveInput(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Window<T>
|
pub struct Window<T>
|
||||||
|
@ -87,19 +90,28 @@ where
|
||||||
let (tx_event_internal, rx_event_internal) = channel();
|
let (tx_event_internal, rx_event_internal) = channel();
|
||||||
let (tx_event, rx_event) = channel();
|
let (tx_event, rx_event) = channel();
|
||||||
*TX_EVENT.try_lock().unwrap() = Some(tx_event);
|
*TX_EVENT.try_lock().unwrap() = Some(tx_event);
|
||||||
|
let (tx_request_input, rx_request_input) = channel();
|
||||||
|
|
||||||
|
let mut exclusive_mode = false;
|
||||||
|
|
||||||
event_thread(rx_event, tx_event_internal.clone());
|
event_thread(rx_event, tx_event_internal.clone());
|
||||||
input_thread(tx_event_internal);
|
input_thread(tx_event_internal.clone(), rx_request_input);
|
||||||
|
tx_request_input.send(()).unwrap();
|
||||||
|
|
||||||
for event in rx_event_internal.iter() {
|
for event in rx_event_internal.iter() {
|
||||||
//Self::clear_status();
|
//Self::clear_status();
|
||||||
//let event = event.unwrap();
|
//let event = event.unwrap();
|
||||||
dbg!(&event);
|
|
||||||
match event {
|
match event {
|
||||||
Events::InputEvent(event) => {
|
Events::InputEvent(event) => {
|
||||||
self.widget.on_event(event);
|
self.widget.on_event(event);
|
||||||
self.screen.cursor_hide();
|
self.screen.cursor_hide();
|
||||||
self.draw();
|
self.draw();
|
||||||
|
if !exclusive_mode {
|
||||||
|
tx_request_input.send(()).unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Events::ExclusiveInput(setting) => {
|
||||||
|
exclusive_mode = setting
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.widget.refresh();
|
self.widget.refresh();
|
||||||
|
@ -110,20 +122,23 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event_thread(rx: Receiver<Events>, tx: Sender<Events>) {
|
fn event_thread(rx: Receiver<Events>,
|
||||||
|
tx: Sender<Events>) {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
for event in rx.iter() {
|
for event in rx.iter() {
|
||||||
dbg!(&event);
|
|
||||||
tx.send(event).unwrap();
|
tx.send(event).unwrap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_thread(tx: Sender<Events>) {
|
fn input_thread(tx: Sender<Events>, request_input: Receiver<()>) {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
for input in stdin().events() {
|
for _ in request_input.iter() {
|
||||||
let input = input.unwrap();
|
for input in stdin().events() {
|
||||||
tx.send(Events::InputEvent(input)).unwrap();
|
let input = input.unwrap();
|
||||||
|
tx.send(Events::InputEvent(input)).unwrap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -188,97 +203,8 @@ pub fn show_status(status: &str) {
|
||||||
draw_status();
|
draw_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn minibuffer(query: &str) -> Option<String> {
|
pub fn minibuffer(query: &str) -> HResult<String> {
|
||||||
show_status(&(query.to_string() + ": "));
|
MINIBUFFER.lock()?.query(query)
|
||||||
write!(stdout(), "{}{}",
|
|
||||||
termion::cursor::Show,
|
|
||||||
termion::cursor::Save).unwrap();
|
|
||||||
stdout().flush().unwrap();
|
|
||||||
|
|
||||||
let mut buffer = "".to_string();
|
|
||||||
let mut pos = 0;
|
|
||||||
|
|
||||||
for key in stdin().events() {
|
|
||||||
|
|
||||||
match key {
|
|
||||||
Ok(Event::Key(key)) => match key {
|
|
||||||
Key::Esc | Key::Ctrl('c') => break,
|
|
||||||
Key::Char('\n') => {
|
|
||||||
if buffer == "" {
|
|
||||||
write!(stdout(), "{}", termion::cursor::Hide).unwrap();
|
|
||||||
stdout().flush().unwrap();
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
write!(stdout(), "{}", termion::cursor::Hide).unwrap();
|
|
||||||
stdout().flush().unwrap();
|
|
||||||
return Some(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Char('\t') => {
|
|
||||||
if !buffer.ends_with(" ") {
|
|
||||||
let part = buffer.rsplitn(2, " ").take(1)
|
|
||||||
.map(|s| s.to_string()).collect::<String>();
|
|
||||||
let completions = find_files(part.clone());
|
|
||||||
if !completions.is_empty() {
|
|
||||||
buffer = buffer[..buffer.len() - part.len()].to_string();
|
|
||||||
buffer.push_str(&completions[0]);
|
|
||||||
pos += &completions[0].len() - part.len();
|
|
||||||
} else {
|
|
||||||
let completions = find_bins(&part);
|
|
||||||
if !completions.is_empty() {
|
|
||||||
buffer = buffer[..buffer.len() - part.len()].to_string();
|
|
||||||
buffer.push_str(&completions[0]);
|
|
||||||
pos += &completions[0].len() - part.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buffer += "$s";
|
|
||||||
pos += 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Backspace => {
|
|
||||||
if pos != 0 {
|
|
||||||
buffer.remove(pos - 1);
|
|
||||||
pos -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Delete | Key::Ctrl('d') => {
|
|
||||||
if pos != buffer.len() {
|
|
||||||
buffer.remove(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Left | Key::Ctrl('b') => {
|
|
||||||
if pos != 0 {
|
|
||||||
pos -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Right | Key::Ctrl('f') => {
|
|
||||||
if pos != buffer.len() {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Key::Ctrl('a') => { pos = 0 },
|
|
||||||
Key::Ctrl('e') => { pos = buffer.len(); },
|
|
||||||
Key::Char(key) => {
|
|
||||||
buffer.insert(pos, key);
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
show_status(&(query.to_string() + ": " + &buffer));
|
|
||||||
|
|
||||||
write!(stdout(), "{}", termion::cursor::Restore).unwrap();
|
|
||||||
stdout().flush().unwrap();
|
|
||||||
if pos != 0 {
|
|
||||||
write!(stdout(),
|
|
||||||
"{}",
|
|
||||||
format!("{}", termion::cursor::Right(pos as u16))).unwrap();
|
|
||||||
}
|
|
||||||
stdout().flush().unwrap();
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_bins(comp_name: &str) -> Vec<String> {
|
pub fn find_bins(comp_name: &str) -> Vec<String> {
|
||||||
|
|
Loading…
Reference in New Issue