Implement native search and filter

pull/3/head
Arijit Basu 3 years ago
parent 7cbb9d2baf
commit 71a23e1f64
No known key found for this signature in database
GPG Key ID: 7D7BF809E7378863

2
Cargo.lock generated

@ -1123,7 +1123,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xplr"
version = "0.2.13"
version = "0.2.14"
dependencies = [
"anyhow",
"criterion",

@ -1,9 +1,9 @@
[package]
name = "xplr"
version = "0.2.13" # Update app.rs
version = "0.2.14" # Update app.rs
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
description = "An experimental, minimal, configurable TUI file explorer, stealing ideas from nnn and fzf."
description = "A hackable, minimal, fast TUI file explorer, stealing ideas from nnn and fzf"
license = "MIT"
readme = "README.md"
repository = "https://github.com/sayanarijit/xplr"

@ -1,64 +1,30 @@
An experimental, minimal, configurable TUI file explorer, stealing ideas from [`nnn`](https://github.com/jarun/nnn) and [`fzf`](https://github.com/junegunn/fzf).
<h1 align="center">xplr</h1>
![Screenshot](https://user-images.githubusercontent.com/11632726/109526906-1b555080-7ad9-11eb-9fd7-03e092220618.gif)
<p align="center">
A hackable, minimal, fast TUI file explorer, stealing ideas from <a href="https://github.com/jarun/nnn">nnn</a> and <a href="https://github.com/junegunn/fzf">fzf</a>.
</p>
<p align="center">
<a href="https://asciinema.org/a/3THQPXNVi801Yu8nWxO6qfUa4" target="_blank">
<img src="https://s4.gifyu.com/images/xplr.gif"/>
</a>
</p>
Example usage:
--------------
<h3 align="center">
[<a href="https://github.com/sayanarijit/xplr/wiki/Features">Features</a>]
[<a href="https://github.com/sayanarijit/xplr/wiki/Quickstart">Quickstart</a>]
[<a href="https://github.com/sayanarijit/xplr/wiki/Plugins">Plugins</a>]
[<a href="https://github.com/sayanarijit/xplr/wiki">Documentation</a>]
[<a href="https://github.com/sayanarijit/xplr/wiki/TODO">TODO</a>]
</h3>
```bash
# Edit file
vim "$(xplr)"
<br>
# Copy file(s)
cp "$(xplr)" "$(xplr)/"
# Search and move file
mv "$(fzf)" "$(xplr)/"
```
Though [xplr](https://github.com/sayanarijit/xplr) strives to be fast and minimalist, it's speciality is it's hackability.
As of now the fuctionality is pretty limited. You basically have to drop
into a shell (default key `s`) in the directory to do things like
create/delete/rename files. A lot of research and implementations to go.
Once you read the [documentation](https://github.com/sayanarijit/xplr/wiki), you should be able to configure the key bindings,
different run modes, and also the way it looks by modifying one single configuration file.
Although, it's currently satisfying my needs (for customization and speed)
with this vim plugin https://github.com/sayanarijit/xplr.vim.
Let's brainstorm
----------------
You can also experiment and help by suggesting ideas/opinions.
1. Install
```bash
cargo install xplr
```
2. Create the customizable config file (requires [`yq`](https://github.com/mikefarah/yq))
```bash
mkdir -p ~/.config/xplr
xplr | yq ".config" -y | tee ~/.config/xplr/config.yml
# When the app loads, press `#`
```
3. Check the key bindings in the config file.
4. Run `xplr`.
TODO research
-------------
- [ ] Research FIFO/socket options for better integration with other tools.
- [ ] Research saner configuration formats.
- [ ] Research saner key binding options.
- [ ] Research how to go beyond filesystem and explore any tree-like structure.
- [ ] Research ways to make it faster (load and run).
- [ ] Research ways to implement a plugin system.
- [ ] CLI options and help menu.
- [ ] Go beyond research and implement things.
If you come up with something cool, or if you feel it's lacking somewhere in portability, flexibility or performance, don't hesitate to
[start a conversation](https://github.com/sayanarijit/xplr/discussions/2).

@ -13,21 +13,21 @@ use std::fs;
use std::io;
use std::path::PathBuf;
pub const VERSION: &str = "v0.2.13"; // Update Cargo.toml
pub const VERSION: &str = "v0.2.14"; // Update Cargo.toml
pub const TEMPLATE_TABLE_ROW: &str = "TEMPLATE_TABLE_ROW";
pub const UNSUPPORTED_STR: &str = "???";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PipesConfig {
pub struct Pipe {
pub msg_in: String,
pub focus_out: String,
pub selection_out: String,
pub mode_out: String,
}
impl PipesConfig {
impl Pipe {
fn from_session_path(path: &String) -> Self {
let pipesdir = PathBuf::from(path).join("pipe");
@ -162,6 +162,237 @@ pub enum InternalMsg {
HandleKey(Key),
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum NodeFilter {
RelativePathIs,
RelativePathIsNot,
RelativePathDoesStartWith,
RelativePathDoesNotStartWith,
RelativePathDoesContain,
RelativePathDoesNotContain,
RelativePathDoesEndWith,
RelativePathDoesNotEndWith,
AbsolutePathIs,
AbsolutePathIsNot,
AbsolutePathDoesStartWith,
AbsolutePathDoesNotStartWith,
AbsolutePathDoesContain,
AbsolutePathDoesNotContain,
AbsolutePathDoesEndWith,
AbsolutePathDoesNotEndWith,
}
impl NodeFilter {
fn apply(&self, node: &Node, input: &String, case_sensitive: bool) -> bool {
match self {
Self::RelativePathIs => {
if case_sensitive {
&node.relative_path == input
} else {
node.relative_path.to_lowercase() == input.to_lowercase()
}
}
Self::RelativePathIsNot => {
if case_sensitive {
&node.relative_path != input
} else {
node.relative_path.to_lowercase() != input.to_lowercase()
}
}
Self::RelativePathDoesStartWith => {
if case_sensitive {
node.relative_path.starts_with(input)
} else {
node.relative_path
.to_lowercase()
.starts_with(&input.to_lowercase())
}
}
Self::RelativePathDoesNotStartWith => {
if case_sensitive {
!node.relative_path.starts_with(input)
} else {
!node
.relative_path
.to_lowercase()
.starts_with(&input.to_lowercase())
}
}
Self::RelativePathDoesContain => {
if case_sensitive {
node.relative_path.contains(input)
} else {
node.relative_path
.to_lowercase()
.contains(&input.to_lowercase())
}
}
Self::RelativePathDoesNotContain => {
if case_sensitive {
!node.relative_path.contains(input)
} else {
!node
.relative_path
.to_lowercase()
.contains(&input.to_lowercase())
}
}
Self::RelativePathDoesEndWith => {
if case_sensitive {
node.relative_path.ends_with(input)
} else {
node.relative_path
.to_lowercase()
.ends_with(&input.to_lowercase())
}
}
Self::RelativePathDoesNotEndWith => {
if case_sensitive {
!node.relative_path.ends_with(input)
} else {
!node
.relative_path
.to_lowercase()
.ends_with(&input.to_lowercase())
}
}
Self::AbsolutePathIs => {
if case_sensitive {
&node.absolute_path == input
} else {
node.absolute_path.to_lowercase() == input.to_lowercase()
}
}
Self::AbsolutePathIsNot => {
if case_sensitive {
&node.absolute_path != input
} else {
node.absolute_path.to_lowercase() != input.to_lowercase()
}
}
Self::AbsolutePathDoesStartWith => {
if case_sensitive {
node.absolute_path.starts_with(input)
} else {
node.absolute_path
.to_lowercase()
.starts_with(&input.to_lowercase())
}
}
Self::AbsolutePathDoesNotStartWith => {
if case_sensitive {
!node.absolute_path.starts_with(input)
} else {
!node
.absolute_path
.to_lowercase()
.starts_with(&input.to_lowercase())
}
}
Self::AbsolutePathDoesContain => {
if case_sensitive {
node.absolute_path.contains(input)
} else {
node.absolute_path
.to_lowercase()
.contains(&input.to_lowercase())
}
}
Self::AbsolutePathDoesNotContain => {
if case_sensitive {
!node.absolute_path.contains(input)
} else {
!node
.absolute_path
.to_lowercase()
.contains(&input.to_lowercase())
}
}
Self::AbsolutePathDoesEndWith => {
if case_sensitive {
node.absolute_path.ends_with(input)
} else {
node.absolute_path
.to_lowercase()
.ends_with(&input.to_lowercase())
}
}
Self::AbsolutePathDoesNotEndWith => {
if case_sensitive {
!node.absolute_path.ends_with(input)
} else {
!node
.absolute_path
.to_lowercase()
.ends_with(&input.to_lowercase())
}
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct NodeFilterApplicable {
filter: NodeFilter,
input: String,
#[serde(default)]
case_sensitive: bool,
}
impl NodeFilterApplicable {
pub fn new(filter: NodeFilter, input: String, case_sensitive: bool) -> Self {
Self {
filter,
input,
case_sensitive,
}
}
fn apply(&self, node: &Node) -> bool {
self.filter.apply(node, &self.input, self.case_sensitive)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct NodeFilterFromInputString {
filter: NodeFilter,
#[serde(default)]
case_sensitive: bool,
}
#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ExplorerConfig {
filters: Vec<NodeFilterApplicable>,
}
impl ExplorerConfig {
pub fn apply(&self, node: &Node) -> bool {
self.filters.iter().all(|f| f.apply(node))
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum ExternalMsg {
Explore,
@ -191,6 +422,11 @@ pub enum ExternalMsg {
UnSelect,
ToggleSelection,
ClearSelection,
AddNodeFilter(NodeFilterApplicable),
RemoveNodeFilter(NodeFilterApplicable),
ToggleNodeFilter(NodeFilterApplicable),
AddNodeFilterFromInputString(NodeFilterFromInputString),
ResetNodeFilters,
PrintResultAndQuit,
PrintAppStateAndQuit,
Debug(String),
@ -261,7 +497,8 @@ pub struct App {
input_buffer: Option<String>,
pid: u32,
session_path: String,
pipes: PipesConfig,
pipe: Pipe,
explorer_config: ExplorerConfig,
}
impl App {
@ -310,6 +547,15 @@ impl App {
.to_string_lossy()
.to_string();
let mut explorer_config = ExplorerConfig::default();
if !config.general.show_hidden {
explorer_config.filters.push(NodeFilterApplicable::new(
NodeFilter::RelativePathDoesNotStartWith,
".".into(),
Default::default(),
));
}
Ok(Self {
config,
pwd,
@ -321,7 +567,8 @@ impl App {
input_buffer: Default::default(),
pid,
session_path: session_path.clone(),
pipes: PipesConfig::from_session_path(&session_path),
pipe: Pipe::from_session_path(&session_path),
explorer_config,
})
}
}
@ -389,6 +636,13 @@ impl App {
ExternalMsg::UnSelect => self.un_select(),
ExternalMsg::ToggleSelection => self.toggle_selection(),
ExternalMsg::ClearSelection => self.clear_selection(),
ExternalMsg::AddNodeFilter(f) => self.add_node_filter(f),
ExternalMsg::AddNodeFilterFromInputString(f) => {
self.add_node_filter_from_input_string(f)
}
ExternalMsg::RemoveNodeFilter(f) => self.remove_node_filter(f),
ExternalMsg::ToggleNodeFilter(f) => self.toggle_node_filter(f),
ExternalMsg::ResetNodeFilters => self.reset_node_filters(),
ExternalMsg::PrintResultAndQuit => self.print_result_and_quit(),
ExternalMsg::PrintAppStateAndQuit => self.print_app_state_and_quit(),
ExternalMsg::Debug(path) => self.debug(&path),
@ -600,6 +854,7 @@ impl App {
fn switch_mode(mut self, mode: &String) -> Result<Self> {
if let Some(mode) = self.config.modes.get(mode) {
self.input_buffer = None;
self.mode = mode.to_owned();
self.msg_out.push_back(MsgOut::Refresh);
};
@ -627,7 +882,12 @@ impl App {
fn un_select(mut self) -> Result<Self> {
if let Some(n) = self.focused_node().map(|n| n.to_owned()) {
self.selection = self.selection.into_iter().filter(|s| s != &n).collect();
self.selection = self
.selection
.clone()
.into_iter()
.filter(|s| s != &n)
.collect();
self.msg_out.push_back(MsgOut::Refresh);
}
Ok(self)
@ -650,6 +910,61 @@ impl App {
Ok(self)
}
fn add_node_filter(mut self, filter: NodeFilterApplicable) -> Result<Self> {
self.explorer_config.filters.push(filter);
self.msg_out.push_back(MsgOut::Explore);
Ok(self)
}
fn add_node_filter_from_input_string(
mut self,
filter: NodeFilterFromInputString,
) -> Result<Self> {
if let Some(input) = self.input_buffer() {
self.explorer_config.filters.push(NodeFilterApplicable::new(
filter.filter,
input,
filter.case_sensitive,
));
self.msg_out.push_back(MsgOut::Explore);
};
Ok(self)
}
fn remove_node_filter(mut self, filter: NodeFilterApplicable) -> Result<Self> {
self.explorer_config.filters = self
.explorer_config
.filters
.into_iter()
.filter(|f| f != &filter)
.collect();
self.msg_out.push_back(MsgOut::Explore);
Ok(self)
}
fn toggle_node_filter(self, filter: NodeFilterApplicable) -> Result<Self> {
if self.explorer_config.filters.contains(&filter) {
self.remove_node_filter(filter)
} else {
self.add_node_filter(filter)
}
}
fn reset_node_filters(mut self) -> Result<Self> {
self.explorer_config.filters.clear();
if !self.config.general.show_hidden {
self.explorer_config.filters.push(NodeFilterApplicable::new(
NodeFilter::RelativePathDoesNotStartWith,
".".into(),
Default::default(),
));
};
self.msg_out.push_back(MsgOut::Explore);
Ok(self)
}
fn print_result_and_quit(mut self) -> Result<Self> {
self.msg_out.push_back(MsgOut::PrintResultAndQuit);
Ok(self)
@ -709,8 +1024,8 @@ impl App {
}
/// Get a reference to the app's pipes.
pub fn pipes(&self) -> &PipesConfig {
&self.pipes
pub fn pipe(&self) -> &Pipe {
&self.pipe
}
/// Get a reference to the app's pid.
@ -726,6 +1041,7 @@ impl App {
pub fn refresh_selection(mut self) -> Result<Self> {
self.selection = self
.selection
.clone()
.into_iter()
.filter(|n| PathBuf::from(&n.absolute_path).exists())
.collect();
@ -747,4 +1063,9 @@ impl App {
.collect::<Vec<String>>()
.join("\n")
}
/// Get a reference to the app's explorer config.
pub fn explorer_config(&self) -> &ExplorerConfig {
&self.explorer_config
}
}

@ -46,19 +46,19 @@ impl Default for FileTypesConfig {
fn default() -> Self {
FileTypesConfig {
directory: FileTypeConfig {
icon: "d".into(),
icon: "ð".into(),
style: Style::default()
.add_modifier(Modifier::BOLD)
.fg(Color::Blue),
},
file: FileTypeConfig {
icon: "f".into(),
icon: "ƒ".into(),
style: Default::default(),
},
symlink: FileTypeConfig {
icon: "s".into(),
icon: "§".into(),
style: Style::default()
.add_modifier(Modifier::ITALIC)
.fg(Color::Cyan),
@ -293,31 +293,11 @@ impl Default for KeyBindings {
ctrl-f:
help: search [/]
messages:
- Call:
command: bash
args:
- -c
- |
PTH=$(echo -e "${XPLR_DIRECTORY_NODES:?}" | fzf)
if [ -d "$PTH" ]; then
echo "ChangeDirectory: ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}"
elif [ -f "$PTH" ]; then
echo "FocusPath: ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}"
fi
- SwitchMode: search
/:
messages:
- Call:
command: bash
args:
- -c
- |
PTH=$(echo -e "${XPLR_DIRECTORY_NODES:?}" | fzf)
if [ -d "$PTH" ]; then
echo "ChangeDirectory: ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}"
elif [ -f "$PTH" ]; then
echo "FocusPath: ${PTH:?}" >> "${XPLR_PIPE_MSG_IN:?}"
fi
- SwitchMode: search
d:
help: delete
@ -340,6 +320,13 @@ impl Default for KeyBindings {
- ToggleSelection
- FocusNext
".":
help: show hidden
messages:
- ToggleNodeFilter:
filter: RelativePathDoesNotStartWith
input: .
enter:
help: quit with result
messages:
@ -429,6 +416,69 @@ pub struct Config {
impl Default for Config {
fn default() -> Self {
let search_mode: Mode = serde_yaml::from_str(
r###"
name: search
key_bindings:
on_key:
enter:
help: focus
messages:
- ResetNodeFilters
- SwitchMode: default
up:
help: up
messages:
- FocusPrevious
down:
help: down
messages:
- FocusNext
right:
help: enter
messages:
- Enter
- ResetInputBuffer
- ResetNodeFilters
- SwitchMode: default
left:
help: back
messages:
- Back
- ResetInputBuffer
- SwitchMode: default
esc:
help: cancel
messages:
- ResetNodeFilters
- SwitchMode: default
backspace:
help: clear
messages:
- ResetInputBuffer
- ResetNodeFilters
ctrl-c:
help: cancel & quit
messages:
- Terminate
default:
messages:
- BufferStringFromKey
- AddNodeFilterFromInputString:
filter: RelativePathDoesContain
case_sensitive: false
"###,
)
.unwrap();
let goto_mode: Mode = serde_yaml::from_str(
r###"
name: go to
@ -515,9 +565,9 @@ impl Default for Config {
args:
- -c
- |
while IFS= read -r line; do
(while IFS= read -r line; do
cp -v "${line:?}" ./
done <<< "${XPLR_SELECTION:?}"
done <<< "${XPLR_SELECTION:?}")
read -p "[enter to continue]"
- ClearSelection
- Explore
@ -531,9 +581,9 @@ impl Default for Config {
args:
- -c
- |
while IFS= read -r line; do
(while IFS= read -r line; do
mv -v "${line:?}" ./
done <<< "${XPLR_SELECTION:?}"
done <<< "${XPLR_SELECTION:?}")
read -p "[enter to continue]"
- Explore
- SwitchMode: default
@ -559,35 +609,35 @@ impl Default for Config {
help: to up [k]
messages:
- FocusPreviousByRelativeIndexFromInput
- ResetInputBuffer
- SwitchMode: default
k:
messages:
- FocusPreviousByRelativeIndexFromInput
- ResetInputBuffer
- SwitchMode: default
down:
help: to down [j]
messages:
- FocusNextByRelativeIndexFromInput
- ResetInputBuffer
- SwitchMode: default
j:
messages:
- FocusNextByRelativeIndexFromInput
- ResetInputBuffer
- SwitchMode: default
enter:
help: to index
messages:
- FocusByIndexFromInput
- ResetInputBuffer
- SwitchMode: default
backspace:
help: clear
messages:
- ResetInputBuffer
ctrl-c:
help: cancel & quit
messages:
@ -600,7 +650,6 @@ impl Default for Config {
default:
messages:
- ResetInputBuffer
- SwitchMode: default
"###,
)
@ -620,7 +669,6 @@ impl Default for Config {
- -c
- |
touch "${XPLR_INPUT_BUFFER:?}"
- ResetInputBuffer
- SwitchMode: default
- Explore
@ -633,14 +681,17 @@ impl Default for Config {
- -c
- |
mkdir -p "${XPLR_INPUT_BUFFER:?}"
- ResetInputBuffer
- SwitchMode: default
- Explore
backspace:
help: clear
messages:
- ResetInputBuffer
esc:
help: cancel
messages:
- ResetInputBuffer
- SwitchMode: default
ctrl-c:
@ -668,14 +719,14 @@ impl Default for Config {
args:
- -c
- |
while IFS= read -r line; do
(while IFS= read -r line; do
if [ -d "$line" ]; then
rmdir -v "${line:?}"
else
rm -v "${line:?}"
fi
done <<< "${XPLR_RESULT:?}"
read -p "[Enter to continue]"
done <<< "${XPLR_RESULT:?}")
read -p "[enter to continue]"
- SwitchMode: default
- Explore
@ -687,7 +738,7 @@ impl Default for Config {
args:
- -c
- |
echo -e "${XPLR_RESULT:?}" | xargs -l rm -rfv
(echo -e "${XPLR_RESULT:?}" | xargs -l rm -rfv)
read -p "[enter to continue]"
- SwitchMode: default
- Explore
@ -711,6 +762,7 @@ impl Default for Config {
modes.insert("create".into(), create_mode);
modes.insert("delete".into(), delete_mode);
modes.insert("action".into(), action_mode);
modes.insert("search".into(), search_mode);
modes.insert("selection ops".into(), selection_ops_mode);
Self {

@ -1,4 +1,5 @@
use crate::app::DirectoryBuffer;
use crate::app::ExplorerConfig;
use crate::app::Node;
use crate::app::Task;
use crate::app::{InternalMsg, MsgIn};
@ -7,10 +8,16 @@ use std::path::PathBuf;
use std::sync::mpsc::Sender;
use std::thread;
pub fn explore(parent: String, focused_path: Option<String>, tx: Sender<Task>) {
pub fn explore(
config: ExplorerConfig,
parent: String,
focused_path: Option<String>,
tx: Sender<Task>,
) {
let path = PathBuf::from(&parent);
let path_cloned = path.clone();
let tx_cloned = tx.clone();
let config_cloned = config.clone();
thread::spawn(move || {
let nodes: Vec<Node> = fs::read_dir(&path)
@ -24,6 +31,7 @@ pub fn explore(parent: String, focused_path: Option<String>, tx: Sender<Task>) {
})
})
.map(|name| Node::new(parent.clone(), name))
.filter(|n| config.apply(n))
.collect();
let focus_index = if let Some(focus) = focused_path {
@ -49,6 +57,7 @@ pub fn explore(parent: String, focused_path: Option<String>, tx: Sender<Task>) {
if let Some(grand_parent) = path_cloned.parent() {
explore(
config_cloned,
grand_parent.to_string_lossy().to_string(),
path_cloned
.file_name()

@ -1,6 +1,6 @@
use anyhow::Result;
use crossterm::terminal as term;
use crossterm::execute;
use crossterm::terminal as term;
use handlebars::Handlebars;
use std::fs;
use std::io::prelude::*;
@ -51,9 +51,14 @@ fn main() -> Result<()> {
.file_name()
.map(|n| n.to_string_lossy().to_string())
});
explorer::explore(app.pwd().clone(), focused_path, tx_msg_in.clone());
explorer::explore(
app.explorer_config().clone(),
app.pwd().clone(),
focused_path,
tx_msg_in.clone(),
);
pipe_reader::keep_reading(app.pipes().msg_in.clone(), tx_msg_in.clone());
pipe_reader::keep_reading(app.pipe().msg_in.clone(), tx_msg_in.clone());
let (tx_event_reader, rx_event_reader) = mpsc::channel();
event_reader::keep_reading(tx_msg_in.clone(), rx_event_reader);
@ -83,6 +88,7 @@ fn main() -> Result<()> {
app::MsgOut::Explore => {
explorer::explore(
app.explorer_config().clone(),
app.pwd().clone(),
app.focused_node().map(|n| n.relative_path.clone()),
tx_msg_in.clone(),
@ -92,6 +98,7 @@ fn main() -> Result<()> {
app::MsgOut::Refresh => {
if app.pwd() != &last_pwd {
explorer::explore(
app.explorer_config().clone(),
app.pwd().clone(),
app.focused_node().map(|n| n.relative_path.clone()),
tx_msg_in.clone(),
@ -108,7 +115,7 @@ fn main() -> Result<()> {
.map(|n| n.absolute_path.clone())
.unwrap_or_default();
fs::write(&app.pipes().focus_out, focused)?;
fs::write(&app.pipe().focus_out, focused)?;
app = app.refresh_selection()?;
@ -119,9 +126,9 @@ fn main() -> Result<()> {
.collect::<Vec<String>>()
.join("\n");
fs::write(&app.pipes().selection_out, selection)?;
fs::write(&app.pipe().selection_out, selection)?;
fs::write(&app.pipes().mode_out, &app.mode().name)?;
fs::write(&app.pipe().mode_out, &app.mode().name)?;
}
app::MsgOut::Call(cmd) => {
@ -163,9 +170,9 @@ fn main() -> Result<()> {
})
.unwrap_or_default();
let pipe_msg_in = app.pipes().msg_in.clone();
let pipe_focus_out = app.pipes().focus_out.clone();
let pipe_selection_out = app.pipes().selection_out.clone();
let pipe_msg_in = app.pipe().msg_in.clone();
let pipe_focus_out = app.pipe().focus_out.clone();
let pipe_selection_out = app.pipe().selection_out.clone();
let app_yaml = serde_yaml::to_string(&app)?;
let session_path = app.session_path();
@ -216,6 +223,8 @@ fn main() -> Result<()> {
execute!(terminal.backend_mut(), term::LeaveAlternateScreen)?;
terminal.show_cursor()?;
fs::remove_dir_all(app.session_path())?;
if let Some(out) = output {
println!("{}", out);
}

Loading…
Cancel
Save