@ -2,7 +2,6 @@ use crate::config::Config;
use crate ::config ::Hooks ;
use crate ::config ::Mode ;
pub use crate ::directory_buffer ::DirectoryBuffer ;
use crate ::dirs ;
use crate ::explorer ;
use crate ::input ::{ InputOperation , Key } ;
use crate ::lua ;
@ -135,10 +134,6 @@ impl History {
self
}
fn peek ( & self ) -> Option < & String > {
self . paths . get ( self . loc )
}
fn push ( mut self , path : String ) -> Self {
if self . peek ( ) ! = Some ( & path ) {
self . paths = self . paths . into_iter ( ) . take ( self . loc + 1 ) . collect ( ) ;
@ -171,47 +166,8 @@ impl History {
self . cleanup ( )
}
fn _is_deepest_dir ( & self , path : & str ) -> bool {
return ! self
. paths
. iter ( )
. any ( | p | p . ends_with ( '/' ) & & p . starts_with ( path ) & & path ! = p ) ;
}
fn _uniq_deep_dirs ( & self ) -> IndexSet < String > {
self . paths
. clone ( )
. into_iter ( )
. filter ( | p | p . ends_with ( '/' ) & & self . _is_deepest_dir ( p ) )
. collect ::< IndexSet < String > > ( )
}
fn visit_next_deep_branch ( self , pwd : & str ) -> Self {
let uniq_deep_dirs = self . _uniq_deep_dirs ( ) ;
if let Some ( path ) = uniq_deep_dirs
. iter ( )
. skip_while ( | p | p . trim_end_matches ( '/' ) ! = pwd )
. nth ( 1 )
{
self . push ( path . to_string ( ) )
} else {
self
}
}
fn visit_previous_deep_branch ( self , pwd : & str ) -> Self {
let uniq_deep_dirs = self . _uniq_deep_dirs ( ) ;
if let Some ( path ) = uniq_deep_dirs
. iter ( )
. rev ( )
. skip_while ( | p | p . trim_end_matches ( '/' ) ! = pwd )
. nth ( 1 )
{
self . push ( path . to_string ( ) )
} else {
self
}
fn peek ( & self ) -> Option < & String > {
self . paths . get ( self . loc )
}
}
@ -298,8 +254,8 @@ impl App {
let config_file = if let Some ( path ) = config_file {
Some ( path )
} else if let Some ( dir ) = dirs ::config _dir( ) {
let path = dir . join ( " xplr/init.lua") ;
} else if let Some ( dir ) = dirs ::home _dir( ) {
let path = dir . join ( " .config/ xplr/init.lua") ;
if path . exists ( ) {
Some ( path )
} else {
@ -314,7 +270,9 @@ impl App {
}
} ;
let config_files = config_file . into_iter ( ) . chain ( extra_config_files ) ;
let config_files = config_file
. into_iter ( )
. chain ( extra_config_files . into_iter ( ) ) ;
let mut load_errs = vec! [ ] ;
for config_file in config_files {
@ -335,12 +293,12 @@ impl App {
& config
. general
. initial_mode
. clone ( )
. to_owned ( )
. unwrap_or_else ( | | "default" . into ( ) ) ,
) {
Some ( m ) = > m . clone ( ) . sanitized (
config . general . read_only ,
config . general . global_key_bindings . clone ( ) ,
config . general . global_key_bindings . to_owned ( ) ,
) ,
None = > {
bail ! ( "'default' mode is missing" )
@ -351,7 +309,7 @@ impl App {
& config
. general
. initial_layout
. clone ( )
. to_owned ( )
. unwrap_or_else ( | | "default" . into ( ) ) ,
) {
Some ( l ) = > l . clone ( ) ,
@ -362,6 +320,7 @@ impl App {
let pid = std ::process ::id ( ) ;
let mut session_path = dirs ::runtime_dir ( )
. unwrap_or_else ( env ::temp_dir )
. join ( "xplr" )
. join ( "session" )
. join ( pid . to_string ( ) )
@ -387,7 +346,7 @@ impl App {
}
if let Some ( sorters ) = & config . general . initial_sorting {
explorer_config . sorters . clone _from ( sorters ) ;
explorer_config . sorters = sorters . clone ( ) ;
} ;
let hostname = gethostname ( ) . to_string_lossy ( ) . to_string ( ) ;
@ -414,12 +373,6 @@ impl App {
prompt : config . general . prompt . format . clone ( ) . unwrap_or_default ( ) ,
} ;
let hist = if & pwd = = "/" {
pwd . clone ( )
} else {
format! ( "{0}/" , & pwd )
} ;
let mut app = Self {
bin ,
version : VERSION . to_string ( ) ,
@ -441,7 +394,7 @@ impl App {
explorer_config ,
logs : Default ::default ( ) ,
logs_hidden : Default ::default ( ) ,
history : History ::default ( ) . push ( hist ) ,
history : Default ::default ( ) ,
last_modes : Default ::default ( ) ,
hostname ,
hooks ,
@ -471,6 +424,11 @@ impl App {
. unwrap_or_default ( )
}
fn enqueue ( mut self , task : Task ) -> Self {
self . msg_out . push_back ( MsgOut ::Enque ( task ) ) ;
self
}
pub fn handle_batch_external_msgs ( mut self , msgs : Vec < ExternalMsg > ) -> Result < Self > {
for task in msgs
. into_iter ( )
@ -499,23 +457,15 @@ impl App {
self . add_last_focus ( parent , focus_path )
}
InternalMsg ::HandleKey ( key ) = > self . handle_key ( key ) ,
InternalMsg ::RefreshSelection = > self . refresh_selection ( ) ,
}
}
fn handle_external ( mut self , msg : ExternalMsg , key : Option < Key > ) -> Result < Self > {
let is_msg_read_only = msg . is_read_only ( ) ;
if self . config . general . read_only & & ! is_msg_read_only {
fn handle_external ( self , msg : ExternalMsg , key : Option < Key > ) -> Result < Self > {
if self . config . general . read_only & & ! msg . is_read_only ( ) {
self . log_error ( "could not execute code in read-only mode." . into ( ) )
} else {
use ExternalMsg ::* ;
if ! is_msg_read_only {
// We don't want to operate on imaginary paths.
self = self . refresh_selection ( ) ? ;
}
self = match msg {
match msg {
ExplorePwd = > self . explore_pwd ( ) ,
ExploreParentsAsync = > self . explore_parents_async ( ) ,
ExplorePwdAsync = > self . explore_pwd_async ( ) ,
@ -552,8 +502,6 @@ impl App {
Back = > self . back ( ) ,
LastVisitedPath = > self . last_visited_path ( ) ,
NextVisitedPath = > self . next_visited_path ( ) ,
PreviousVisitedDeepBranch = > self . previous_visited_deep_branch ( ) ,
NextVisitedDeepBranch = > self . next_visited_deep_branch ( ) ,
FollowSymlink = > self . follow_symlink ( ) ,
SetVroot ( p ) = > self . set_vroot ( & p ) ,
UnsetVroot = > self . unset_vroot ( ) ,
@ -671,20 +619,9 @@ impl App {
PrintAppStateAndQuit = > self . print_app_state_and_quit ( ) ,
Debug ( path ) = > self . debug ( path ) ,
Terminate = > bail ! ( "" ) ,
} ? ;
if ! is_msg_read_only {
// We don't want to keep imaginary paths in the selection.
// But the write action is probably still in queue.
// So we need to refresh selection after the write action.
let msg = InternalMsg ::RefreshSelection ;
let msg = MsgIn ::Internal ( msg ) ;
let task = Task ::new ( msg , None ) ;
self . msg_out . push_back ( MsgOut ::Enqueue ( task ) ) ;
}
Ok ( self )
}
} ?
. refresh_selection ( )
}
fn handle_key ( mut self , key : Key ) -> Result < Self > {
@ -733,11 +670,7 @@ impl App {
} ) ;
for msg in msgs {
// Rename breaks without enqueue
let external = MsgIn ::External ( msg ) ;
let task = Task ::new ( external , Some ( key ) ) ;
let msg_out = MsgOut ::Enqueue ( task ) ;
self . msg_out . push_back ( msg_out ) ;
self = self . enqueue ( Task ::new ( MsgIn ::External ( msg ) , Some ( key ) ) ) ;
}
Ok ( self )
@ -820,6 +753,7 @@ impl App {
fn focus_previous ( mut self ) -> Result < Self > {
let bounded = self . config . general . enforce_bounded_index_navigation ;
if let Some ( dir ) = self . directory_buffer_mut ( ) {
dir . focus = if dir . focus = = 0 {
if bounded {
@ -904,6 +838,7 @@ impl App {
fn focus_next ( mut self ) -> Result < Self > {
let bounded = self . config . general . enforce_bounded_index_navigation ;
if let Some ( dir ) = self . directory_buffer_mut ( ) {
dir . focus = if ( dir . focus + 1 ) = = dir . total {
if bounded {
@ -915,7 +850,6 @@ impl App {
dir . focus + 1
}
} ;
Ok ( self )
}
@ -994,7 +928,7 @@ impl App {
fn follow_symlink ( self ) -> Result < Self > {
if let Some ( pth ) = self
. focused_node ( )
. and_then ( | n | n . symlink . clone ( ) . map ( | s | s . absolute_path ) )
. and_then ( | n | n . symlink . to_owned ( ) . map ( | s | s . absolute_path ) )
{
self . focus_path ( & pth , true )
} else {
@ -1082,23 +1016,19 @@ impl App {
}
fn enter ( self ) -> Result < Self > {
if let Some ( node ) = self . focused_node ( ) {
if node . is_dir | | node . symlink . as_ref ( ) . map ( | s | s . is_dir ) . unwrap_or ( false ) {
let path = node . absolute_path . clone ( ) ;
self . change_directory ( & path , true )
} else {
Ok ( self )
}
if let Some ( path ) = self . focused_node ( ) . map ( | n | n . absolute_path . clone ( ) ) {
self . change_directory ( & path , true )
} else {
Ok ( self )
}
}
fn back ( self ) -> Result < Self > {
let pwd = self . pwd . clone ( ) ;
if let Some ( p ) = PathBuf ::from ( & pwd ) . parent ( ) . and_then ( | p | p . to_str ( ) ) {
self . change_directory ( p , false )
. and_then ( | a | a . focus_path ( & pwd , true ) )
if let Some ( p ) = PathBuf ::from ( self . pwd . clone ( ) )
. parent ( )
. and_then ( | p | p . to_str ( ) )
{
self . change_directory ( p , true )
} else {
Ok ( self )
}
@ -1130,24 +1060,6 @@ impl App {
}
}
fn previous_visited_deep_branch ( mut self ) -> Result < Self > {
self . history = self . history . visit_previous_deep_branch ( & self . pwd ) ;
if let Some ( path ) = self . history . peek ( ) . cloned ( ) {
self . change_directory ( path . trim_end_matches ( '/' ) , false )
} else {
Ok ( self )
}
}
fn next_visited_deep_branch ( mut self ) -> Result < Self > {
self . history = self . history . visit_next_deep_branch ( & self . pwd ) ;
if let Some ( path ) = self . history . peek ( ) . cloned ( ) {
self . change_directory ( path . trim_end_matches ( '/' ) , false )
} else {
Ok ( self )
}
}
fn set_input_prompt ( mut self , p : String ) -> Result < Self > {
self . input . prompt = p ;
Ok ( self )
@ -1300,7 +1212,7 @@ impl App {
}
pub fn scroll_up_half ( mut self ) -> Result < Self > {
self . msg_out . push_back ( MsgOut ::ScrollUp Half ) ;
self . msg_out . push_back ( MsgOut ::ScrollUp ) ;
Ok ( self )
}
@ -1381,7 +1293,7 @@ impl App {
self = self . push_mode ( ) ;
self . mode = mode . sanitized (
self . config . general . read_only ,
self . config . general . global_key_bindings . clone ( ) ,
self . config . general . global_key_bindings . to_owned ( ) ,
) ;
// Hooks
@ -1406,7 +1318,7 @@ impl App {
self = self . push_mode ( ) ;
self . mode = mode . sanitized (
self . config . general . read_only ,
self . config . general . global_key_bindings . clone ( ) ,
self . config . general . global_key_bindings . to_owned ( ) ,
) ;
// Hooks
@ -1433,7 +1345,7 @@ impl App {
fn switch_layout_builtin ( mut self , layout : & str ) -> Result < Self > {
if let Some ( l ) = self . config . layouts . builtin . get ( layout ) {
self . layout = l . clone ( ) ;
self . layout = l . to_owned ( ) ;
// Hooks
if ! self . hooks . on_layout_switch . is_empty ( ) {
@ -1449,7 +1361,7 @@ impl App {
fn switch_layout_custom ( mut self , layout : & str ) -> Result < Self > {
if let Some ( l ) = self . config . layouts . get_custom ( layout ) {
self . layout = l . clone ( ) ;
self . layout = l . to_owned ( ) ;
// Hooks
if ! self . hooks . on_layout_switch . is_empty ( ) {
@ -1556,8 +1468,6 @@ impl App {
if dir . parent = = self . pwd {
self . directory_buffer = Some ( dir ) ;
// Might as well refresh the selection
self = self . refresh_selection ( ) ? ;
} ;
Ok ( self )
@ -1573,15 +1483,9 @@ impl App {
}
pub fn select ( mut self ) -> Result < Self > {
let count = self . selection . len ( ) ;
if let Some ( n ) = self . focused_node ( ) . cloned ( ) {
if let Some ( n ) = self . focused_node ( ) . map ( | n | n . to_owned ( ) ) {
self . selection . insert ( n ) ;
}
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
@ -1589,59 +1493,36 @@ impl App {
let path = PathBuf ::from ( path ) . absolutize ( ) ? . to_path_buf ( ) ;
let parent = path . parent ( ) . map ( | p | p . to_string_lossy ( ) . to_string ( ) ) ;
let filename = path . file_name ( ) . map ( | p | p . to_string_lossy ( ) . to_string ( ) ) ;
let count = self . selection . len ( ) ;
if let ( Some ( p ) , Some ( n ) ) = ( parent , filename ) {
self . selection . insert ( Node ::new ( p , n ) ) ;
}
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
pub fn select_all ( mut self ) -> Result < Self > {
let count = self . selection . len ( ) ;
if let Some ( d ) = self . directory_buffer . as_ref ( ) {
self . selection . extend ( d . nodes . clone ( ) ) ;
} ;
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
pub fn un_select_path ( mut self , path : String ) -> Result < Self > {
let pathbuf = PathBuf ::from ( path ) . absolutize ( ) ? . to_path_buf ( ) ;
let count = self . selection . len ( ) ;
self . selection
. retain ( | n | PathBuf ::from ( & n . absolute_path ) ! = pathbuf ) ;
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
pub fn un_select ( mut self ) -> Result < Self > {
let count = self . selection . len ( ) ;
if let Some ( n ) = self . focused_node ( ) . cloned ( ) {
if let Some ( n ) = self . focused_node ( ) . map ( | n | n . to_owned ( ) ) {
self . selection
. retain ( | s | s . absolute_path ! = n . absolute_path ) ;
}
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
pub fn un_select_all ( mut self ) -> Result < Self > {
let count = self . selection . len ( ) ;
if let Some ( d ) = self . directory_buffer . as_ref ( ) {
d . nodes . clone ( ) . into_iter ( ) . for_each ( | n | {
self . selection
@ -1649,10 +1530,6 @@ impl App {
} ) ;
} ;
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
@ -1690,11 +1567,7 @@ impl App {
}
fn clear_selection ( mut self ) -> Result < Self > {
let count = self . selection . len ( ) ;
self . selection . clear ( ) ;
if self . selection . len ( ) ! = count {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
@ -1803,7 +1676,7 @@ impl App {
. config
. general
. initial_sorting
. clone ( )
. to_owned ( )
. unwrap_or_default ( ) ;
Ok ( self )
}
@ -2025,28 +1898,12 @@ impl App {
format! ( "{0}\n" , & self . mode . name )
}
// This is a performance heavy function. Use it only when necessary.
fn refresh_selection ( mut self ) -> Result < Self > {
let count = self . selection . len ( ) ;
self . selection . retain ( | n | {
let p = PathBuf ::from ( & n . absolute_path ) ;
// Should be able to retain broken symlink
p . exists ( ) | | p . symlink_metadata ( ) . is_ok ( )
} ) ;
if count ! = self . selection . len ( ) {
self = self . on_selection_change ( ) ? ;
}
Ok ( self )
}
fn on_selection_change ( mut self ) -> Result < Self > {
if ! self . hooks . on_selection_change . is_empty ( ) {
let msgs = self . hooks . on_selection_change . clone ( ) ;
self = self . handle_batch_external_msgs ( msgs ) ?
}
Ok ( self )
}
@ -2105,10 +1962,8 @@ impl App {
let read_only = self . config . general . read_only ;
let global_kb = & self . config . general . global_key_bindings ;
let modes = builtin . into_iter ( ) . chain ( custom ) ;
std ::iter ::once ( ( self . mode . name . clone ( ) , self . mode . clone ( ) ) )
. chain ( modes )
builtin . into_iter ( )
. chain ( custom . into_iter ( ) )
. map ( | ( name , mode ) | {
( name , mode . sanitized ( read_only , global_kb . clone ( ) ) )
} )