Compare commits

...

842 Commits

Author SHA1 Message Date
Arijit Basu 68fb6fa1a2 Fix docs
Fixes: https://github.com/sayanarijit/xplr/issues/717
1 week ago
Arijit Basu a82ea6a3e5
Fix CI 1 week ago
Arijit Basu e13dd21728 Upgrade
Fixes: https://github.com/sayanarijit/xplr/issues/718
1 week ago
Arijit Basu 182a201b0d
Limit scroll padding 2 months ago
Arijit Basu e0b0466e42
v0.21.8 (#716)
- Added vim-like scrolling as the default scrolling method. Set
`xplr.config.general.paginated_scrolling = false` to disable ~ by
@ElSamhaa & @sayanarijit.
- Added `xplr.config.general.scroll_padding` config option to set the
padding in vim-like scrolling ~ by @ElSamhaa & @sayanarijit.
- Fixed some color rendering issues ~ by @har7an.
- Added feature flag so that xplr can be build with system Lua ~ by
@nekopsykose.
- Fixed `ScrollUpHalf` behavior.
- `xplr.util.lscolor()` won't return nil anymore.
- Arguments passed to the custom dynamic layout Lua function will
include `scrolltop` field.
- Fixed node_type resolution for directories with `.` in their name ~ by
@abhinavnatarajan.
- Dependency updates.
2 months ago
Arijit Basu 805e1594ed
Fix vim scrolling 2 months ago
Arijit Basu 41648ced34 Linting fixes 2 months ago
Arijit Basu 89d7bccce8 Update docs 2 months ago
Arijit Basu e15c1e8a8c
Lock ratatui 2 months ago
Arijit Basu 8afdf9e478
Fix node type resolution (#714)
Fixes #712 and #713.
2 months ago
Abhinav Natarajan a48dae008c Fix node type resolution
Fix node_type for directory with extension
2 months ago
Arijit Basu ad8afa9d38 Update deps 2 months ago
Arijit Basu c2a11059c8
Add yazi an alternative 2 months ago
Arijit Basu 6d7ccce282 Pass scrolltop in custom Lua function 2 months ago
Arijit Basu 90df0a2b5a vimlike_scrolling -> paginated_scrolling
Inspired by @ElSamhaa 's PR https://github.com/sayanarijit/xplr/pull/704
2 months ago
Arijit Basu ce52bcdf94 Revert vimlike scrolling
Use stateful ui widget.
2 months ago
Arijit Basu 6fb0781fe4 xplr.util.lscolor shouldn't return nil
Closes: https://github.com/sayanarijit/xplr/issues/705

Also update xplr version.
2 months ago
Arijit Basu c1bb251fef
Adds Vim-Like Scrolling to XPLR (#704)
- Added through a setting `vimlike_scrolling` which is turned off by
default
- A hard-coded _(for now)_ cushion of `5` lines that allows for
previewing the next lines while scrolling
- A separate struct `ScrollState` with getters and setters for the
`current_focus` field to disallow setting the field without updating the
`last_focus` field
3 months ago
Arijit Basu 976530ba70
Gen docs 3 months ago
Arijit Basu 96da7e1da8
Fix linting 3 months ago
Arijit Basu 96ffe8680b
Fix ScrollUpHalf 3 months ago
Ahmed ElSamhaa 1600ad9a9c Makes the preview cushion dynamic now, and sets an initial value 5 for it 3 months ago
Ahmed ElSamhaa 2a3d056bf1 Clarifies some comments 3 months ago
Ahmed ElSamhaa 91276f6871 Removes an unnecessary condition 3 months ago
Ahmed ElSamhaa 00bd54abe9 Removes unnecessary mut from the calc_skipped_rows fn 3 months ago
Ahmed ElSamhaa 95621af9eb Increases the preview_cushion to 5 like in vim 3 months ago
Ahmed ElSamhaa 5240b3904b Rolls back changes to the open terminal file 3 months ago
Ahmed ElSamhaa a6fb695ff9 Refactors the calc_skipped_rows function to make it even more readable 3 months ago
Ahmed ElSamhaa fd40de26e7 Adds tests for the ScrollState calc_skipped_rows fn 3 months ago
Ahmed ElSamhaa 87805509c5 Refactors the calc_skipped_rows function to make it more readable 3 months ago
Ahmed ElSamhaa 4aa367ca7c Makes the current_focus field private to limit usage to its setters and getters 3 months ago
Ahmed ElSamhaa 01606e0e60 Adds corresponding config setting for vimlike_scrolling 3 months ago
Ahmed ElSamhaa e834242f5d Adds vim-like scrolling 3 months ago
alice 7c6dffc2c6
cargo: allow building with system lua (#703)
useful for distros
3 months ago
har7an d5217f6677
cargo: Revert version update on `ansi-to-tui` (#702)
which causes custom styling to be lost on the currently selected line.
3 months ago
Arijit Basu 0285f0824c
Disable snap build 3 months ago
Arijit Basu a6b19425ae
Release v0.21.6 (#701)
- Snap build
- xplr.util.debug()
- `c` and `m` key bindings for quick copy and move.
- ScrollUpHalf fix
- Dependency updates
3 months ago
Arijit Basu 9db8b2cc19
Upgrade dependencies (#700) 3 months ago
mikoloism 68500f3a8e
[Feat] support `snapcraft` build package to releases (#697)
* build(snap): support snapcraft package

- add `snap/snapcraft.yaml` file

NOTE: under `devmode` until fit to release

* ci(gh-action): support `snapcraft` build and publish to `gh-release` page
4 months ago
Arijit Basu ded2e108bf
Add xplr.util.debug
Also update version
5 months ago
Arijit Basu 6e8f3da971
Quick copy and quick move (#692)
* Quick copy and quick move

- Press `c` to quickly copy the focused or selected path
- Press `m` to quickly move the focused or selected path
5 months ago
Arijit Basu d76a70fed4
Fix ScrollUpHalf 5 months ago
Arijit Basu 16673963aa
Minor fix 5 months ago
Arijit Basu b0ef9a5190
Remove unnecessary config example for now 5 months ago
Arijit Basu b70337708c
Minor fix 5 months ago
Arijit Basu 9127d15494
Use tree-view as example 5 months ago
Arijit Basu 66d9f7e586
Minor doc fix 5 months ago
Arijit Basu eab47a9044
Fix nixpkgs link 5 months ago
Arijit Basu a9e3752f56
Minor doc fix 5 months ago
Arijit Basu 470bea1265
NixOS install instructions 5 months ago
Arijit Basu cc578aaf0a
Add initial pwd to history 6 months ago
Arijit Basu 50e81853fe
Update version 6 months ago
Arijit Basu 414b45e4fd
Sync branch (#687)
* Update awesome-plugins.md

* Update awesome-plugins.md

* Visit deep level branches (#686)

* Visit deep level branches

- Press `)` to pass `NextVisitedDeepBranch`
- Press `(` to pass `LastVisitedDeepBranch`

* Last -> Previous

* Upgrade pkgs

* Clippy fixes

* Fix clippy err

---------

Co-authored-by: Dmitry Savosh <d.savosh@gmail.com>
6 months ago
Arijit Basu 75dabeb283
Add support for function keys upto F24 6 months ago
Arijit Basu 1629398adf
Sync branch (#677)
- Selection indicator in input and logs pane title for people who hide
the selection pane.
7 months ago
Arijit Basu dd8bb74dd4
Update Arch Linux package URL in install.md (#676)
The old URL returns 404 now.
7 months ago
Felix Yan 1dc5eae8fc
Update Arch Linux package URL in install.md
The old URL returns 404 now.
7 months ago
Arijit Basu 484b94a961
Add selection indicator in input and logs panel 8 months ago
Arijit Basu 50d9d1c54b
New plugin (#666) 9 months ago
Dugan Chen c7c3d2d7f6 Link to the one-table-column theme 9 months ago
Arijit Basu 1441275860
Avoid duplicate strip call (#664)
Fixes: https://github.com/sayanarijit/xplr/issues/662
11 months ago
Arijit Basu 8af1647c09
v0.21.3 (#661) 11 months ago
Arijit Basu 22b5fca8d9 Update version 11 months ago
Arijit Basu 4a3f18100d Display current mode help menu on top
Also, add global key binding f1.
Also, update deps.

Closes: https://github.com/sayanarijit/xplr/issues/655
11 months ago
Lewis Cook 6df168f8c1 init: Fix error upon deleting file on non-GNU systems 11 months ago
Arijit Basu eeb7b5d684
Update README.md 11 months ago
Arijit Basu 9a7ff5846d
Fix android build (#657)
* Fix android build

Ref: https://github.com/khvzak/mlua/issues/267#issuecomment-1644559018

* Update docs

* Fix typos by cheating a bit

* Instruction first
11 months ago
Arijit Basu 1b2226512f
Imrove builds (#650)
- Add more build targets
- Allow cross compile (if you have the resources, I don't)
- Fix failing nixos tests
11 months ago
Arijit Basu 56472998f5
Don't give up yet 11 months ago
Arijit Basu bf7ae3f748
Give up on the new platforms 11 months ago
Arijit Basu 94ba22bbcc
Upgrade 11 months ago
Arijit Basu 567a6201a8
Silently fail to 'enter' regular files (#654)
Silently fail to "Enter" regular files. Entering only makes sense for
directories.

This fixes
https://github.com/sayanarijit/xplr/issues/653#issue-1806818324

I don't know Rust at all, so I make no claims to the code quality. But I
have tested this change and it does work.
11 months ago
Arijit Basu 54d6d19003
Also enter symlink dir 11 months ago
Dugan Chen 4aeb3dd7c8 Use built-in node method 12 months ago
Dugan Chen 5626422ba4 Silently fail to 'enter' regular files 12 months ago
Arijit Basu 1941355128
Imrove builds
- Add more build targets
- Allow cross compile (if you have the resources, I don't)
- Fix failing nixos tests
12 months ago
Arijit Basu 2f78691333
Update/upgrade deps 12 months ago
Arijit Basu a2fbf759dd
Strip 12 months ago
Arijit Basu bc7f3cbbcf
Minor update 12 months ago
Arijit Basu ad50342260
Fix focus on back 12 months ago
Arijit Basu 313c61db96
Optimize get_current_dir
Closes: https://github.com/sayanarijit/xplr/issues/628
12 months ago
Arijit Basu 255517c2a9
Also respect general.table.headers.cols[*].style 12 months ago
Arijit Basu 9844ae1476
Respect low priority styles
- xplr.config.general.selection.item.style
- xplr.config.general.table.row.style
- xplr.config.general.table.row.cols[*].style
- xplr.config.general.table.header.cols[*].style

Ref: https://github.com/sayanarijit/xplr/issues/640
12 months ago
Arijit Basu d282032b3d
Fix symlink base again 12 months ago
Arijit Basu ba26752f6c
Use correct base for symlink for alternate layouts 12 months ago
Arijit Basu 0cc8723e8e
Document on_selection_change 12 months ago
Arijit Basu 2f3c2ea0e4
Fix lint 12 months ago
Arijit Basu 219ee68152
Update sum-type.md 12 months ago
Arijit Basu 859d888bde
Improve sum type docs 12 months ago
Arijit Basu f84d9d5c6a
More fixes 12 months ago
Arijit Basu 3fcfb1dbef
Fix sum type doc 12 months ago
Arijit Basu 4c51f0affe
Document sum types for hackers (#647)
* Document sum types for hackers

So you don't have to learn rust to configure xplr.

* Fix typos
12 months ago
Arijit Basu 9d1bd99fd4
Implement on_selection_change
Also optimize navigation with selection items.
Refresh selection only when it's required.

Closes: https://github.com/sayanarijit/xplr/issues/635
12 months ago
Arijit Basu 8209988ba6
Add tree-view.xplr 12 months ago
Arijit Basu 33c5aa9f14
Bring back enqueue 12 months ago
Arijit Basu cae50e4bcf
Remove unnecessary enqueue step 12 months ago
har7an 048b1c701a
docs: Add entry to awesome-plugins (#634)
mentioning https://gitlab.com/hartan/web-devicons.xplr
1 year ago
Arijit Basu 508f4b980b
Fix doc 1 year ago
Karim Lalani 28c9e0e3a0
chore: added codespell to github ci (#632)
chore: added codespell from 8cca2d3566 to github ci

chore: move spellcheck to out of testsuite

fix: add missing checkout step to spellcheck

exclude target

move works to ignore to .codespellignore

fix: typo

add lua specific
1 year ago
Noah Mayr 4ccd9796c4
Use xdg-rust crate instead of dirs crate (#631)
* Use xdg-rust crate instead of dirs crate

* Fix clippy warning
1 year ago
Solitude 36a7f1dc17
Honor XDG_CONFIG_HOME (#629) 1 year ago
Arijit Basu 2cc8e0c510
Cleanup unused features 1 year ago
Arijit Basu 27bc1217b3
Document jf syntax 1 year ago
Arijit Basu ab90381fda
Prompt when in doubt (#623)
* Update deps

* Prompt for user input when in doubt

- Ask before delete.
- For copy, move or symlink operations, ask what to do if a file with
  the same name exists.
- Update version.

Closes: https://github.com/sayanarijit/xplr/issues/615
1 year ago
Arijit Basu 2a775371f6
Use sayanarijit/jf for xplr -m 1 year ago
Arijit Basu 3bee8060c7
Lower LS_COLORS priority (#622)
* Lower LS_COLORS priority

Fixes: https://github.com/sayanarijit/xplr/issues/620

* Remove default style

* Fix doc markdown
1 year ago
Arijit Basu 97e30e2a6f
Allow nesting layouts inside a custom layout (#618)
This adds `CustomLayout` panel for nesting a `Layout` inside the `Static` and
`Dynamic` layouts.

This will help switching between different layouts dynamically, without
having to switch modes.
1 year ago
Arijit Basu 7c26c48e18
Trim binary size by reducing skim dependencies 1 year ago
Arijit Basu 17269ab17f
Update awesome-hacks.md 1 year ago
Arijit Basu 8aff0ba918
Clarify how to deal with init.lua 1 year ago
Arijit Basu 4228a71ed9
Mostly documentation fixes (#611)
Also, CLI help improvement.
1 year ago
Arijit Basu 252a1f5c37
Documentation fixes and cli help improvement 1 year ago
Henrique Goulart 4f0db1f3e3
docs: add missing comma to node_types lua conf
Add missing comma to node_types lua conf example in the docs.
1 year ago
Kian-Meng Ang 8cca2d3566 Fix typos
Found via `codespell -S target -L ratatui,crate,ser,enque,noice`
1 year ago
Arijit Basu b995be0089 Fix SelectAll and UI colors (#604)
- SelectAll will not unselect the existing paths. Same for
  ToggleSelectAll.
- Fixed UI bug causing random bold characters (deps).
1 year ago
Arijit Basu c79175764b SelectAll: extend selection list rather than replacing (#603)
Also applies to ToggleSelectAll i.e. `ctrl-a`
1 year ago
Arijit Basu e0d683b13a
Release 0.21.0 (#602)
* Add xplr.util.lscolor and xplr.util.paint (#569)

* Add xplr.util.lscolor and xplr.util.style

* Fix formatting

* Fix clippy suggestions

* Remove redundant closures

* Optimize, support NO_COLOR, and rename style to paint

* Use xplr.util.paint and xplr.util.color in init.lua

Co-authored-by: Noah Mayr <dev@noahmayr.com>

* Add utility function xplr.util.textwrap (#567)

* Add utility function xplr.util.wrap

* Cleanup and fix formatting

* Update src/lua/util.rs

Co-authored-by: Arijit Basu <sayanarijit@users.noreply.github.com>

* Update wrap to return lines instead

* Fix doc

* Rename wrap -> text wrap

Co-authored-by: Arijit Basu <sayanarijit@users.noreply.github.com>
Co-authored-by: Arijit Basu <sayanarijit@gmail.com>

* Add xplr.util.relative_to and xplr.util.path_shorthand (#568)

* Add xplr.util.relative_to and xplr.util.path_shorthand

* Remove duplicate slash at end

* Use pwd from env and remove pathdiff package

* Some fixes and improvements

* Generate docs

* Some more improvements

* Improve selection rendering

* Improve functions with test cases

* Update docs

* Minor doc fix

* Rename path_shorthand -> shortened

* Handle homedir edgecase

Also fix init.lua

* Minor fix

* Use config argument for relative and shortened paths

* Prefix relative paths with "." and fix edge cases where we're not showing the file name

* Use and_then instead of map and flatten

* WIP: Move selection rendering to lua

* Make selection renderer function configurable on lua side

* Some improvements

* Some impovements

* Minor doc fix

* Remove symlink style

---------

Co-authored-by: Arijit Basu <sayanarijit@gmail.com>

* Add xplr.util.layout_replaced (#574)

Closes: https://github.com/sayanarijit/xplr/issues/573

* Improve selection operations (#575)

- `:sl` to list selection.
- `:ss` to softlink.
- `:sh` to hardlink.
- Avoid conflict by adding suffix.
- Unselect individual path only on operation success.

Closes:

- https://github.com/sayanarijit/xplr/issues/572
- https://github.com/sayanarijit/xplr/issues/571
- https://github.com/sayanarijit/xplr/issues/570

* Minor updates

* Add more features (#581)

* Add more features

- Key binding ":se" to edit selection list in $EDITOR
- New utility functions:
  - xplr.util.clone
  - xplr.util.exists
  - xplr.util.is_dir
  - xplr.util.is_file
  - xplr.util.is_symlink
  - xplr.util.is_absolute
  - xplr.util.path_split
  - xplr.util.node

Closes: https://github.com/sayanarijit/xplr/issues/580
Closes: https://github.com/sayanarijit/xplr/issues/579
Closes: https://github.com/sayanarijit/xplr/issues/577

* Fix edit selection list

* Fix clippy lints

* Fix layout link in doc

* xplr.util.shortened -> xplr.util.shorten

* Fix more clippy lints

* Fix xplr.util.shorten name change

* More UI utilities and improvements (#582)

* More UI utilities and improvements

- Apply style only to the file column in the table.
- Properly quote paths.
- Expose the applicable style from config in the table renderer argument.
- Add utility functions:
  - xplr.util.node_type
  - xplr.util.style_mix
  - xplr.util.shell_escape

* Make escaping play nice with shorten

* Fix tests

* Fix doc

* Some fixes

* Fix selection editor

* Fix clear selection for selection editor

* Add selection navigation (#583)

* Add selection navigation

- FocusNextSelection      (ctrl-n)
- FocusPreviousSelection  (ctrl-p)

Also improve batch operations

* Minor doc fixes

* Minor doc fix

* Remove tab -> ctrl-i binding

* Improve batch operation interaction

- More robust focus operation.
- Focus on failed to delete paths.

* Fix Rust compatibility

* Fix panic on permission denial

Also, improve the error messages.

* More logging improvements

* Fix layout_replace only working with table parameters (#586)

* Improve builtin search mode (#585)

* Improve builtin search mode

* Remove commented out code

* Make search ranking and algorithm more extensible

* Flatten messages

BREAKING: xplr.config.general.sort_and_filter_ui.search_identifier -> xplr.config.general.sort_and_filter_ui.search_identifiers

Messages:

- Search
- SearchFromInput
- SearchFuzzy
- SearchFuzzyUnranked
- SearchFuzzyUnrankedFromInput
- SearchRegexUnrankedFromInput
- SearchRegex
- SearchRegexUnranked
- SearchRegexUnrankedFromInput
- SearchRegexUnrankedFromInput
- CycleSearchAlgorithm
- EnableRankedSearch
- DisableRankedSearch
- ToggleRankedSearch

Static config:

xplr.config.general.search.algorithm = "Fuzzy"

* Handle search ranking in search algorithm

* Make CycleSearchAlgorithm only cycle between algorithms, without changing ranking

* Separate algorithm and ordering

* Minor doc updates

* Some cleanup

* Final touch

* Cycle -> Toggle

---------

Co-authored-by: Arijit Basu <sayanarijit@gmail.com>

* Fix layout replace for unit layouts (#588)

* Allow custom title and ui config in custom layout. (#589)

* Allow custom title and ui config in custom layout.

Adds the following layouts:

- Static
- Dynamic

Deprecates `CustomContent` (but won't be removed to maintain compatibility).

Closes: https://github.com/sayanarijit/xplr/issues/563

* Delete init.lua

* Update docs/en/src/layout.md

* Update docs/en/src/layout.md

* Rename

- Paragraph => CustomParagraph
- List => CustomList
- Table => CustomTable

Also update init.lua

* Fix clippy errs

* Fix doc links

* Fix search order

* Improve working with file permissions (#591)

* Improve working with file permissions

Implements:

- xplr.util.permissions_rwx
- xplr.util.permissions_octal

* Edit permissions

* Add permissions in Resolved Node (#592)

* Add permissions in Relolved Node

And handle application/x-executable mime type.

* Fix bench

* Improve permissions editor

* More permissions editor improvements

* Doc updates

* Remove ResolvedNode.permissions (#593)

Reason: Too much serialization making lua calls slow.

* Add workaround for macos with legacy coreutils (#595)

Refs:
- https://github.com/sayanarijit/xplr/issues/594
- https://github.com/sayanarijit/xplr/issues/559

* Use H:M:S format to display logs (#596)

* Keep the selection list and clear manually (#597)

* Keep the selection list and clear manually

Ref: https://github.com/sayanarijit/map.xplr/issues/4

* Fix linting err

* Fix broken history (#599)

* Fix broken hostory

Fixes: https://github.com/sayanarijit/xplr/issues/598

* Minor cleanup

* Slightly optimize selection retention (#600)

* Update deps

* chrono -> time

* update: 0.20.2 -> 0.21.1

* Update post-install.md

* Upgrade guide

* Minor fix

* Fix tests

* Add missing doc

* Fix clippy lints

---------

Co-authored-by: Noah Mayr <dev@noahmayr.com>
1 year ago
Arijit Basu 59279b816d
Update post-install.md 1 year ago
Arijit Basu 8c4f744bb1 Try fix gh action 1 year ago
Arijit Basu d80b1b4db8 Alternate theme 1 year ago
Arijit Basu 5f07e6143f Update v0.20.2 1 year ago
Arijit Basu 43c88b4873 Improve install instruction for macOS 1 year ago
Arijit Basu d52ccac8ba Fix file creation for mac's default bash 1 year ago
Arijit Basu 26d79bd799
Update awesome-plugins.md 1 year ago
Arijit Basu 553b4ed3d6
Fix keeping input buffer 1 year ago
Arijit Basu e9fc643bd9
Fix test 1 year ago
Arijit Basu 3afccf2a54
Doc fix 1 year ago
Arijit Basu 1d9d5f5145
Minor changes 1 year ago
Emanuel 0715e242ef fix: run cargo fmt 1 year ago
Emanuel 006c655e3a fix: restore a wrongly removed part 1 year ago
Emanuel 105e770f58 feat: complete last changes to add hooks 1 year ago
emanuel d6e33e68e3 :feat: Add hooks for mode and layout changes 1 year ago
BoolPurist 078da205ca Fixed own typo. 1 year ago
BoolPurist caa365b4a0 link to header for crates.io install is fixed 1 year ago
emanuel 4c4e7f41b4 Add style.xplr plugin to awesome-plugins.md 2 years ago
Arijit Basu e6e701b371
Update alternatives.md 2 years ago
Arijit Basu 0cd5a9163d
Minor fixes 2 years ago
Arijit Basu ac958c9532
Update awesome-plugins.md 2 years ago
Arijit Basu 7fbcd18bb4
Update util.rs 2 years ago
Arijit Basu 1369fcea9a
Update xplr.util.md 2 years ago
Arijit Basu d17489de8d Update version 2 years ago
Arijit Basu 57492b84c0
Show HelpMenu in intermediate modes
Also, use xplr.util api in xplr.fn.builtin.try_complete_path
2 years ago
Arijit Basu d719700122 Launch $SHELL, fallback to bash
Closes: #499
2 years ago
Arijit Basu 011d3d4a68
Fix logs count 2 years ago
Arijit Basu efb4d605c5 Fix doc 2 years ago
Arijit Basu e559b96e31 Implement xplr.util.version
Closes: https://github.com/sayanarijit/xplr/issues/540
2 years ago
Arijit Basu d4edf3302f Gen docs 2 years ago
Arijit Basu c382768f23 Implement xplr.util.to/from_json/yaml
Closes: https://github.com/sayanarijit/xplr/issues/541
2 years ago
Arijit Basu 6558ba8092 Hide counts if 0
Closes https://github.com/sayanarijit/xplr/issues/542
2 years ago
Arijit Basu 91c87a3901 Fix creating file with spaces
Fixes: https://github.com/sayanarijit/xplr/issues/544
2 years ago
Arijit Basu 25798009ad Fix displaying multispace characters
Ref: https://github.com/sayanarijit/tui-input/pull/14
2 years ago
Arijit Basu 104dc0534b
Minor doc fixes 2 years ago
Arijit Basu 919a492131
Packaging instructions in readme 2 years ago
Arijit Basu 399fd183d4
Update docs 2 years ago
Arijit Basu 053615b041 Fix linting errors 2 years ago
Arijit Basu 76224c42e1 Fix Nix cache push 2 years ago
Arijit Basu ca4f4f3f45
Fix nix build second attempt 2 years ago
Arijit Basu a0c19025dd
Fix nix build 2 years ago
Arijit Basu 69db7fa9e6
Push Binary Cache for Nix 2 years ago
Arijit Basu 555bc02e10
Update lua-function-calls.md 2 years ago
Arijit Basu bbb1026ff8
Add nix flake 2 years ago
Arijit Basu aea17b415b
Update writing-plugins.md 2 years ago
Arijit Basu 7e70487511
Use default values for some fields 2 years ago
Arijit Basu 15e7123aba
Update awesome-hacks.md 2 years ago
Arijit Basu 133ba120af Update .github/workflows/cd.yml 2 years ago
Orhun Parmaksız 77164ff98a fix(cd): use external gzip for compressing the git source 2 years ago
Arijit Basu ea8a1fcd46
Update xplr.util.md 2 years ago
Arijit Basu 70cb745c9f Implement and expose xplr.util
Closes: https://github.com/sayanarijit/xplr/issues/517
2 years ago
Arijit Basu 15979e4974 Release v0.20.0 2 years ago
Arijit Basu 49c4729047
Improve CLI errors 2 years ago
Arijit Basu 01060ed025
Add $XPLR_INITIAL_PWD and 'gi' key binding
This will allow enable workspace like features, without using vroot.
2 years ago
Arijit Basu 74318435e8
Update version 2 years ago
Arijit Basu 553f6b9bc6
Fix doc 2 years ago
Arijit Basu 71bd2e2776
Add more vroot options
- ToggleVroot
- UnsetVroot

Along with key bindings.
2 years ago
Arijit Basu 94154c56df
Consistent help text 2 years ago
Arijit Basu 5c4dec05ed
Don't restrict saving location outside vroot 2 years ago
Arijit Basu 289556f452
Add builtin vroot mode
Also fix ResetVroot
2 years ago
Arijit Basu eba9de5a44
Improve vroot indicator 2 years ago
Arijit Basu e3a5f3c044 Add messages SetVroot and ResetVroot 2 years ago
Arijit Basu a62b72bf2a Add support for --vroot
--vroot helps isolating navigation of an xplr session inside a specific
directory. However, interaction still requires passing full paths
(`/tmp/vroot`). Shell scripts and Lua functions can still access files
outside the virtual root.

This PR also fixes unwanted dot (.) and extra slash (//) issues in paths.
2 years ago
Arijit Basu 00ffd077aa
Upgarde version 2 years ago
Arijit Basu bafe15e25e
Add more hooks 2 years ago
Arijit Basu fc798aad97 Update version 2 years ago
Arijit Basu deb28fa14a Add initial support for hooks
A new optional section of the configuration defined using the lua return
statement, which can be used to define append only things, such as hooks
and callbacks, specific to each config file.

Example

```lua
version = "0.0.0"

return {
  -- Adds messages to pass when xplr loads (similar to `--on-load`)
  on_load = {
    { LogInfo = "Hello xplr user," },
    { LogSuccess = "Configuration successfully loaded!" },
  }
}
```
2 years ago
Arijit Basu 723dd6ec2c Even better error messages 2 years ago
Arijit Basu 4a34780eb4
Improve errors messages `-m` and `-M` 2 years ago
Arijit Basu b05e702536
Actually validate the passed message 2 years ago
Arijit Basu baa8759d68
Minor performance improvement 2 years ago
Arijit Basu 52c8e05a1e
Fix clippy error 2 years ago
Arijit Basu 65eee2dc90
Use serde_yaml::with::singleton_map_recursive 2 years ago
Arijit Basu 77852b435f
Error on missing *-msg-in arguments 2 years ago
Arijit Basu 060544a2b8
Minor fixes 2 years ago
Arijit Basu e51818dfc1
Fix cargo release 2 years ago
Arijit Basu f8b16a7ddd Rebase main 2 years ago
Arijit Basu 85c4253782 Upgrade version 2 years ago
Arijit Basu 1d829c36e6 Implement -M, --print-msg-in
To help with creating safe arguments for --on-load.

e.g.

```
xplr --on-load $(xplr -M 'ChangeDirectory: %q' "${HOME:?}")
```
2 years ago
Arijit Basu 7c730557f2 Shell escape SRC and TARGET for logging 2 years ago
Arijit Basu 93e9b2b2ae There's no end of arguments for -m 2 years ago
Arijit Basu 3fb174cdc0 Allow using -m outside of xplr shell for debugging
Also validate the message before passing.
2 years ago
Arijit Basu 111a648818 Shell escape file paths in logs 2 years ago
Arijit Basu 1e820030a0 Implement subcommand -m / --pipe-msg-in 2 years ago
Arijit Basu bffe1d43ec Escape newline in selection list 2 years ago
Arijit Basu fbe6b2be10 Improve parsing CLI arguments 2 years ago
Arijit Basu 88fe71779b Add best practices section 2 years ago
Arijit Basu 91e3990df1 Rename messages 2 years ago
Arijit Basu 7b8f38df5b Update is_readonly 2 years ago
Jeremy Cantrell 2b5755aa8a An attempt at safer message passing. 2 years ago
Arijit Basu 895d55ca23
Minor improvement 2 years ago
Arijit Basu 1d20039fae
Improve log rendering 2 years ago
Arijit Basu c93202b649
Minor improvements 2 years ago
Arijit Basu 89b3731b56
Fix serialization error 2 years ago
Arijit Basu ae714e9713
Update xplr.desktop 2 years ago
Arijit Basu a4f98f0b63
Add tri-pane.xplr 2 years ago
Arijit Basu bd478ff64b Update serde_yaml to 0.9 2 years ago
Arijit Basu 57483bef41 Use fuzzy search instead of regex search
Ref: https://github.com/sayanarijit/xplr/issues/496
2 years ago
Arijit Basu 93bd53bbcb Add quick scrolling
Not ideal, but better than nothing.

Closes: https://github.com/sayanarijit/xplr/issues/509
2 years ago
Arijit Basu 81e83365f2
Document how to try out the hacks. 2 years ago
Arijit Basu 58c572d77a Fix directory explore scheduling
Fixes https://github.com/sayanarijit/xplr/issues/503
2 years ago
Arijit Basu f2713d9549
Fix support for readline keys 2 years ago
Arijit Basu 12264722ef
Add missing doc 2 years ago
Arijit Basu d579832c69 Update version 2 years ago
Arijit Basu 4e9c056a10 Custom input prompts for different modes without SetInputPrompt
Closes: https://github.com/sayanarijit/xplr/issues/502
2 years ago
Arijit Basu b4ba56aa72 Fix regex deserialization
Fixes https://github.com/sayanarijit/xplr/issues/503
2 years ago
Arijit Basu 096e520a2b Fix clippy err 2 years ago
Arijit Basu 5dcaca3816 Document new input operation 2 years ago
Arijit Basu f10fad958d Upgrade deps 2 years ago
Arijit Basu 9f9a6f9d73 Parse file UID and GID
Closes https://github.com/sayanarijit/xplr/pull/494
2 years ago
Arijit Basu d8ff70bcaa
Fix typo 2 years ago
Arijit Basu 12d2020f01
Improve preview hacks 2 years ago
Matt_BSD d6d7e70425 Added FreeBSD to a list of OSes
Added FreeBSD 13.1-RELEASE to a list of OSes on which the image preview hack and text preview hack were tested on.
2 years ago
Arijit Basu 846e0c7f60
Update awesome-plugins.md 2 years ago
Arijit Basu c42c4dc69d
Fix doc 2 years ago
Arijit Basu 673bcd21c0
Update awesome-hacks.md 2 years ago
Arijit Basu a9b1527e55
Point type-to-nav to th eupdated plugin for now 2 years ago
Junker 7f1f33b422 add 'nuke.xplr' plugin to awesome-plugins 2 years ago
Arijit Basu 220d253be4
Update index.html 2 years ago
Arijit Basu 95f1f45375
Reduce the selection pane height 2 years ago
Arijit Basu ed8fb9dc6c
Upgrade deps 2 years ago
Arijit Basu bf354352d5 Upgrade version 2 years ago
Arijit Basu bea0f277bc Add global key bindings that can be overwritten.
- Use `xplr.config.general.global_key_bindings` to define a set of
  key bindings that are available by default in every mode. e.g `esc`
  and `ctrl-c`.
- Remove boilerplate config from `init.lua`.
- Update docs.
2 years ago
Arijit Basu 07d0fe2dee
Update configure-key-bindings.md 2 years ago
Arijit Basu 4c4d3843e9
Avoid using unnecessary prompts
Problem with long paths.
2 years ago
Arijit Basu a2f246f61e
Improve the "duplicate as" prompt 2 years ago
Arijit Basu 27394dd177
Tab completion for "duplicate_as" mode 2 years ago
Arijit Basu 88416d862e
Minor improvement 2 years ago
Arijit Basu 563e2d1475 Update docs 2 years ago
Arijit Basu 5370cc2e8c Go to path and native auto completion on tab
- BREAKING: Rename mode `create directory` and `create file` to
  `create_directory` and `create_file`.
- Add key binding `gp` to go to a given path from input.
- Add function `fn.builtin.try_complete_path` to auto complete the path
  in input buffer.
- Use `tab` to auto complete path in `rename`, `create_file`,
  `create_directory` and `go_to_path` modes.
- Show different prompts in different modes.

And some cleanup.
2 years ago
Arijit Basu e5576e2990 Fix typo 2 years ago
Arijit Basu e04b0964ae Use 89 chars line length 2 years ago
Arijit Basu 21b8c3e807 Make clippy happy 2 years ago
Arijit Basu 744b68197d Some optimizations 2 years ago
Arijit Basu 02b631bf83 Strict NO_COLOR compliance and some cleanups 2 years ago
Arijit Basu 61d3b1635e Use NO_COLOR to disable OSC 7 2 years ago
Arijit Basu d35a4c58cb Write OSC 7 escaped codes on a the tui backend 2 years ago
Arijit Basu 47f7d51e46 Cleanups 2 years ago
Arijit Basu de856b7870 Implement Write 2 years ago
Arijit Basu 2f9992bf8b OSC 7 fixes
Co-authored-by: Wez Furlong <wez@wezfurlong.org>
2 years ago
Arijit Basu 3c8f6e1b08 Experiment with OSC 7
Ref: https://github.com/wez/wezterm/issues/2054
2 years ago
Arijit Basu 07b2374616
Make the table a little compact 2 years ago
Arijit Basu 7163c8932c
Simplify Cargo.toml 2 years ago
Arijit Basu c3b25bb82f
Update awesome-integrations.md 2 years ago
Arijit Basu e655c245c5 Optimize regex search further
No need to compile both regex and iregex.
2 years ago
Arijit Basu f324e976da Compile regex only when required 2 years ago
Arijit Basu 95248ae6d1 Cleanup 2 years ago
Arijit Basu e2cf313ee2 Optimize regex search
Compile regex once and reuse it.
2 years ago
Arijit Basu a28addbd0e
Add wl-clipboard.xplr 2 years ago
Arijit Basu 33e537949f
Install instructions for Alpine and Gentoo 2 years ago
Arijit Basu 7c3721a46c
Add PayPal link 2 years ago
Arijit Basu e3184c5f88
Update awesome-hacks.md 2 years ago
Arijit Basu 3eabbd4ed6
Update awesome-hacks.md 2 years ago
Arijit Basu fba1c2d0d6
ethicalads back to bottom 2 years ago
Arijit Basu 1df0d2cc90
Clarify sponsoring 2 years ago
Arijit Basu e22ed3f12e
Update FUNDING.yml 2 years ago
Arijit Basu 87783872a5
Remove xplr.nvim for the time being
Discussed in chat room.
2 years ago
Arijit Basu 2aee3743e1
Update awesome-plugins.md 2 years ago
Arijit Basu 3733dc3262
Add offline-docs.xplr 2 years ago
Arijit Basu 278642cc41
Remove snap
Ref: https://forum.snapcraft.io/t/request-deletion-of-snap-xplr/30145
2 years ago
Arijit Basu a25659d76c
Try ads above toc 2 years ago
Arijit Basu cb2851875c
Fix mime_essence doc - again 2 years ago
Arijit Basu dc8cb30eb3
Fix mime_essence docs 2 years ago
Arijit Basu cf052d2691
Fix doc 2 years ago
Arijit Basu 6f626d1ba0
Clarify metadata API 2 years ago
Arijit Basu de45123e26
Update writing-plugins.md 2 years ago
Tom van Dijk 4fa90d715e fixup! Edit footnote 2 years ago
Tom van Dijk 2194ddcc61 Edit footnote
Co-authored-by: Arijit Basu <sayanarijit@users.noreply.github.com>
2 years ago
Tom van Dijk c40ffe1633 Add dtomvan/extra-icons.xplr to awesome-plugins.md 2 years ago
Arijit Basu 90e56deedc
Doc updates 2 years ago
Arijit Basu 2c39f9499a
Umprove the upgrade guide 2 years ago
Arijit Basu 3da8140bb1
Fix printing unicode 2 years ago
Arijit Basu 071195fd61
Fix link 2 years ago
Arijit Basu e006152066 Upgrade version 2 years ago
Arijit Basu 2179be473f Fix docs 2 years ago
Arijit Basu cc5b996db9 Add remove last filter 2 years ago
Arijit Basu e634d7150f Add more docs 2 years ago
Arijit Basu fadfb3d70c Add docs 2 years ago
Arijit Basu d9465f1a57 Display in the table 2 years ago
Arijit Basu 8e1d16b751 Remove merge conflicts 2 years ago
Arijit Basu 37e660e0a6 Improve timestamp support 2 years ago
Shunsuke Mie 626a48cf88 Update init.lua to show a last modification column 2 years ago
Shunsuke Mie 57c29d47c5 Support a column shows time of modification 2 years ago
Arijit Basu 09002d0e65 Add support for null characters as separator
- Use `--read0 -` to read `\0` separated paths from stdin.
- Use `--write0` to write `\0` separated paths to stdout.
- Use `-0` or `--null` to combine `--read0` and `--write0`.
2 years ago
Arijit Basu 1fef30ce56 Update key bindings 2 years ago
Arijit Basu 86f56cc051 Fix search navigation 2 years ago
Arijit Basu 5f598648a8 Make the prompt not null 2 years ago
Arijit Basu d2390285fb Fix doc 2 years ago
Arijit Basu 21c8ea68bb Document regex filters 2 years ago
Arijit Basu 1de737cefa Add regex support and dynamic input prompt
- Add new regex filters
  - `RelativePathDoesMatchRegex`
  - `RelativePathDoesNotMatchRegex`
  - `IRelativePathDoesMatchRegex`
  - `IRelativePathDoesNotMatchRegex`
  - `AbsolutePathDoesMatchRegex`
  - `AbsolutePathDoesNotMatchRegex`
  - `IAbsolutePathDoesMatchRegex`
  - `IAbsolutePathDoesNotMatchRegex`
- Search mode now defaults to regex
- Added new message `SetInputPrompt` to set the input prompt
  dynamically.
2 years ago
Arijit Basu 5765698fb7 Minor doc fix 2 years ago
Arijit Basu a233bc9bfb
Update awesome-plugins.md 2 years ago
Arijit Basu e045b3ac3a
Update awesome-hacks.md 2 years ago
Arijit Basu 3cfed5aa6f
Improve docs 2 years ago
Arijit Basu 0d12762c1d
Upgrade tui-input to 0.4.0 2 years ago
Arijit Basu a66d5e05d5
Upgrade to tui-input 0.3 2 years ago
Arijit Basu 8f7d6c4c9e
Fix panic when inserting unicode characters
Fixed in tui-input 0.2.1
2 years ago
Arijit Basu a89b58d686
Fix deleting unicode characters
Fixed in tui-input 0.2.0.
2 years ago
Arijit Basu 188a2a8751
Update environment-variables-and-pipes.md 2 years ago
Matt_BSD ed392a0755 Update docs/en/src/environment-variables-and-pipes.md
Co-authored-by: Arijit Basu <sayanarijit@users.noreply.github.com>
2 years ago
Matt_BSD 9a7e8cc0f4 Update docs/en/src/environment-variables-and-pipes.md
Co-authored-by: Arijit Basu <sayanarijit@users.noreply.github.com>
2 years ago
Matt_BSD fb54b29dec Update environment-variables-and-pipes.md
added section for for environment variables and did a short description for each of those.
I don't know what $XPLR_INPUT_BUFFER does  thou, so I left it as TODO.
I also added a small example using $XPLR_FOCUS_PATH with xdg-open.
2 years ago
Arijit Basu 7f8e293bd0
Update install.md 2 years ago
Arijit Basu 3301e03880
Fix fb meta tag 2 years ago
Arijit Basu a136a469a9
Fix meta tags 2 years ago
Arijit Basu d596a839ea
Fix homepage image 2 years ago
Arijit Basu 6eec0ff489 New demo and picture 2 years ago
Arijit Basu 2bd4346156 Improve CLI help menu 2 years ago
Arijit Basu 6d9d324a7d
Upgrade version 2 years ago
Arijit Basu b7ee0fc2e1
Fix cargo build 2 years ago
Arijit Basu 68ae6f5265
Don't release android for now 2 years ago
Arijit Basu b0af9d7f69
Release android binaries 2 years ago
Arijit Basu 171c93dbb1 Fix ansi colors
Also upgrade deps and do some cleanup.
2 years ago
Arijit Basu bbcd0be0c4
Fix book 2 years ago
Arijit Basu 569d3559d6
Set ea id 2 years ago
Arijit Basu 0e380f980c
Try EthicalAds on sidebar 2 years ago
Arijit Basu 6c775d72dc
Bold directories 2 years ago
Arijit Basu f6364b5403 Try a new UI 2 years ago
Arijit Basu 9b686baf50 Document luarocks support 2 years ago
Arijit Basu adc47b72c3
Trigger deployment 2 years ago
Arijit Basu 9673eba89c Upgrade version
- Upgrade rust to 1.58.1
- Upgrade rust edition to 2021
- Upgrade xplr packages
2 years ago
Arijit Basu ebfbe39668
Update README.md 2 years ago
Arijit Basu a8b0775f7e
Update awesome-plugins.md 2 years ago
Arijit Basu 4c167bae7f
Update installing-plugins.md 2 years ago
Arijit Basu 536ee9b439
Update awesome-plugins.md 2 years ago
Arijit Basu 583f685627
Minor line gap 2 years ago
Arijit Basu aca6127166
Minor fixes 2 years ago
Arijit Basu 0472bc3e66 Auto generate docs 2 years ago
Arijit Basu 88b79643cb
Update configure-key-bindings.md 2 years ago
Arijit Basu 88d13e675d
Dark theme book 2 years ago
Arijit Basu 84ba95f8e3
Remove bg-hero 2 years ago
Arijit Basu 60e17f3b87
Fix GH pages 2 years ago
Arijit Basu a1d0c222a1 New website
Also try EthicalAds by Read The Docs folks.
2 years ago
Arijit Basu dfb4854f01
Set cursor position based on prompt 2 years ago
Arijit Basu bfd403c894 Upgrade version 2 years ago
Arijit Basu 28a378e031
Improve generate.py 2 years ago
Arijit Basu 2f320fa8a7
Fix docs 2 2 years ago
Arijit Basu 048f89139b
Fix publish docs 2 years ago
Arijit Basu 3d81a49cec Auto generate messages docs
- Huge refactor.
- Run `python docs/script/generate.py` to generate `docs/en/src/messages.md`.
2 years ago
Arijit Basu 67eca1ecdf
More doc fixes 2 years ago
Arijit Basu 8385b92661
Minor doc improvements 2 years ago
Arijit Basu e30116cca2
Make linkcheck optional even in gh ci 2 years ago
Arijit Basu 119adc6b57
Make linkcheck optional 2 years ago
Arijit Basu 250e21e949 Install mdbook-linkcheck in ci 2 years ago
Arijit Basu db8ef0e15a Fix docs and build 2 years ago
Arijit Basu ca211bb073 Auto generate some API docs from init.lua
This is the beginning of the xplr documentation revolution.

Closes: https://github.com/sayanarijit/xplr/issues/445
2 years ago
Arijit Basu 0cfeed7660 Upgrade github action for book.yml
Closes: https://github.com/sayanarijit/xplr/issues/426
2 years ago
Arijit Basu ecc59b1d9d Option to configure wrapping on move
Closes: https://github.com/sayanarijit/xplr/issues/441
2 years ago
Arijit Basu 618ade53ec Document init.lua
Ref: https://github.com/sayanarijit/xplr/issues/445
2 years ago
Arijit Basu 721ffd0216 Support custom `border_type` and `border_style`
This adds support for defining custom border types and border styles.

Example:

```
xplr.config.general.panel_ui.default.border_type = "Thick"
xplr.config.general.panel_ui.default.border_style.fg = "Black"
xplr.config.general.panel_ui.default.border_style.bg = "Gray"
```

Closes: https://github.com/sayanarijit/xplr/issues/448
2 years ago
Arijit Basu 26a363c7b1
Update message.md 2 years ago
Arijit Basu 74d5bb5e87
Update awesome-hacks.md 2 years ago
Arijit Basu 4f1e8e9021
Fix doc 2 years ago
Arijit Basu 7c3e74e46c
Document cd on quit 2 years ago
Arijit Basu 5e20eea349
Update docs 2 years ago
Arijit Basu 6b054cbc01
Minor fixes 2 years ago
Arijit Basu c4ad648556
Add plugin find.xplr 2 years ago
Arijit Basu 087f3b0d52 Version 0.17.2 2 years ago
Arijit Basu bb65870ee0 CLI for a better "cd on quit"
Use `--print-pwd-as-result` to print the last working directory instead
of the focused or selected nodes, when you quit using the `PrintResultAndQuit`
message (i.e. by pressing `enter`).

This helps with implementing the "cd on quit" functionality using a plain shell
alias.

Example:

```
alias xcd='cd "$(xplr --print-pwd-as-result)"'
```

With this alias set, you can navigate directories using xplr by entering
`xcd` command, and when you quit by pressing enter, you will enter the
directory.

You can of course, quit with plain `Quit` (i.e. by pressing `esc`) to
gracefully cancel "cd on quit".
2 years ago
Arijit Basu 7da7c54e86
Add direct download link for linux musl 2 years ago
Arijit Basu bb034180f4
Drop snapcraft 2 years ago
Arijit Basu 1627e3974f
Remove xplr-bin 2 years ago
Arijit Basu e6ea983a8a Add "duplicate as"
Closes https://github.com/sayanarijit/xplr/issues/434
2 years ago
Arijit Basu 64c248f86d
Minor fix 2 years ago
Arijit Basu ef0d37e9f6
Update Writing Plugins docs 2 years ago
Arijit Basu e2c3251736
Fix focus when creating files in non-$PWD location 2 years ago
Arijit Basu 4ff11c2ef4 Upgrade version 2 years ago
Arijit Basu 24bd6d9f00 Merge the pending changes from #429 2 years ago
Arijit Basu 729b51d674 Create parent directories before creating the file 2 years ago
Arijit Basu 533a591d7f
Add dual-pane.xplr 3 years ago
Tom van Dijk 17f3893198
Debug mode
Features:
- Display debug menu on configuration error
- Configure wether xplr should show the debug menu
    (through `debug_on_error`)
- Open logs in editor
- Redirect people to the issues page on Github.
3 years ago
Arijit Basu ebbce317a0
Update introduction.md 3 years ago
Arijit Basu 9cafe0d09f
Update introduction.md 3 years ago
Arijit Basu 17036c98f0
Update README.md 3 years ago
Arijit Basu 7723f35944
Remove icons.xplr 3 years ago
Arijit Basu 95abe5e22b
Add dtomvan/icons.xplr 3 years ago
Arijit Basu cdd60eef4c
Format init.lua consistently 3 years ago
Arijit Basu c89d5a45f1
Fix NixOS tests 3 years ago
Arijit Basu 59c8db821b
Fix doc 3 years ago
Arijit Basu 7364812554
Add upgrade guide 3 years ago
Arijit Basu 3142cdd3ec Upgrade version 3 years ago
Arijit Basu 7b9e4deff5 Support passing argument to `LuaEval` and `LuaEvalSilently`
If the argument of `LuaEval` evaluates to a function, xplr will try to
pass Lua Context to it.

Example:

```lua
{ LuaEval = [[function(app) return { { LogInfo = app.pwd } } end]] }
```

Closes: https://github.com/sayanarijit/xplr/issues/394
3 years ago
Arijit Basu ea235b6969
Fix custom dynamic list layout demo 3 years ago
Arijit Basu 8f5e5491f2 Optimize change directory performance
This PR breaks the custom layout renderer API by deprecating the
following heavyweight fields in the Lua Context passed to the renderer
functions.

The following fields are being deprecated:

- app.directory_buffer
- app.history
- app.last_modes

However, there's no change in the Lua Context passed to the functions
called via `CallLua*` messages.

Closes: https://github.com/sayanarijit/xplr/issues/418
3 years ago
Arijit Basu e370c25bc5
Confinement 'classic' not allowed with plugs/slots 3 years ago
Arijit Basu a97ff38e7a
Use classic confinement for snap 3 years ago
Arijit Basu ad2c7ab695 Fix snap build 3 years ago
Arijit Basu 807f715f8f
Update installation docs 3 years ago
Arijit Basu 9683676587 Add snap build 3 years ago
Arijit Basu b7d86ae1ec Serialize None to nil
Fixes: https://github.com/sayanarijit/xplr/issues/417
3 years ago
Tom van Dijk 04dde7a477 Docs "help buffer" -> "help menu" 3 years ago
Tom van Dijk b1ac4944ab `help_hide_remaps` -> `hide_remaps_in_help_menu` 3 years ago
Tom van Dijk 263eb5943a Introduce `general.help_hide_remaps` config entry. 3 years ago
Arijit Basu def98de9b3 Upgrade version 3 years ago
Arijit Basu 5b710070a3 Optimize more 3 years ago
Arijit Basu 41c387542f Fix auto jumping on cursor when visiting large dir 3 years ago
Arijit Basu 7c7351c2e8 Optimize optimization hack 3 years ago
Arijit Basu 5211e4fa40 Fix Lua API performance by caching
Fixes: https://github.com/sayanarijit/xplr/issues/412
3 years ago
Arijit Basu 05f048ce53 Add demo video
Video contributed by @igorepst

Closes: https://github.com/sayanarijit/xplr/issues/389
3 years ago
Arijit Basu b29c25c66f
Update install.md 3 years ago
Arijit Basu 132b528a5d
Remove trailing `.` in the CLI help menu 3 years ago
Arijit Basu d2c5f49835 Fix CD (try again) 3 years ago
Arijit Basu 0443c103e7 Fix doc 3 years ago
Arijit Basu 296d93a81a Fix linux gnu bin release 3 years ago
Arijit Basu 605b2bc391 Fix CD for musl 3 years ago
Arijit Basu 9d088239ea Upgrade to 0.16.0 3 years ago
Igor Epstein a5a71c9191 Add context-switch plugin to docs 3 years ago
Arijit Basu 24637449a3
Improve CLI help 3 years ago
Arijit Basu f78d9e99fd
Remove debug statement and add tests 3 years ago
Arijit Basu 2acfc97653
Update awesome-plugins.md 3 years ago
Arijit Basu 98799d0967
add ouch.xplr 3 years ago
Arijit Basu 8e00b3e658
Add scroll for the input buffer 3 years ago
Arijit Basu 3692647a7a
Update debug-key-bindings.md 3 years ago
Arijit Basu 858b4066ca
Update quickstart.md 3 years ago
Arijit Basu 3ac39fd8f4
Fix doc link 3 years ago
Arijit Basu b0c91a954e
Improve update_input_buffer 3 years ago
Arijit Basu 074217f21e Fix issue with input buffer 3 years ago
Arijit Basu 48ab6eac21 Add on_function key handler
This adds `on_function` handler to handle F1-F12 keys.

This also fixes issues with previously added handlers and adds a
checklist for future additions.
3 years ago
Arijit Basu 6a3b26cc18 Add support for LuaEval(Silently) messages
This PR adds support for quickly executing arbitrary lua functions,
without needing to define a function.

Example:

```lua
xplr.config.modes.builtin.default.key_bindings.on_key["#"] = {
  help = "test",
  messages = {
    { LuaEvalSilently = [[return { { LogInfo = "foo" } }]] },
    { LuaEval = [[return { { LogInfo = io.read() } }]] },
  },
}
```

Partly closes: https://github.com/sayanarijit/xplr/issues/394
3 years ago
Arijit Basu 165d992b75 Fix logs UI
Fixes: https://github.com/sayanarijit/xplr/issues/396
3 years ago
Arijit Basu 05c2f7aa68 Support more control over input buffer
This PR adds a new message: `UpdateInputBuffer: InputOperation`

This makes it possible to perform cursor based input operations without
needing input from the keyboard.
3 years ago
Arijit Basu b45a553a0c Add more keyboard input handlers
This PR adds 3 more keyboard input handlers:

- on_alphanumeric
- on_character
- on_navigation

Also updates documentation.
3 years ago
Arijit Basu ca6cefb1c1 Use tui-input to handle input buffer
New message: `UpdateInputBufferFromKey` to replace most
`BufferInputFromKey` usage.

This adds more functionalities to the input buffer and reduces
boilerplate code.

This commit also deprecates `config.general.cursor` as the cursor will
be set by the terminal from now.

Repo: https://github.com/sayanarijit/tui-input
3 years ago
Arijit Basu 4495740cb0
Add fm-nvim integration 3 years ago
Shunsuke Mie f5131b08ab Change to use `PWD` env vairable to get current dir
Fixes 285.
3 years ago
Arijit Basu 00030e44ff Fail if path doesn't exist 3 years ago
Arijit Basu de6f54f9bd Fix bug with selection 3 years ago
Arijit Basu a797d7b1c7 Format & lint code 3 years ago
Arijit Basu 2d7158afc0 Implement support path selection and force focus
Examples:

```bash
xplr $PWD /path/to/select1 path/to/select2

xplr -- $PWD /path/to/select1 path/to/select2

echo -e "$PWD\n/path/to/select1\npath/to/select2" | xplr -

ls -d /path/to/select1 path/to/select2 | xplr $PWD -
```
3 years ago
Arijit Basu 91675e28af Some cleanup 3 years ago
Tom van Dijk 102832c65c Fix suggestions and run clippy 3 years ago
Tom van Dijk 44f05c6e4b Implement --force-focus 3 years ago
Tom van Dijk 3c8de699ca Fix everything @sayanarijit fed back. 3 years ago
Tom van Dijk cbb244f9a0 Implement --select 3 years ago
Arijit Basu a81dd3f63f
Update awesome-plugins.md 3 years ago
Arijit Basu abaaa91409
Add more tips for writing plugins 3 years ago
jmechnich 6efd29c3dd updated dependencies of mlua in order to fix https://github.com/sayanarijit/xplr/issues/384 3 years ago
Shunsuke Mie 05fb583f98 Add a test for pwd_watcher 3 years ago
Shunsuke Mie c5f60951f7 Add tests for explorer
The comment outed test fails randomly.  Change the recusive function
before enableing the test.
3 years ago
Arijit Basu ba3cf24125 Release musl binary
Closes https://github.com/sayanarijit/xplr/issues/269
3 years ago
Shunsuke Mie 1947d77a33 Change mods name for tests
There are two mod names for test, `test` and `tests`. This commit unites
the name to `tests`. It is commonly used.
3 years ago
Arijit Basu 061d1b068a Add docs to crate 3 years ago
Arijit Basu 4e1580e91f Some cleanups 3 years ago
Shunsuke Mie 41d1385020 Fix a Node type of directory
Fix a issue #364.
3 years ago
Arijit Basu 66f5acc482 Fix criterion test 3 years ago
Arijit Basu 3ec870739c Bump version 3 years ago
Arijit Basu 0328eb63c7 Fix selection count
Fixes https://github.com/sayanarijit/xplr/issues/363
3 years ago
Shunsuke Mie 13d046e6ad Move definitions runner related functions
fn runner() and fn from_cli(cli: Cli) util functions are located to
app.rs. However, those are utils for runner. So this commit change the
functions to runner.rs.
3 years ago
Shunsuke Mie a3b400d749 Add a trace level to test CI task
To enable to get stack trace on CI, add a `RUST_BACKTRACE` env
variable. It is useful to debug.

Some rust projects applied this parameter. e.g. deno
https://github.com/denoland/deno/blob/main/.github/workflows/ci.yml#L54
3 years ago
Arijit Basu 947cfeb327
Minor fixes and format docs 3 years ago
Hans Tognon 413430d5f8 added remaining fields for general config. 3 years ago
Arijit Basu cd9880de71
Update community.md 3 years ago
Arijit Basu 385c8fd755
Add matrix chat link 3 years ago
Arijit Basu 258937c307
Run cargo diet 3 years ago
Arijit Basu ee48fa1568
Don't open new tabs by default 3 years ago
Arijit Basu 884cd98d0b Minor links and formatting fixes for the homepage 3 years ago
Zain ul abideen 4ad4e0894d Updates in 2nd version of xplr 3 years ago
Zain ul abideen 8c9905f7f9 Updates in 2nd version of xplr 3 years ago
Zain Ul abideen 8bac4cf92a Adding version2 of the website xplr 3 years ago
Arijit Basu 347b9625aa Add padding to custom content titles 3 years ago
Arijit Basu 4bb54d49df
Fix typo 3 years ago
Gilad Woloch 402715cdcd Remove trivial `ResolvedNode` methods 3 years ago
Gilad Woloch 95b580203f Introduce `xor` helper 3 years ago
Gilad Woloch 18b784f054 Correct typo 3 years ago
Gilad Woloch 2ef7523bb0 Derive `PartialEq` 3 years ago
Gilad Woloch 46287f6373 Use `Result::is_ok` 3 years ago
Gilad Woloch 45ad449eb3 Avoid unnecessary lambda 3 years ago
Arijit Basu 7142c97c7f
Add map.xplr 3 years ago
Gilad Woloch a2343c05ca Correct `clippy` error 3 years ago
Gilad Woloch cc022e85ff Fix `cargo clippy` warnings and reformat 3 years ago
Gilad Woloch c399236fd3 Reformat 3 years ago
Gilad Woloch 3f0e479f56 Remove trivial methods 3 years ago
Arijit Basu 9982a205db Downgrade version for the docs
Upgrade version when just before making a new release, else the doc will
link to invalid release.
3 years ago
Arijit Basu bec80e98df Improve input reading by eliminating pause
Stop the thread instead of pausing it. This improves the CPU usage and
responsiveness a slightly.
3 years ago
Gilad Woloch ac1b40799a Apply `rustfmt` 3 years ago
Gilad Woloch 394c09ae87 Merge normal/reversed cases in `NodeSorterApplicable::apply` 3 years ago
Gilad Woloch a1ee7f931c Ignore Vi[m] backups & Jetbrains config 3 years ago
Arijit Basu b2ad4a1303
List more examples in "writing plugins" section 3 years ago
Arijit Basu 113bdde01b
Further improve upgrade guide. 3 years ago
Arijit Basu dcbccde096
Improve upgrade guide
Upgrade guide is not release note.
3 years ago
Arijit Basu e0c1581a0a Optimize silent calls
Silent calls don't need to pause input reading as they are non
interactive and quickly finish execution.
3 years ago
Arijit Basu 5e96d48135 Major release 0.15.0 3 years ago
Arijit Basu 3a897ea0d1
Fix doc 3 years ago
Arijit Basu dd73220ec8 Support switching modes keeping input buffer.
Closes: https://github.com/sayanarijit/xplr/issues/303
3 years ago
Arijit Basu 14e1360820
More cleanup 3 years ago
Arijit Basu ac1476bc08
Cleanup 3 years ago
Arijit Basu aee49dad78 Support terminal colors in custom layout
Example:

```lua
xplr.config.layouts.builtin.default = {
  CustomContent = {
    title = "custom title",
    body = {
      StaticParagraph = { render = "\x1b[31mcustom body\x1b[0m" },
    },
  },
}
```
3 years ago
Arijit Basu 5eab3c6033 Support defining custom layout for different modes
Closes: https://github.com/sayanarijit/xplr/issues/335
3 years ago
Arijit Basu 85cc956792 Fix reading input
Wait for confirmation after sending a message to a thread.

Also, use unwrap() to crash and burn than using unwrap_or_default() when
message passing fails.

It's highly unlikely to happen and also trivial to the core logic. But
let's see the error when it does happen.

Fixes https://github.com/sayanarijit/xplr/issues/301
3 years ago
Arijit Basu 8c42a62c93 Try in docker 3 years ago
Arijit Basu 5723acc21b Fix input conversion logic 3 years ago
Arijit Basu 9e08287c31 Fix CI 3 years ago
Arijit Basu e3150798d9 Disable recover mode by default
Closes: https://github.com/sayanarijit/xplr/issues/330
3 years ago
Arijit Basu bb5dcff0bf
Use --locked in CI/CD & remove daily compile check
Daily compile check doesn't capture runtime errors. It's better to
--locked and be safe.
3 years ago
Arijit Basu 20bb7939fe Fix gh pages CNAME 3 years ago
Arijit Basu 068d45e657 Always pass `--locked` when installing using cargo
Closes: https://github.com/sayanarijit/xplr/issues/332
3 years ago
Arijit Basu 6ae7c96937 Improve xplr homepage 3 years ago
Arijit Basu 14b14bba42 Let's get a hew home - xplr.dev
Thanks to your continued support we're getting a new homepage https://xplr.dev
3 years ago
Arijit Basu 696549e2e5 Remove `config` from CallLuaArg
The `Config` object is globally available as `xplr.config`, and thus
it's redundant. Config is read only once, when xplr loads.

Closes https://github.com/sayanarijit/xplr/issues/321
3 years ago
Arijit Basu 69d3b3af46 Add focus selection UI
Closes: https://github.com/sayanarijit/xplr/issues/322
3 years ago
Arijit Basu 3f668c2d04 Improve runner API
Some API improvements on top of #324
3 years ago
Tom van Dijk 671d1b11fd Refactored parts of `runner::Runner` to take a Cli struct, instead of putting everything manually in `Runner`. 3 years ago
Arijit Basu a6dcd476aa
Update TODOs 3 years ago
Arijit Basu a7f7fa0012
Fix link 3 years ago
Arijit Basu f1c417063d
Improve documentation
Remove some TODO, add more links.
3 years ago
Arijit Basu 275c4d0b7b
Add command-mode.xplr 3 years ago
Arijit Basu 9685cdedcb
Add xplr.nvim, nvim-ctrl.xplr 3 years ago
Arijit Basu ad6c8b7617
Update deps 3 years ago
Arijit Basu 44a6b6f1b0
Fix doc links 3 years ago
Arijit Basu e9a48613de
Add plugin alacritty.xplr 3 years ago
Arijit Basu 1756332e5b Fallback to tempdir if runtime is inaccessible
Fixes: https://github.com/sayanarijit/xplr/issues/319
3 years ago
Arijit Basu 1b1032d0bd Fix missing config error
Do not report error when config file is missing and it's not specified
via CLI args.
3 years ago
Arijit Basu abc7011f56
Test upgrade guild is up to date.
Never miss updating the upgrade guide again
3 years ago
Arijit Basu e7c3c7044e
Update upgrade guide 3 years ago
Arijit Basu 7cc8c621ef Update version 3 years ago
Arijit Basu 79b3676662
Improve doc
Make links readable.
3 years ago
Arijit Basu a5607a8186
Improve doc
Add related links to "Writing Plugins".
3 years ago
Arijit Basu 61657a70c7 Add support for loading extra config files
Use `-C` / `--extra-config` to load Lua files to overwrite the default
or user defined config.

This helps with integration, where integrating xplr with another tool
requires xplr to overwrite some config, without requiring the users to
install an xplr plugin or update the xplr config.

Example:

```bash
    xplr -C one.lua two.lua

    # Or

    xplr -C one.lua -C two.lua
```

> **WARNING:**
>
> Extra config doesn't require specifying the `version`, hence, it's the
> integration author or the user's responsibility to assert
> compatibility using the globally exposed `version` in the extra config
> files, similar to xplr plugins.

Ref: https://github.com/sayanarijit/xplr/issues/316
3 years ago
Arijit Basu d6766919de
Update awesome-plugins.md 3 years ago
Arijit Basu 1c9b8322ec
Update awesome-plugins.md 3 years ago
Arijit Basu b66155895a
Update awesome-plugins.md 3 years ago
Arijit Basu 7b2b29b27d Cleanup - Format Lua code 3 years ago
Arijit Basu af5a99328f Update version 3 years ago
Arijit Basu 35c18a25dc Remove per directory buffer
Closes: https://github.com/sayanarijit/xplr/issues/289
3 years ago
Arijit Basu 7d0605479e Allow dynamic linking to load Lua modules using C
Also upgrade packages.

Closes: https://github.com/sayanarijit/xplr/issues/309
3 years ago
Arijit Basu 865d6930db Use unsafe lua to allow c modules
Closes: https://github.com/sayanarijit/xplr/issues/309
3 years ago
Arijit Basu f1e75caf89 Remove termion dependency
Termion is only used to get TTY which is simple enough to implement.
3 years ago
Arijit Basu 5626335e0e
More docs 3 years ago
Arijit Basu 9070cd9e17 Add more docs 3 years ago
Arijit Basu 6162744bef Minor improvements
Closes: https://github.com/sayanarijit/xplr/issues/305
3 years ago
Arijit Basu 1b6ea5b63c
Add icons.xplr 3 years ago
Arijit Basu 6b926c2b88
Minor fix again 3 years ago
Arijit Basu d6893130a6
Minor doc fixes 3 years ago
Arijit Basu 3171c37d4e
Add type-to-nav.xplr to awesome plugins 3 years ago
Arijit Basu a12818ea30
Add qrcp.xplr 3 years ago
Arijit Basu 72712a8bae
Update install.md 3 years ago
Arijit Basu b916701d59
Add completion.xplr 3 years ago
Raf Czlonka 9b20affbfd Use 'noice' actual repository URL 3 years ago
Arijit Basu e1fb5e02c5
Add paste-rs.xplr to Awesome Plugins
Also, fix the cargo documentation link and delete unused doc page.
3 years ago
Arijit Basu 193202c325
Improve FreeBSD install instructions 3 years ago
Arijit Basu f5aceb1e66 Make the number of visible logs dynamic
It helps to see all the logs when switching to a bigger InputAndLogs
layout.
3 years ago
Arijit Basu 7eb81c33de
Update FUNDING.yml 3 years ago
Arijit Basu db9c926b0c Update version 3 years ago
Arijit Basu b1ce57d68b Recursively clean session path
Fixes: https://github.com/sayanarijit/xplr/issues/293
3 years ago
Arijit Basu 6babfeb3d6 Minor improvements
Fixes: https://github.com/sayanarijit/xplr/issues/284
Closes: https://github.com/sayanarijit/xplr/issues/286
Partially fixes: https://github.com/sayanarijit/xplr/issues/285
3 years ago
Arijit Basu 4307ba657a Fix ToggleSelectionByPath
Fixes: https://github.com/sayanarijit/xplr/issues/295
3 years ago
Arijit Basu 6a63f5f3c7
Update awesome-plugins.md 3 years ago
Arijit Basu ea4becf52e
Update awesome-plugins.md 3 years ago
Arijit Basu 3603d24c95
Update modes.md 3 years ago
Arijit Basu 91f8cdf889
Update modes.md 3 years ago
Arijit Basu 9f8a4222ee
Update modes.md 3 years ago
Arijit Basu 1e13fdacda Use footer links
Tool used: https://github.com/sayanarijit/md-footer
3 years ago
Arijit Basu e37752d41e
Document keys and fix links 3 years ago
Arijit Basu fc89910cf7
Move introductions & reviews up 3 years ago
Arijit Basu e3e362d683
Add introductions & reviews in README 3 years ago
Arijit Basu bc140c165b
More doc fixes 3 years ago
Arijit Basu 6056dbc0df
Fix typo 3 years ago
Arijit Basu f42285f459
Fix doc formatting 3 years ago
Arijit Basu 659d2db075
Document all the messages for human 3 years ago
Arijit Basu 0542ad6b7e
Update awesome-plugins.md 3 years ago
Arijit Basu c9d91660d3
Add preview-tabbed.xplr 3 years ago
Arijit Basu 3e6c6954f3
Add dragon.xplr 3 years ago
Arijit Basu 0e9a96f0a8
Add zoxide.xplr 3 years ago
Arijit Basu 41e0f3c09e
Update awesome-plugins.md 3 years ago
Arijit Basu be0a96abae
Update awesome-plugins.md 3 years ago
Arijit Basu 7e373972f4
Update integration.md 3 years ago
Arijit Basu ed0ca1009d
Update writing-plugins.md 3 years ago
Arijit Basu 5779c3447b
Fix doc 3 years ago
Arijit Basu 8ad962214a
Fix doc 3 years ago
Arijit Basu fb8f60c822
Add trash-cli.xplr plugin 3 years ago
Arijit Basu 67f604e4f0
Fix typos 3 years ago
Arijit Basu 4d61f2e9ad
Improve docs
Add features and integration docs.
3 years ago
Arijit Basu ea9c33539a
Update node_types.md 3 years ago
Arijit Basu 8da9cf23b2
Update fzf.xplr 3 years ago
Arijit Basu 3771dc9efe
Update awesome-plugins.md 3 years ago
Arijit Basu 8d576ba629
Update sorting.md 3 years ago
Arijit Basu 18b7a0b3fa
Update modes.md 3 years ago
Maxim Baz 0df4f87d7f Update pacman command 3 years ago
Arijit Basu 05272a6575
Update filtering.md 3 years ago
Arijit Basu 9c144c0f30
Update general-config.md 3 years ago
Arijit Basu d7d7ade877
Update configuration.md 3 years ago
Arijit Basu 2d9a256691
Update quickstart.md 3 years ago
Arijit Basu 32a32e9180 Add more docs
Also, prepare for release.
3 years ago
Arijit Basu d29f6aed6d Fix StartFifo and ToggleFifo
Also add `xplr.config.general.start_fifo`.

Closes: https://github.com/sayanarijit/xplr/issues/280
3 years ago
Arijit Basu 2df8d47b0f
Fix doc 3 years ago
Arijit Basu cf2a38fe5f
Fix doc 3 years ago
Arijit Basu 2cd17f18d8
Add fzf.xplr 3 years ago
Arijit Basu c4c1020aa0 Add dua-cli plugin 3 years ago
Arijit Basu 3cadaf8880 Add plugin docs
Also minor doc improvements
3 years ago
Arijit Basu 7ed89a4aff Mime wildcard 3 years ago
Arijit Basu 4be0a23d4f Update readme 3 years ago
Arijit Basu 1f3d6f2152 Document node types 3 years ago
Arijit Basu cdc1e6825b
Update message.md 3 years ago
Arijit Basu 2146bc646b
Update book.toml 3 years ago
Arijit Basu cb38ca048d Typos and documentation fixes 3 years ago
Arijit Basu c1b667d9f7 Fix publish attempt 2 3 years ago
Arijit Basu 107d5a79a3 Fix publishing book 3 years ago
Arijit Basu 3e646b063b Publish xplr book with intensive documentation
Closes: https://github.com/sayanarijit/xplr/issues/263
3 years ago
Arijit Basu b61bf9dcf2 Increase poll timeout
Ref: https://github.com/sayanarijit/xplr/issues/10
3 years ago
Arijit Basu ed65d85568 Remove auto refresh service
Ref: https://github.com/sayanarijit/xplr/issues/10
3 years ago
Arijit Basu 5c6fd1ef63 Update version 3 years ago
Arijit Basu 329821ca1b Improve FocusNext and FocusPrevious behavior
Closes: https://github.com/sayanarijit/xplr/issues/253
3 years ago
Arijit Basu 4d8f1ef2ef Fix handling of relative paths
Closes: https://github.com/sayanarijit/xplr/issues/255
3 years ago
Arijit Basu f6d1be61d5 Add quit mode and key bindings
`:q<enter>` -> Just quit.
`:qp` -> quit printing pwd.
`:qf` -> quit printing focus.
`:qs` -> quit printing selection.
`:qr` -> quit printing result.

Ref: https://github.com/sayanarijit/xplr/issues/257
3 years ago
Arijit Basu 6b03598b5d Add more quit options
Adds the following messages.

- PrintPwdAndQuit
- PrintFocusPathAndQuit
- PrintSelectionAndQuit

Closed: https://github.com/sayanarijit/xplr/issues/257
3 years ago
Arijit Basu 6a70b568bf Fix FocusPath issue
Fixes: https://github.com/sayanarijit/xplr/issues/249
3 years ago
Arijit Basu f1ffc551fc Minor addition 3 years ago
Arijit Basu 844480204c Support specifying read-only mode via CLI argument
Closes: https://github.com/sayanarijit/xplr/issues/22
3 years ago
Arijit Basu f12e1e5290 Fix config path on macOS
Also, add `-c` / `--config` CLI option to specify custom config file.

Priority is:

`-c <PATH>` > `~/.config/xplr/init.lua` > `/etc/xplr/init.lua`.

Fixes: https://github.com/sayanarijit/xplr/issues/230
3 years ago
Arijit Basu 1513c325d6 Add option to disable recover mode
Use `config.general.disable_recover_mode = true` to disable the recover
mode.

Closes: https://github.com/sayanarijit/xplr/issues/232
3 years ago
Arijit Basu a2f42ac6fc Add support for FIFO based previewer
Adds basic support for nnn-like FIFO based previewer.

The FIFO can be manager with the following messages:

- StartFifo: /path/to/fifo
- StopFifo
- ToggleFifo: /path/to/fifo

A basic nnn plugin wrapper example:

```lua
-- Usage Example:
--
--   require("nnn_preview_wrapper").setup{
--     plugin_path = os.getenv("HOME") .. "/.config/nnn/plugins/preview-tabbed",
--     fifo_path = "/tmp/xplr.fifo",
--     mode = "action",
--     key = "p",
--   }
--
-- Press `:p` to toggle preview mode.

local function setup(o)

  if o.fifo_path == nil then
    o.fifo_path = os.getenv("NNN_FIFO")
  end

  if o.mode == nil then
    o.mode = "action"
  end

  if o.key == nil then
    o.key = "p"
  end

  local enabled = false
  local message = nil

  os.execute('[ ! -p "' .. o.fifo_path ..'" ] && mkfifo "' .. o.fifo_path .. '"')

  xplr.fn.custom.preview_toggle = function(app)

    if enabled then
      enabled = false
      message = "StopFifo"
    else
      os.execute('NNN_FIFO="' .. o.fifo_path .. '" "'.. o.plugin_path .. '" & ')
      enabled = true
      message = { StartFifo = o.fifo_path }
    end

    return { message }
  end

  xplr.config.modes.builtin[o.mode].key_bindings.on_key[o.key] = {
    help = "search with preview",
    messages = {
      "PopMode",
      { CallLuaSilently = "custom.preview_toggle" },
    },
  }
end

return { setup = setup }
```

Press `:p` to toggle preview mode.

Closes: https://github.com/sayanarijit/xplr/issues/205
3 years ago
Arijit Basu 2962a8d52d Further improve the API.
This improves the compatibility and adds the ability to introduce
non-breaking changes by using a builder pattern.

Example:

```rust
fn main() {
    match xplr::runner(None).and_then(|a| a.run()) {
        Ok(Some(out)) => print!("{}", out),
        Ok(None) => {}
        Err(err) => {
            if !err.to_string().is_empty() {
                eprintln!("error: {}", err);
            };

            std::process::exit(1);
        }
    }
}
```
3 years ago
Arijit Basu fd3e8a5a9f
fix discord link 3 years ago
Arijit Basu fabcc8e865 Implement CLI arguments
Going with custom CLI parsing for minimalism and flexibility.

Closes: https://github.com/sayanarijit/xplr/issues/228
3 years ago
Arijit Basu a1a1dee4af Remove --locked
Many crates are outdated. One is even yanked. IMO failing build is
better than running with insecure/bad dependencies.

Ref: https://github.com/sayanarijit/xplr/issues/212#issuecomment-855175144
3 years ago
Arijit Basu 9a22c8b423 Fix pgp publishing attempt 4 3 years ago
Arijit Basu 7e35ff42fc Fix pgp publishing attempt 3 3 years ago
Arijit Basu 6991db8c33 Fix gpg publishing attempt 2 3 years ago
Arijit Basu cb13736a9f Fix releasing gpg signature 3 years ago
Arijit Basu c5e97d5bae Sign releases with pgp
Closes: https://github.com/sayanarijit/xplr/issues/131
3 years ago
Arijit Basu 902f20aa83 Fix focus jumping
Fixes: https://github.com/sayanarijit/xplr/issues/211
3 years ago
Arijit Basu 36ab821d2c Upgrade dependencies
Closes: https://github.com/sayanarijit/xplr/issues/187
3 years ago
Arijit Basu 7496f5bf8f Disable mouse by default and bind `:m` to toggle
- Make mouse disabled by default.
- Add key binding `:m` to toggle mouse.

Closes: https://github.com/sayanarijit/xplr/issues/206
3 years ago
Arijit Basu 7de0811eaf No need to "Refresh" explicitly
Closes: https://github.com/sayanarijit/xplr/issues/207
3 years ago
Arijit Basu 81854b9323 Use --locked when releasing
Closes: https://github.com/sayanarijit/xplr/issues/212
3 years ago
Arijit Basu 72a86f8e0e Polish xplr library API
Minor improvements to the xplr library API.

Closes: https://github.com/sayanarijit/xplr/issues/213
3 years ago
Arijit Basu 69ac8883ab Check xplr compilation daily
https://github.com/sayanarijit/xplr/issues/208 should never happen
again. This added check will notify me if `xplr` compilation starts
failing.
3 years ago
Arijit Basu 6412856d73 Improve docs 3 years ago
Arijit Basu b284124022 Optimize rendering speed by serializing less
Serializing to and from Lua value is expensive. Hence, once serialized,
we should reuse the value.
3 years ago
Arijit Basu fc7d205d92 Improve CallLua and CallLuaSilently
Pass a custom table, optimized for convenience and speed.
3 years ago
Arijit Basu 695acf5c1e Inherit node types config
Inherit `node_types` config instead of overwriting it.

Closes: https://github.com/sayanarijit/xplr/issues/200
3 years ago
Arijit Basu 88aedddf53 Fix rustc 1.50 compatibility 3 years ago
Arijit Basu b4247a7d03 Improve CallLua, mime_essence, permissions
Refs:
- https://github.com/sayanarijit/xplr/issues/187
- https://github.com/sayanarijit/xplr/issues/194
- https://github.com/sayanarijit/xplr/issues/195
3 years ago
Arijit Basu 9b02ef3429 Fix "ctrl-i" key for history navigation
Unfortunately, "ctrl-i" doesn't work unless "tab" i sremapped to the
key.
3 years ago
Maxim Baz eef4d92548 Remove extra spaces 3 years ago
Maxim Baz d51848daf9 Fix typo 3 years ago
Arijit Basu 6663f0851a
Update CONTRIBUTING.md 3 years ago
Arijit Basu aa7a441751 Make mode visible in input/logs panel
Also, map ":q" to "quit" action.

Ref: https://github.com/sayanarijit/xplr/issues/158
3 years ago
Arijit Basu db669cdcbf Remove "remaps:"
Remaps has been removed to simplify key bindings. With Lua, it's now
possible to remap using basic assignments.

For e.g.

```Lua
xplr.config.modes.builtin.default.key_bindings.on_key["v"] = xplr.config.modes.builtin.default.key_bindings.on_key.space
```

Help menu will auto detect remapped keys and display after removing the
redundant mappings.

Ref: https://github.com/sayanarijit/xplr/discussions/183#discussioncomment-774159
3 years ago
Arijit Basu cb695fcaa7 Add colorful permissions
Ref: https://github.com/sayanarijit/xplr/issues/187
3 years ago
Arijit Basu 91838f88ce Fix symlink handling
- Broken symlink should display without error.
- Display the symlink destination.
- Fix deleting symlinks pointing to a directory.

Fixes: https://github.com/sayanarijit/xplr/issues/185
3 years ago
Arijit Basu b86be16ee3 Make structs public
Public structs enable automatec documentation.
xplr is not (yet) a library. Even it want to become one, making fields
private is probably not the right way.
3 years ago
Arijit Basu 74a0ecb922 Fix icons 3 years ago
Arijit Basu a80bf2d683 Final touches for v0.10.0 3 years ago
Arijit Basu b99fa927bf Get out of beta now 3 years ago
Arijit Basu bfdb7736b9 Simplify pipe reader
Make pipe readers and call lua behave in a similar way.
3 years ago
Arijit Basu 84a50a8fde Add CallLua and CallLuaSilently
This works:

```lua
xplr.fn.custom.ping = function(app)
  print("What's your name?")
  local name = io.read()
  os.execute('read -p "Hello ' .. name .. ', you are in ' .. app.pwd .. '"')
  return {
    { LogSuccess = "pong" },
  }
end
```

Then it can be called via `CallLua: custom.ping`.
3 years ago
Arijit Basu cdb3560e12 Read input pipe only once when commands execute
The initial idea was to enable other tools to control `xplr` via the
input pipe. However, so far I didn't feel the need to use this feature.
And even if there is any need, it's much better to implement ad-hoc
services instead of wasting cpu resources.
3 years ago
Arijit Basu 10e874afe7 Fall back to default config when custom config fails
When the custom `init.lua` is corrupt, xplr will fall back to default
configuration, logging the encountered error after load.
3 years ago
Arijit Basu f9d13e5e4c Fix initial directory sync issue 3 years ago
Arijit Basu 4da481bf5c Cleanup all the YAML overwriting logics 3 years ago
Arijit Basu 25a9d03237 Finish porting config.yml to init.lua 3 years ago
Arijit Basu da1d7742f7 Fully migrate to init.lua
This PR aims to fully migrate `config.yml` to `init.lua`

Also, use `builtin.foo_func` instead of `xplr.fn.builtin.foo_func`.
Similarly, use `custom.foo_func` instead of `xplr.fn.custom.foo_func`.

Closes: https://github.com/sayanarijit/xplr/issues/160
3 years ago
Arijit Basu 0a2f2aeda8 Fix version 3 years ago
Arijit Basu eb06342523 Update version 3 years ago
Arijit Basu e977aeb7d3 Fix MacOS error directory not empty
With this change, xplr will delete the pipe files when command execution
is over.
3 years ago
Arijit Basu 003e90a7d1 Improve history further 3 years ago
Arijit Basu 97aa2ff8b4 Fix initial focus 3 years ago
Arijit Basu 5b2aee3479 Improve history navigation
Make path history behave like jump list and allow jumping back and
forth in the same directory.
3 years ago
Arijit Basu 5c179a9b70 Add optional mouse handler
Mouse actions will be enabled on devices that support it.
Because sometimes, scrolling with mouse is the laziest way to navigate.
3 years ago
Arijit Basu d96e620120 Begin porting config.yml to init.lua
This commit begins porting of `config.yml` to `init.lua`.
As of now, it's not be possible to do the complete migration
because of how lua and yaml handles `null`/`nil` value.

So, we will need to completely deprecate `config.yml` in order to do the
complete migration.
3 years ago
Arijit Basu 074e0d1250 Replace handlebars with Lua
Replace handlebars with Lua functions by introduction Lua function API.
3 years ago
Arijit Basu 85696ded7a
Hide demo to speed to page load 3 years ago
Arijit Basu 79855dba15 Fix CallSilently
Closes: https://github.com/sayanarijit/xplr/issues/163
3 years ago
Arijit Basu 6cc863e6d4 Add send+anyhow support for mlua
Ref: https://github.com/khvzak/mlua/issues/48
3 years ago
Arijit Basu f744553a0a Add support for native lua bindings
Ref: https://github.com/sayanarijit/xplr/discussions/146#discussioncomment-741580
3 years ago
Arijit Basu 98687515d7
Use editor to open the focus path 3 years ago
Arijit Basu 7eabd3fb7d Fix selecting broken symlink 3 years ago
Arijit Basu 8e98da5004 Add support for un-mapping keys.
Use `remaps: {key: null}` to un-map a key.

Also,
- `gx` will now open only the file under focus.
- `:sx` will open the selected files.

And other minor improvements.

Discussion: https://github.com/sayanarijit/xplr/discussions/146
3 years ago
Arijit Basu 57a0a49aae Hide logs also when calling subprocess 3 years ago
Arijit Basu c5b461f795 Increase the input poll timeout
The input poll timeout needs to stay low, else xplr will panic when you
spawn a subshell and start typing immediately.

From the `top` command, it didn't show any noticeable overhead.
3 years ago
Arijit Basu d944828d2f Minor improvements in some operations 3 years ago
Arijit Basu 96f3640fc0 Rename `reckless` to `recover`
Also, improve the warning message.
3 years ago
Arijit Basu ae8a391064 Introduce `PopMode`
This change requires manual `Refresh` after mode switches.
Also, fix the rename operation.
3 years ago
Arijit Basu 7588620c8f Hind logs when switching to input mode
Ref: https://github.com/sayanarijit/xplr/pull/143#issuecomment-840069000
3 years ago
Arijit Basu 65fa408fea Stay in filter mode after applying filters
Also stay in create mode after creating files and directories.
3 years ago
Arijit Basu c68bd96253 Add reckless mode
Pressing an invalid key will take you to the "reckless" mode. All you
need to do is calm down, escape that mode, and try again.

Closes: https://github.com/sayanarijit/xplr/issues/142
3 years ago
Arijit Basu efec86c616 Clear selection after copy 3 years ago
Arijit Basu e0f8207900 Improve key bindings
Stay in the current mode when key input is not recognised. It's better
to do nothing than doing something wrong, at least when dealing with
important files and folders.
3 years ago
Arijit Basu c8dba61d4b Further optimize performance and CPU usage
- Optimize by avoiding cloning the whole app in each iteration of the main
loop.
- Increase the input poll timeout from 1 to 200. This works because the
poll will not apply to key hold.
- Do not read input pipe if it hasn't been modified.
3 years ago
Arijit Basu e7ec27e359
Add commtit activity badge
Pretty active!
3 years ago
Arijit Basu 11a5ca168f
Update CONTRIBUTING.md 3 years ago
Arijit Basu 161b5fb1fe
Update CONTRIBUTING.md 3 years ago
Arijit Basu 67df321cd9
Update CONTRIBUTING.md 3 years ago
Arijit Basu 7f1d29f558
Create CONTRIBUTING.md 3 years ago
Arijit Basu 82b975c5f0 Fix some sync issues
Handle out messages immediately instead or scheduling in messages.
3 years ago
Arijit Basu cd5bf81646 Optimize performance
```
Benchmarking focus next item: Collecting 100 samples in estimated 5.1972 s (126k itera                                                                                      focus next item         time:   [41.216 us 41.346 us 41.494 us]
                        change: [-28.669% -28.110% -27.551%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  4 (4.00%) high mild
  5 (5.00%) high severe

Benchmarking focus previous item: Collecting 100 samples in estimated 5.0576 s (116k i                                                                                      focus previous item     time:   [43.589 us 43.754 us 43.927 us]
                        change: [-29.506% -28.748% -28.039%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  1 (1.00%) high mild
  2 (2.00%) high severe

Benchmarking focus first item: Collecting 100 samples in estimated 5.1765 s (116k iter                                                                                      focus first item        time:   [44.071 us 44.340 us 44.634 us]
                        change: [-26.739% -26.314% -25.885%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 12 outliers among 100 measurements (12.00%)
  8 (8.00%) high mild
  4 (4.00%) high severe

Benchmarking focus last item: Collecting 100 samples in estimated 5.1522 s (116k itera                                                                                      focus last item         time:   [43.950 us 44.214 us 44.541 us]
                        change: [-27.571% -26.953% -26.337%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  5 (5.00%) high mild
  6 (6.00%) high severe

Benchmarking leave and enter directory: Collecting 100 samples in estimated 5.4863 s (                                                                                      leave and enter directory
                        time:   [96.645 us 96.915 us 97.234 us]
                        change: [-28.720% -27.224% -25.666%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  6 (6.00%) high mild
  3 (3.00%) high severe
```
3 years ago
Arijit Basu e9680d9abd Optimize release binary size
Also improve the $PWD watcher logic.
3 years ago
Arijit Basu 9597e78387 Remove unused dev dependencies
For now, we're only using criterion. I hope we bring them back soon with
real purpose.
3 years ago
Arijit Basu b64d6b59a5 Remove dependency notify
Using a whole crate just to watch `$PWD` for the last modification time
was a overkill.
3 years ago
Arijit Basu 50d6edb168 Fix search 3 years ago
Arijit Basu 3e812045e6
Fix release 3 years ago
Arijit Basu 92545f6387 Fix rename breaking UI 3 years ago
Arijit Basu 7c5468cabe Fix exploring and escaping paths
This PR targets 2 pain points.

1. The `Explore` message was async, which caused some unexpected
   behavior. This was fixed by splitting `Explore` into `ExplorePwd`,
   `ExplorePwdAsync` and `ExploreParentsAsync`. `ExploreParentsAsync`
   is similar to the former `Explore`, which is mainly used when loading
   `xplr` for the first time. However, what we'll be using frequently
   are `ExplorePwd` and `ExplorePwdAsync` messages.

2. Files with spaces caused some unexpected behavior. This was fixed by
   escaping the paths properly. This also fixed focusing of a file after
   creating or renaming it.

Anothor breaking change is that `XPLR_PIPE_FOCUS_OUT` has been removed.
`XPLR_FOCUS_PATH` is all we need. So, the rule of thumb is if a variable
contains one liner value, it can be used directly from the env vars.
Variables that can contain multi-line values, will be exposed via the
pipes.

Minor changes are

- Add `switch_mode` mode to the global key binding help menu
- Moved some UI related code from config.rs to ui.rs.
- Fixed compilation issue on `rustc 1.50.0`.
3 years ago
Arijit Basu 52fbaef189 Fix displaying global help menu
Also support toggle selection in search mode.
And use a pager to display logs.
3 years ago
Arijit Basu 9e89c6503d Use key `tab` to select files in search mode
Select files fzf style in search mode, without losing the search input.

Also, make background tasks failsafe and measure ui rendering.
3 years ago
Arijit Basu 38812e733b Improve config inheritance for layout UI
With this commit, users will be able to define the common configuration
as super config and inherit from them in each layout.
3 years ago
Arijit Basu 0270fecec9 Read pipes in a safer way.
Do not close the input pipe file after reading. Instead, read it, empty
it, then close it.
3 years ago
Arijit Basu 3aa349f614 Don't refresh pipes on every iteration
From this commit, the app state will be written to the output pipes only
when invoking a command.

For auto refreshing pipes, we can brainstorm on `service`s concept.
3 years ago
Arijit Basu fd92d8ee01 Make modifiers more intuitive
It's difficult to calculate the bits for adding and removing modifiers.
With this commit, we be will use words `Bold`, `Italic` instead of `bits: x`.
3 years ago
Arijit Basu 59b55ee192 Add more UI styling options
With this commit, the following can be done:

- Define layout constraints based on screen size and relative panel
  size.
- Define borders.
- Define panel style.
- Define panel title and title style.
3 years ago
Arijit Basu cfa82cf99f Fix margin overwriting logic 3 years ago
Arijit Basu b8df831248 Add `Nothing` layout config inheritance workaround
Any layouts will default to `Nothing` which resembles blank screen.
However, they will be overwritten when there is a more specific layout
available either in the super config or in the sub config.
3 years ago
Arijit Basu b3e6679b50 Add feature dynamic UI
Now, users can change the UI layout via the `SwitchLayout{Builtin|Custom}`
message, or by using key `ctrl-w`.

There are 3 default layout options -

- default
- no_help
- no_selection
- no_help_no_selection

Also, the initial mode and the initial layout can be specified in the
config.

Closes: https://github.com/sayanarijit/xplr/issues/107
3 years ago
Arijit Basu 474c17b493
Minor fix 3 years ago
Arijit Basu 0584a43c7c Add SwitchModeBuiltin and SwitchModeCustom
Also, use a better prompt symbol.

Ref: https://github.com/sayanarijit/xplr/issues/107
3 years ago
Arijit Basu f2eb8febd5
Add ko-fi payment option 3 years ago
Arijit Basu c0c4f27ef8 Update version 3 years ago
Arijit Basu 2c7ec47253 Fix `cd` on first focus
Fix issue where running `xplr /path/to/file` doesn't `cd` into the
parent directory.
3 years ago
Arijit Basu debe98298d
Fix image link 3 years ago
Arijit Basu 1c967cfef6 Update version 3 years ago
Arijit Basu b57950983c
Optimize icon 3 years ago
Arijit Basu 7ae5e2624a
Improve icon 3 years ago
Arijit Basu 7e7f6f8dd8 Add icons
Also move xplr.desktop to assets/desktop
3 years ago
Arijit Basu 64d81407fa
Improve readme 3 years ago
Arijit Basu 4f28dc4d0a
Add icon 3 years ago
aflying pumpkin c9d62d6061 Fixed indentation
Fixed indentation
3 years ago
aflying pumpkin d0fd1c6bce added ctrl-n and ctrl-p as rebinds for down and up inside of search mode, to improve usability 3 years ago
Arijit Basu 8413b68237
Add community and contribute pages to readme 3 years ago
Arijit Basu 60eb46a3c9
Add demo for more hacks 3 years ago
Arijit Basu 36af3e8ced Fix pipes not updating properly
Let's not optimize things until we have tests.
3 years ago
Arijit Basu 4dad10815a Fix support for filenames starting with - (hiphen)
Make it possible to create, delete, copy, move, rename filenames starting
with a - (hiphen).
3 years ago
Arijit Basu 2437fd67cf Fix distorted screen when opening files in GUI
Fixes: https://github.com/sayanarijit/xplr/issues/111
3 years ago
Arijit Basu 8ddc000895 Make config private 3 years ago
Arijit Basu 2470827aac Organize tests 3 years ago
Arijit Basu f38398e900 Make internals private 3 years ago
Arijit Basu 33e500a16d Move pipe writing logic from runner to app
Ref: https://github.com/sayanarijit/xplr/issues/103
3 years ago
Arijit Basu eca35c73a2 Update version 3 years ago
Arijit Basu d0c5801474 Fix terminal output redirection when piping stdout
Now, when you do `xplr > out.txt` and invoke the terminal, the stdout
won't be redirected.
3 years ago
Arijit Basu 680aeb053c
Add packaging status 3 years ago
Arijit Basu d81111c134 Reduce col_spacing to 1 3 years ago
Arijit Basu 014043d330 Fix formatting the global help menu 3 years ago
Arijit Basu 76a3c22f99 Add test for config incompatibility error
Make sure https://github.com/sayanarijit/xplr/issues/97 hever happens
again.
3 years ago
Arijit Basu e9b5dfe478 Fix incompatible config version
Fixes: https://github.com/sayanarijit/xplr/issues/97
3 years ago
Arijit Basu 36c2c9cc2b
Update version 3 years ago
Arijit Basu 1a08318593 Improve CPU usage attempt 2
Fixes: https://github.com/sayanarijit/xplr/issues/10
3 years ago
Arijit Basu 871a4c79aa
Fix divide by zero error 3 years ago
Arijit Basu 371d572d50
Add discord badge 3 years ago
Arijit Basu 1ffa85f30f Do not exit on permission denial
While trying to enter restricted directories, log error instead or
exiting.
3 years ago
Arijit Basu 2e541fbf04
Minor UI improvement 3 years ago
Arijit Basu ad3dd8eec2
Update version 3 years ago
Arijit Basu 785c20de13 Sync pwd
Sync session's $PWD with `xplr`'s current directory.
If you use alacritty, every window will open in `xplr`'s current
directory.
3 years ago
Arijit Basu c11099f651 Fix follow symlink behavior.
Use `gf` to follow symlinks instead of `enter`/`l`.

Or use the message `FollowSymlink`.
3 years ago
Arijit Basu 87cd6ff015 Fix global help menu not displaying sort & filter 3 years ago
Arijit Basu f8fb6913c9 Rework README.md, Cargo.toml 3 years ago
Arijit Basu 1f99e8ba99 Follow file symlinks
Follow symlink to files to it's parent directory.

Also, hide file size for directories.

Ref: https://github.com/sayanarijit/xplr/issues/84
3 years ago
Arijit Basu 1783834a2e Fix ctrl-l breaking UI
Also improve the filter mode key bindings

Fixes: https://github.com/sayanarijit/xplr/issues/88
3 years ago
Arijit Basu ca13ebb193 Added inode size
Also supports sorting by inode size.

Closes: https://github.com/sayanarijit/xplr/issues/84
3 years ago
Arijit Basu b53f0c21bb Improve read-only mode
Do not include non-read-only actions in the in read-only mode. i.e. do
not display non-read-only actions in help menu.

Ref: https://github.com/sayanarijit/xplr/issues/22
3 years ago
Arijit Basu 21f87d6a08 Add read-only mode
Ref: https://github.com/sayanarijit/xplr/issues/22
3 years ago
Arijit Basu 4d21a89050 Use ctrl-r to refresh, ctrl-l to clear selection 3 years ago
Arijit Basu e70fa57228 Fix selection
Fixes: https://github.com/sayanarijit/xplr/issues/81
3 years ago
Arijit Basu 9fce3b1d14 Fix space input 3 years ago
Arijit Basu 1bb2622f24 Improve key inputs
Add support proper implementations of -

- backspace
- ctrl-w
- ctrl-u

Also, improved sort and filter behavior.

Fixes: https://github.com/sayanarijit/xplr/issues/77
3 years ago
Maxim Baz 468cf7f3df Allow empty icons 3 years ago
Arijit Basu f582f49ad8 Make search and filter case insensitive 3 years ago
Arijit Basu 1546ba0a75 Add filter key binding 3 years ago
Arijit Basu a8896740c8 Add sorting support
Also improve filtering.

Closes: https://github.com/sayanarijit/xplr/issues/58
3 years ago
Arijit Basu 1dc25c4998
Remove `default.nix` 3 years ago
Arijit Basu bbd5c1ba8b
Map V to `ctrl-a` 3 years ago
Arijit Basu 3ab9bcb4c9 Fix UI style priority
Fixes: https://github.com/sayanarijit/xplr/issues/68
3 years ago
Arijit Basu 54bad4aa09 Add mode selection commands
- SelectAll
- SelectPath
- UnSelectAll
- UnSelectPath
- ToggleSelectAll
- ToggleSelectionByPath
3 years ago
Arijit Basu 1dba3d5e8e Fix overwriting of default config.
This fixes the overwriting of default configuration.

Also, add tests to validate the new logic.

Fixes: https://github.com/sayanarijit/xplr/issues/68
3 years ago
Arijit Basu 0c82a645d9 Add history pipe 3 years ago
Arijit Basu d0342260fe Add support for NO_COLOR
Also, add `general.logs` to the config.

Ref: https://no-color.org/
3 years ago
Arijit Basu af1cda5762 Better symlink support
Closes: https://github.com/sayanarijit/xplr/issues/37
3 years ago
Arijit Basu 0d4cd29a08 Fix panic on permission denied
Issue:
$PWD watch service panics when visiting restricted directories.

Fix:
Log error instead of panic.
3 years ago
Arijit Basu ea42b1969a Improve scrolling behaviour
Closes: https://github.com/sayanarijit/xplr/issues/54
3 years ago
Maxim Baz d63d612339 add desktop file 3 years ago
Arijit Basu c640edc4d9
Improve number mode clear behaviour 3 years ago
Arijit Basu 49ffd8e1f1 Fix exit error codes
Also remove cucumber-rust (will try https://github.com/rust-rspec/rspec)

Fixes: https://github.com/sayanarijit/xplr/issues/33
3 years ago
Arijit Basu 55e1a6a0fa Add basic history navigation
Use `ctrl-i` (tab) and `ctrl-o` to navigate history.

Closes: https://github.com/sayanarijit/xplr/issues/49
3 years ago
Arijit Basu f247acf626 Fix remap behaviour and help menu
Remapping a key should overwrite default. Also, remapped keys shouldn't
be redundantly visible in help menu.

Also, display log time.
3 years ago
Arijit Basu 055c1083d6 Support easier key remaps
Also,

- Add key binding `~` to go to homedir.
- Add customizable cursor and prompts.
- Improve the help menus.
3 years ago
Arijit Basu d34dc77ea5 Initial BDD testing setup 3 years ago
Arijit Basu 080e1686f3 Improve version compatibility
From this version, xplr won't annoy the users to visit the upgrade guide
when there is no need.

Also, users will only get upgrade related notification when it is
there is one.
3 years ago
Arijit Basu 3598be0f19 Improve config defaults
- Rename `custom` field for node metadata to `meta`.
- Move `icon` to `meta.icon`.
- Rename `normal_ui` to `default_ui`.
- Rename `filetypes` to `node_types`.
- Split `modes` into `modes.builtin` and `modes.custom`.
- Add the missing `create file` mode.
- Rename `focused_ui` to `focus_ui`.
- Make `general.table.header` non-nullable.
- Add support for incremental configuration updates.

Ref: https://github.com/sayanarijit/xplr/issues/45
3 years ago
Arijit Basu 6aa3df301e Separate config.yml file from rust files
Also be less aggressive for version compatibility.

Use the following logic:

Knowing that we use `{major}.{minor}.{patch}` versioning,

- Major version mismatch are incompatible. Fail with error, suggesting to
  visit the Upgrade Guide.
- Minor version updates and patch fixes are compatible. Suggest user to
  update the config file version manually. Or visit the Upgrade Guide.

- However, if the config file has greater value for minor version
  than the app, also fail with error. Suggesting the user to visit Upgrade
  Guide. Though in this case, the user will be downgrading.

Ref: https://github.com/sayanarijit/xplr/issues/45
3 years ago
Arijit Basu 233f6d44a5
Update version 3 years ago
Arijit Basu 2af1b4c70a
Fix exit screen 3 years ago
Arijit Basu 2596c0c4c3 Remove task priority
Since we are now blocking on task inputs, the priority is no longer
required.
3 years ago
Arijit Basu 5030749ab1
Use `cp -vr` instead of `cp -v` 3 years ago
Arijit Basu 9f78a1fcff Ability to call commands silently
Some commands doesn't require to capture stdout and stderr.
They can be called without needing to reset the screen.

Add `CallSilently` and `BashExecSilently` to execute those commands
faster.

Also, some optimization.
3 years ago
deadjakk be2911e073 Fixed error preventing compilation
Removed comma.

Error was as follows:
error: no rules expected the token `,`
   --> src/input.rs:373:29
    |
373 |                 | Self::Num9,
    |                             ^ no rules expected this token in macro call

error: aborting due to previous error
3 years ago
Arijit Basu 588a50af7e Add support for custom metadata for table row UI
Example:

```yaml
filetypes:
  directory:
    custom:
      foo: bar
     ...
```

Where `foo` and `bar` can be anything string.

It can be accessed with the `{{{custom.foo}}}` variable.
3 years ago
Arijit Basu a68fec0c11
No need to clone selection 3 years ago
Arijit Basu b50ce48264
Revert "Optimize the main thread"
This reverts commit 097c9dd8c5.

Queued tasks might create unexpected issues. We need a test suit first.
3 years ago
Arijit Basu 097c9dd8c5 Optimize the main thread 3 years ago
Arijit Basu b9e9601a71
Fix failed build and update version 3 years ago
Arijit Basu b5986c59d6 Fix logs for being written in the pipe
Don't depend on `Refresh`, always write to the pipes.
3 years ago
Arijit Basu 2b0572228d
Draw once
Draw on the terminal only once i.e. when `Refresh`ing.
3 years ago
Arijit Basu ededb49bd4
Fix the help menu
`esc` no longer exits from the `default` mode.
3 years ago
Maxim Baz 70a3794857 Fix symlink support
canonicalize() and metadata() both resolve symlinks, thus showing symlinks as regular files
3 years ago
Arijit Basu c06a3cb51e
Update version 3 years ago
Arijit Basu 6d0ea06d7b Add pwd watcher
Also optimize the main thread.
3 years ago
Arijit Basu 0a3cf7b5c1
Update version 3 years ago
Arijit Basu 832016bb18 Stay in search mode when entering or leaving a directory
This might be a little counter intuitive to the `nnn` users, but I think
this will add to the productivity and should be the default.

Since we have a real-time mode indicator, users shouldn't face much of
an issue switching to the alternate default.
3 years ago
Arijit Basu 6bc079a3ed
Update README.md 3 years ago
Arijit Basu 1b35ba3d8d
Add backers 3 years ago
Arijit Basu 1e7e2ecc2f Add `:e` for "open in editor" 3 years ago
Arijit Basu fa4d4168cd Don't quit on esc
Issue:
`esc` is generally used to get back to the `default` mode and mistakenly
pressing `esc` while in `default` mode will annoyingly terminate the
session.

Fix:
Remove `esc` from the `default` mode's key bindings. Use `q` or `crtl-c`
instead.
3 years ago
Arijit Basu fa37cd1c10 Improve search and filter
Concern:
Using `ResetNodeFilters` to clear the filters while searching or exiting
from search unexpectedly resets the `show hidden` mode because the
action not only removes the target filter, it resets all the other
filters as well.

Solution:
Implement `RemoveNodeFilterFromInput` to be able to clear or remove
target filters without having to reset it.
3 years ago
Arijit Basu e2b49ab4fd Display help menu using $PAGER
Also, use `open` to open files when `xdg-open` is missing.
3 years ago
Arijit Basu 45a95a792d Clear screen before exit 3 years ago
Arijit Basu 91a319fc80
Update version 3 years ago
Arijit Basu 57494f8ebf Do not print command outputs to the main screen.
Print all the outputs of xplr commands on the alternate screen. Not on
the real stdout.
3 years ago
Arijit Basu 7661019fd9 Make dir colors cyan
Fixes: https://github.com/sayanarijit/xplr/issues/11
3 years ago
Arijit Basu 23b51cf8fe
Fix failed build and re-publish 3 years ago
Arijit Basu 2bd2b743fb
Fix renaming 3 years ago
Arijit Basu a484c2fd39
Update default.nix 3 years ago
Arijit Basu e34755b11b Update version
Also fix nix hash
3 years ago
Arijit Basu 09abda29a3 Several optimizations
- Write to pipes only when the value changes.
- Sleep when not reading key event or messages.

Fixes: https://github.com/sayanarijit/xplr/issues/10
3 years ago
Arijit Basu 65ddb0ee4b
Release 0.3.2 3 years ago
Arijit Basu 53fd92b680 Fix debug formatting 3 years ago
Arijit Basu 65b7412579 Add XPLR_APP_VERSION and XPLR_CONFIG_VERSION
Since users don't need to update config file version for minor app
releases, we have to differentiate between app and config version.

Also, expose them via `$XPLR_CONFIG_VERSION` and `$XPLR_APP_VERSION`.
3 years ago
Arijit Basu 0272284f40
Fix print formatting 3 years ago
Quentin Guilloteau a9309a52c0 add comment to remind to update the version in the default.nix file 3 years ago
Quentin Guilloteau 42ca66c0fd use release tarball 3 years ago
Quentin Guilloteau 73a492bd3a add default.nix 3 years ago
Christian Paul e034827f01 Update README.md: it's -> its 3 years ago
Arijit Basu 6dff9dbe65
Add donation button 3 years ago
Arijit Basu 175699ac63
Expose `$XPLR_PIPE_MODE_OUT`
Also, add new line character to `$XPLR_PIPE_MODE_OUT` and
`$XPLR_FOCUS_PATH` for consistency.
3 years ago
Arijit Basu eeee3394bc Improve version incompatibility error
With this change, `xplr` will only raise version incompatibility error
if the major version changes. Minor version updates are assumed to be
backwards compatible.

If the major version is `v0`, the minor version will be considered as
the major version and the security/patch version will be considered as
minor version and the same logic will apply.
3 years ago
Arijit Basu 981ead8c36 Fix large env vars breaking the subprocess
Bug:
When you call some command in a directory with a large number of hosts,
xplr will fail setting the environment vars as the command will become
too large to handle.

Fix:
Port the value of multi-line variables from env vars to pipes and set
the name of the pipe as env var instead. And deprecate the variables
that doesn't make much sense.

In other words,

- `$XPLR_APP_YAML` has been removed.
- `$XPLR_RESULT` has been ported to `$XPLR_PIPE_RESULT_OUT`.
- `$XPLR_GLOBAL_HELP_MENU` has been ported to
  `$XPLR_PIPE_GLOBAL_HELP_MENU_OUT`.
- `$XPLR_DIRECTORY_NODES` has been ported to
  `$XPLR_PIPE_DIRECTORY_NODES_OUT`.
- `$XPLR_LOGS` has been ported to `$XPLR_PIPE_LOGS_OUT`.
- `$XPLR_PIPE_RESULT` has been ported to `$XPLR_PIPE_RESULT_OUT`.

Hence, instead of `<<< $VAR`, `< $VAR_PIPE_OUT` should be used.
3 years ago
Arijit Basu 9747c8667b Autocomplete the relative path while renaming
By default, while renaming a file, autocomplete the relative path i.e.
the filename instead of the absolute path in the input buffer.
3 years ago

@ -0,0 +1,14 @@
# Why dynamic linking?
# See https://github.com/sayanarijit/xplr/issues/309
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
[target.aarch64-unknown-linux-gnu]
rustflags = ["-C", "linker=aarch64-linux-gnu-gcc", "-C", "link-args=-rdynamic"]
[target.aarch64-linux-android]
rustflags = ["-C", "linker=aarch64-linux-android-clang", "-C", "link-args=-rdynamic", "-C", "default-linker-libraries"]
[target.arm-unknown-linux-gnueabihf]
rustflags = ["-C", "linker=arm-linux-gnueabihf-gcc", "-C", "link-args=-rdynamic"]

@ -0,0 +1,6 @@
ratatui
crate
ser
enque
noice
ans

@ -0,0 +1 @@
use flake

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [ sayanarijit ]
patreon: # Replace with a single Patreon username
open_collective: xplr
ko_fi: sayanarijit
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: sayanarijit
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

@ -0,0 +1,35 @@
name: Book
on:
push:
branches:
- main
paths:
- 'docs/**'
workflow_dispatch:
jobs:
deploy_en:
name: Deploy book on gh-pages
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# - name: Install mdBook
# uses: peaceiris/actions-mdbook@v1
- name: Render book
run: |
# From cloudflare pages
curl -L https://github.com/rust-lang/mdBook/releases/download/v0.4.15/mdbook-v0.4.15-x86_64-unknown-linux-gnu.tar.gz -o mdbook.tgz && tar xzvf mdbook.tgz && ./mdbook build docs/en && mkdir dist && mv -v docs/en/book/html dist/en && mv -v assets dist && mv -v docs/landing/index.html docs/landing/css docs/landing/js dist
mv docs/CNAME dist
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
allow_empty_commit: true
keep_files: false
publish_dir: dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PUBLISH_BRANCH: gh-pages
PUBLISH_DIR: dist

@ -12,67 +12,138 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- macos-latest
- ubuntu-latest
# - windows-latest
build:
- macos
- macos-aarch64
- linux
- linux-musl
- linux-aarch64
- linux-arm
rust: [stable]
include:
- os: macos-latest
artifact_prefix: macos
# See the list: https://github.com/cross-rs/cross
- build: macos
os: macos-latest
target: x86_64-apple-darwin
binary_postfix: ""
- os: ubuntu-latest
artifact_prefix: linux
- build: macos-aarch64
os: macos-latest
target: aarch64-apple-darwin
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
binary_postfix: ""
# - os: windows-latest
# artifact_prefix: windows
# target: x86_64-pc-windows-msvc
# binary_postfix: ".exe"
- build: linux-musl
os: ubuntu-latest
target: x86_64-unknown-linux-musl
- build: linux-aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- build: linux-arm
os: ubuntu-latest
target: arm-unknown-linux-gnueabihf
steps:
- uses: actions/checkout@v3
- name: Installing Rust toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
override: true
target: ${{ matrix.target }}
- name: Installing needed macOS dependencies
if: matrix.os == 'macos-latest'
run: brew install openssl@1.1
- name: Installing needed Ubuntu dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
- name: Checking out sources
uses: actions/checkout@v1
sudo apt-get update --fix-missing
sudo apt-get install -y --no-install-recommends liblua5.1-0-dev libluajit-5.1-dev gcc pkg-config curl git make ca-certificates
sudo apt-get install -y snapd
# sudo snap install snapcraft --classic
# sudo snap install multipass --classic --beta
- if: matrix.build == 'linux-musl'
run: sudo apt-get install -y musl-tools
- if: matrix.build == 'linux-aarch64'
run: sudo apt-get install -y gcc-aarch64-linux-gnu
- if: matrix.build == 'linux-arm'
run: |
sudo apt-get install -y gcc-multilib
sudo apt-get install -y gcc-arm-linux-gnueabihf
sudo ln -s /usr/include/asm-generic/ /usr/include/asm
- name: Running cargo build
uses: actions-rs/cargo@v1
with:
command: build
toolchain: ${{ matrix.rust }}
args: --release --target ${{ matrix.target }}
run: cargo build --locked --release --target ${{ matrix.target }}
# - name: Running snapcraft build
# run: |
# snapcraft
# printf ' [ INFO ] generated <snapcraft> files include:\n'
# command ls -Al | grep "\.snap" | awk '{ print $9 }'
# mv ./*.snap ./xplr.snap
- name: Install gpg secret key
run: |
cat <(echo -e "${{ secrets.GPG_SECRET }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Packaging final binary
shell: bash
run: |
cd target/${{ matrix.target }}/release
BINARY_NAME=xplr${{ matrix.binary_postfix }}
strip $BINARY_NAME
RELEASE_NAME=xplr-${{ matrix.artifact_prefix }}
BINARY_NAME=xplr
RELEASE_NAME=$BINARY_NAME-${{ matrix.build }}
tar czvf $RELEASE_NAME.tar.gz $BINARY_NAME
if [[ ${{ runner.os }} == 'Windows' ]]; then
certutil -hashfile $RELEASE_NAME.tar.gz sha256 | grep -E [A-Fa-f0-9]{64} > $RELEASE_NAME.sha256
else
shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256
fi
shasum -a 256 $RELEASE_NAME.tar.gz > $RELEASE_NAME.sha256
cat <(echo "${{ secrets.GPG_PASS }}") | gpg --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor $RELEASE_NAME.tar.gz
- name: Releasing assets
uses: softprops/action-gh-release@v1
with:
files: |
target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.tar.gz
target/${{ matrix.target }}/release/xplr-${{ matrix.artifact_prefix }}.sha256
target/${{ matrix.target }}/release/xplr-${{ matrix.build }}.tar.gz
target/${{ matrix.target }}/release/xplr-${{ matrix.build }}.sha256
target/${{ matrix.target }}/release/xplr-${{ matrix.build }}.tar.gz.asc
xplr.snap
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Cleaning snapcraft
# run: |
# command rm --verbose ./*.snap
# snapcraft clean
publish-gpg-signature:
name: Publishing GPG signature
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install gpg secret key
run: |
cat <(echo -e "${{ secrets.GPG_SECRET }}") | gpg --batch --import
gpg --list-secret-keys --keyid-format LONG
- name: Signing archive with GPG
run: |
VERSION=${GITHUB_REF##*v}
git -c tar.tar.gz.command='gzip -cn' archive -o xplr-${VERSION:?}.tar.gz --format tar.gz --prefix "xplr-${VERSION:?}/" "v${VERSION}"
cat <(echo "${{ secrets.GPG_PASS }}") | gpg --pinentry-mode loopback --passphrase-fd 0 --detach-sign --armor "xplr-${VERSION:?}.tar.gz"
mv "xplr-${VERSION:?}.tar.gz.asc" "source.tar.gz.asc"
- name: Releasing GPG signature
uses: softprops/action-gh-release@v1
with:
files: |
source.tar.gz.asc
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@ -80,15 +151,16 @@ jobs:
name: Publishing to Cargo
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
override: true
- run: |
sudo apt-get update
sudo apt-get update --fix-missing
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
- uses: actions-rs/cargo@v1
with:
command: publish
args: --token ${{ secrets.CARGO_API_KEY }} --allow-dirty
- run: cargo publish --allow-dirty
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_KEY }}

@ -1,104 +1,144 @@
name: Continuous Integration
on:
pull_request:
push:
branches: master
branches: main
workflow_dispatch:
name: Continuous Integration
jobs:
# Workaround for making Github Actions skip based on commit message `[skip ci]`
# Source https://gist.github.com/ybiquitous/c80f15c18319c63cae8447a3be341267
prepare:
runs-on: ubuntu-latest
if: |
!contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title, github.event.pull_request.body), '[skip ci]')
steps:
- run: |
cat <<'MESSAGE'
github.event_name: ${{ toJson(github.event_name) }}
github.event:
${{ toJson(github.event) }}
MESSAGE
check:
name: Check
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
- uses: actions-rs/cargo@v1
with:
command: check
test:
name: Test Suite
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
# These dependencies are required for `clipboard`
- run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
- uses: actions-rs/cargo@v1
with:
command: test
# bench:
# name: Benchmarks
# runs-on: ubuntu-latest
# needs: prepare
# steps:
# - uses: actions/checkout@master
# - uses: actions-rs/toolchain@v1
# with:
# toolchain: stable
# profile: minimal
# override: true
# # These dependencies are required for `clipboard`
# - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
# - uses: actions-rs/cargo@v1
# with:
# command: bench
- run: cargo check
fmt:
name: Rustfmt
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- run: cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
components: clippy
- uses: actions-rs/cargo@v1
- run: cargo clippy -- -D warnings
spellcheck:
name: Spellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: codespell-project/actions-codespell@v1
with:
command: clippy
args: -- -D warnings
ignore_words_file: .codespellignore
test:
name: Test Suite
runs-on: ${{ matrix.os }}
needs:
- check
- fmt
- clippy
- spellcheck
strategy:
matrix:
build:
- macos
- macos-aarch64
- linux
- linux-musl
- linux-aarch64
- linux-arm
rust: [stable]
include:
# See the list: https://github.com/cross-rs/cross
- build: macos
os: macos-latest
target: x86_64-apple-darwin
- build: macos-aarch64
os: macos-latest
target: aarch64-apple-darwin
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- build: linux-musl
os: ubuntu-latest
target: x86_64-unknown-linux-musl
- build: linux-aarch64
os: ubuntu-latest
target: aarch64-unknown-linux-gnu
- build: linux-arm
os: ubuntu-latest
target: arm-unknown-linux-gnueabihf
env:
RUST_BACKTRACE: full
steps:
- uses: actions/checkout@v3
- name: Installing Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
- name: Installing needed macOS dependencies
if: matrix.os == 'macos-latest'
run: brew install openssl@1.1
- name: Installing needed Ubuntu dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y --no-install-recommends liblua5.1-0-dev libluajit-5.1-dev gcc pkg-config curl git make ca-certificates
- if: matrix.build == 'linux-musl'
run: sudo apt-get install -y musl-tools
- if: matrix.build == 'linux-aarch64'
run: sudo apt-get install -y gcc-aarch64-linux-gnu
- if: matrix.build == 'linux-arm'
run: |
sudo apt-get install -y gcc-multilib
sudo apt-get install -y gcc-arm-linux-gnueabihf
sudo ln -s /usr/include/asm-generic/ /usr/include/asm
- run: cargo build --target ${{ matrix.target }}
- if: matrix.build == 'macos' || matrix.build == 'linux'
run: cargo test --target ${{ matrix.target }}
# bench:
# name: Benchmarks
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: dtolnay/rust-toolchain@stable
# with:
# toolchain: stable
# # These dependencies are required for `clipboard`
# - run: sudo apt-get install -y -qq libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev
# - run: cargo bench

@ -0,0 +1,23 @@
name: "Push Binary Cache for Nix"
on:
pull_request:
push:
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: nixbuild/nix-quick-install-action@v19
with:
nix_conf: experimental-features = nix-command flakes
- uses: cachix/cachix-action@v11
with:
name: xplr
authtoken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- run: nix profile install .
- name: Run tests
run: |
xplr --version

24
.gitignore vendored

@ -1 +1,25 @@
/target
/flamegraph.svg
/perf.data
/perf.data.old
.vscode/
book/
# Vi[m] backups
*.swp
*~
# Jetbrains config
.idea/
.venv/
# direnv
.direnv/
# nix
result
# test files
/init.lua

@ -5,7 +5,7 @@
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
- Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within

@ -0,0 +1,39 @@
If you are new to GitHub, visit the [first-contributions instructions](https://github.com/firstcontributions/first-contributions/blob/master/README.md) to learn how to contribute on GitHub.
If you are new to Rust, I recommend you to go through [the book](https://doc.rust-lang.org/book).
To find issues you can help with, go though the list of [good first issues](https://github.com/sayanarijit/xplr/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) or issues labeled with [help wanted](https://github.com/sayanarijit/xplr/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22).
If you have something to suggest or issues to report, you can [create a new issue](https://github.com/sayanarijit/xplr/issues/new). Be sure to explain in details the context and the outcome that you are lookign for. If reporting bugs, provide basic information like you OS version, `xplr` version, window manager and the terminal you are using.
Once found or created an issue, let us know that you want to work on it by commenting in the issue.
Development Guideline
---------------------
Assuming that you have mentioned the issue you are working on and that you have forked and cloned the repository locally, in order to contribute by making changes to the code follow the steps below:
- Make changes to the code
- Test the changes
```bash
cargo run
cargo test
```
- Format code and get linting helps
```bash
cargo fmt
cargo clippy
```
- Commit, push and finally create a pull request.
- Don't worry if you make a mistake, we will provide constructive feedback and guidance to improve the pull request.
- If you encounter any situation that violates our [code of conduct](https://github.com/sayanarijit/xplr/blob/main/CODE_OF_CONDUCT.md) please report it to sayanarijit@gmail.com.

1793
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -1,31 +1,94 @@
[[bin]]
name = 'xplr'
[[bench]]
name = 'criterion'
harness = false
path = './benches/criterion.rs'
[package]
name = "xplr"
version = "0.2.20" # Update app.rs
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
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"
homepage = "https://github.com/sayanarijit/xplr"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
name = 'xplr'
version = '0.21.9'
authors = ['Arijit Basu <hi@arijitbasu.in>']
edition = '2021'
description = 'A hackable, minimal, fast TUI file explorer'
license = 'MIT'
readme = 'README.md'
repository = 'https://github.com/sayanarijit/xplr'
homepage = 'https://xplr.dev'
documentation = 'https://xplr.dev/en'
keywords = ['terminal', 'file', 'explorer', 'manager', 'tui']
categories = ['command-line-interface', 'command-line-utilities']
include = ['src/**/*', 'docs/en/src/**/*', 'LICENSE', 'README.md']
[dependencies]
tui = { version = "0.14", default-features = false, features = ['crossterm', 'serde'] }
termion = "1.5"
crossterm = "0.18"
dirs = "3.0.1"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
handlebars = "3.5"
mime_guess = "2.0.3"
anyhow = "1.0"
chrono = { version = "0.4", features = ["serde"] }
libc = "0.2.155"
humansize = "2.1.3"
natord = "1.0.9"
anyhow = "1.0.86"
serde_yaml = "0.9.34"
crossterm = { version = "0.27.0", features = [], default-features = false }
ansi-to-tui = "=3.1.0"
regex = "1.10.5"
gethostname = "0.4.3"
serde_json = "1.0.117"
path-absolutize = "3.1.1"
which = "6.0.1"
nu-ansi-term = "0.50.0"
textwrap = "0.16"
snailquote = "0.3.1"
skim = { version = "0.10.4", default-features = false }
time = { version = "0.3.36", features = ["serde", "local-offset", "formatting", "macros"] }
jf = "0.6.2"
xdg = "2.5.2"
home = "0.5.9"
[dependencies.lscolors]
version = "0.17.0"
default-features = false
features = ["nu-ansi-term"]
[dependencies.lazy_static]
version = "1.4.0"
default-features = false
[dependencies.mime_guess]
version = "2.0.4"
default-features = false
[dependencies.tui]
version = "=0.26.1" # https://github.com/ratatui-org/ratatui/issues/1032
default-features = false
features = ['crossterm', 'serde']
package = 'ratatui'
[dependencies.serde]
version = "1.0.203"
features = []
default-features = false
[dependencies.indexmap]
version = "2.2.6"
features = ['serde']
[dependencies.mlua]
version = "0.9.8"
features = ['luajit', 'serialize', 'send']
[dependencies.tui-input]
version = "0.8.0"
features = ['serde']
[dev-dependencies]
criterion = "0.3"
criterion = "0.5.1"
assert_cmd = "2.0.14"
[[bench]]
name = "navigation"
harness = false
[profile.release]
lto = true
codegen-units = 1
panic = 'abort'
strip = true
[features]
default = ["vendored-lua"]
vendored-lua = ["mlua/vendored"]

@ -1,53 +1,65 @@
<h1 align="center">xplr</h1>
<h1 align="center">
▸[<a href="https://github.com/sayanarijit/xplr/blob/main/assets/icon/xplr.svg"><img src="https://s3.gifyu.com/images/xplr32.png" alt="▓▓" height="20" width="20" /></a> xplr]
</h1>
<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>.
A hackable, minimal, fast TUI file explorer
</p>
<p align="center">
<a href="https://crates.io/crates/xplr" target="_blank">
<a href="https://crates.io/crates/xplr">
<img src="https://img.shields.io/crates/v/xplr.svg" />
</a>
<a href="https://github.com/sayanarijit/xplr/actions/workflows/ci.yml" target="_blank">
<img src="https://github.com/sayanarijit/xplr/actions/workflows/ci.yml/badge.svg" />
</a>
<a href="https://github.com/sayanarijit/xplr/actions/workflows/cd.yml" target="_blank">
<img src="https://github.com/sayanarijit/xplr/actions/workflows/cd.yml/badge.svg" />
</a>
</p>
<p align="center">
<a href="https://asciinema.org/a/3THQPXNVi801Yu8nWxO6qfUa4" target="_blank">
<img src="https://s4.gifyu.com/images/xplr.gif" />
</a>
https://user-images.githubusercontent.com/11632726/166747867-8a4573f2-cb2f-43a6-a23d-c99fc30c6594.mp4
</p>
<h3 align="center">
[<a href="https://github.com/sayanarijit/xplr/wiki/Quickstart">Quickstart</a>]
[<a href="https://github.com/sayanarijit/xplr/wiki/Features">Features</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/Upgrade-Guide">Upgrade Guide</a>]
[<a href="https://github.com/sayanarijit/xplr/wiki/TODO">TODO</a>]
[<a href="https://xplr.dev/en/install">Install</a>]
[<a href="https://xplr.dev/en">Documentation</a>]
[<a href="https://xplr.dev/en/awesome-hacks">Hacks</a>]
[<a href="https://xplr.dev/en/awesome-plugins">Plugins</a>]
[<a href="https://xplr.dev/en/awesome-integrations">Integrations</a>]
</h3>
<br>
xplr is a terminal UI based file explorer that aims to increase our terminal
productivity by being a flexible, interactive orchestrator for the ever growing
awesome command-line utilities that work with the file-system.
Though [xplr](https://github.com/sayanarijit/xplr) strives to be fast and minimalist, it's speciality is it's hackability.
To achieve its goal, xplr strives to be a fast, minimal and more importantly,
hackable file explorer.
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.
xplr is not meant to be a replacement for the standard shell commands or the
GUI file managers. Rather, it aims to [integrate them all][14] and expose an
intuitive, scriptable, [keyboard controlled][2],
[real-time visual interface][1], also being an ideal candidate for [further
integration][15], enabling you to achieve insane terminal productivity.
<p align="center">
## Introductions & Reviews
<a href="https://asciinema.org/a/404815" target="_blank">
<img src="https://s4.gifyu.com/images/xplr9d8b7b05c6bc39ae.gif" />
</a>
- [[VIDEO] XPLR: Insanely Hackable Lua File Manager ~ Brodie Robertson](https://youtu.be/MaVRtYh1IRU)
</p>
- [[Article] What is a TUI file explorer & why would you need one? ~ xplr.stck.me](https://xplr.stck.me/post/25252/What-is-a-TUI-file-explorer-why-would-you-need-one)
- [[Article] FOSSPicks - Linux Magazine](<https://www.linux-magazine.com/Issues/2022/258/FOSSPicks/(offset)/6>)
## Packaging
Package maintainers please refer to the [RELEASE.md](./RELEASE.md).
<a href="https://repology.org/project/xplr/versions"><img src="https://repology.org/badge/vertical-allrepos/xplr.svg" /></a>
## Backers
<a href="https://opencollective.com/xplr#backer"><img src="https://opencollective.com/xplr/tiers/backer.svg?width=890" /></a>
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).
[1]: https://xplr.dev/en/layouts
[2]: https://xplr.dev/en/configure-key-bindings
[14]: https://xplr.dev/en/awesome-plugins#integration
[15]: https://xplr.dev/en/awesome-integrations

@ -0,0 +1,17 @@
# Build
See [install.md](./docs/en/src/install.md#build-from-source)
Note: xplr ships with vendored luajit. If the platform can't compile this,
you need to compile using `--no-default-features` argument to avoid using
vendored luajit, so that you can static link luajit yourself.
# Release
The final binary `target/release/xplr` can be shipped with the following assets
- [License](./LICENSE)
- [Desktop Entry](./assets/desktop/xplr.desktop)
- [Desktop Icons](./assets/icon/)
- [Offline Docs](./docs/en/src)
- [Lua Configuration Example](./src/init.lua)

@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Name=xplr
Comment=Terminal file explorer
Exec=xplr
Terminal=true
Icon=xplr
MimeType=inode/directory
Categories=System;FileTools;FileManager;ConsoleOnly
Keywords=File;Manager;Management;Explorer;Launcher

@ -0,0 +1,11 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="128" height="128" rx="18" fill="#272727"/>
<rect x="15" y="19" width="44" height="39" rx="5" fill="#37474F"/>
<rect x="71" y="19" width="44" height="39" rx="5" fill="#37474F"/>
<rect x="15" y="73" width="44" height="39" rx="5" fill="#37474F"/>
<rect x="71" y="73" width="44" height="39" rx="5" fill="#37474F"/>
<path d="M46.7754 29.3711L39.627 39.1562L46.7754 49H41.6484L36.9023 42.2324L32.2734 49H27.293L34.4121 39.2441L27.293 29.3711H32.4199L37.166 36.3438L41.6484 29.3711H46.7754Z" fill="white"/>
<path d="M94.8984 83.3711C95.8164 83.3711 96.7148 83.498 97.5938 83.752C98.4727 84.0059 99.2637 84.3965 99.9668 84.9238C100.689 85.4512 101.256 86.1152 101.666 86.916C102.096 87.6973 102.311 88.6348 102.311 89.7285C102.311 90.8809 102.018 91.877 101.432 92.7168C100.865 93.5371 100.113 94.1816 99.1758 94.6504C98.2383 95.0996 97.2227 95.3633 96.1289 95.4414L103.189 103H97.8281L91.002 94.9727V103H86.9297V83.3711H94.8984ZM91.002 87.1797V92.1309C91.4707 92.2871 91.9883 92.4141 92.5547 92.5117C93.1406 92.5898 93.7266 92.6289 94.3125 92.6289C95.4844 92.6289 96.4121 92.3945 97.0957 91.9258C97.7988 91.4375 98.1504 90.7539 98.1504 89.875C98.1504 88.918 97.7793 88.2344 97.0371 87.8242C96.3145 87.3945 95.4258 87.1797 94.3711 87.1797H91.002Z" fill="white"/>
<path d="M94.8984 29.3711C95.8164 29.3711 96.7148 29.5273 97.5938 29.8398C98.4727 30.1523 99.2637 30.6113 99.9668 31.2168C100.689 31.8223 101.256 32.5547 101.666 33.4141C102.096 34.2539 102.311 35.2207 102.311 36.3145C102.311 37.6426 101.969 38.8242 101.285 39.8594C100.621 40.875 99.7324 41.6758 98.6191 42.2617C97.5059 42.8477 96.2949 43.1406 94.9863 43.1406C94.1074 43.1406 93.3164 43.0527 92.6133 42.877C91.9102 42.7012 91.3242 42.4961 90.8555 42.2617V49H86.7832V29.3711H94.8984ZM90.8555 33.1797V38.9805C91.2852 39.1562 91.8027 39.3125 92.4082 39.4492C93.0332 39.5664 93.668 39.625 94.3125 39.625C95.4844 39.625 96.4121 39.3027 97.0957 38.6582C97.7988 37.9941 98.1504 37.2129 98.1504 36.3145C98.1504 35.2793 97.7598 34.498 96.9785 33.9707C96.1973 33.4434 95.2793 33.1797 94.2246 33.1797H90.8555Z" fill="white"/>
<path d="M34.002 82.3711V98.3379H43.8164V102H29.9297V82.3711H34.002Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

@ -0,0 +1,137 @@
use crate::app;
use crate::ui;
use criterion::{criterion_group, criterion_main, Criterion};
use crossterm::execute;
use crossterm::terminal as term;
use std::fs;
use tui::backend::CrosstermBackend;
use tui::Terminal;
use xplr::runner::get_tty;
use xplr::*;
const PWD: &str = "/tmp/xplr_bench";
fn navigation_benchmark(c: &mut Criterion) {
fs::create_dir_all(PWD).unwrap();
(1..10000).for_each(|i| {
fs::File::create(std::path::Path::new(PWD).join(i.to_string())).unwrap();
});
let lua = mlua::Lua::new();
let mut app =
app::App::create("xplr".into(), None, PWD.into(), &lua, None, [].into())
.expect("failed to create app");
app = app
.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::ChangeDirectory(PWD.into())),
None,
))
.unwrap();
c.bench_function("focus next item", |b| {
b.iter(|| {
app.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::FocusNext),
None,
))
.unwrap()
})
});
c.bench_function("focus previous item", |b| {
b.iter(|| {
app.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::FocusPrevious),
None,
))
.unwrap()
})
});
c.bench_function("focus first item", |b| {
b.iter(|| {
app.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::FocusFirst),
None,
))
.unwrap()
})
});
c.bench_function("focus last item", |b| {
b.iter(|| {
app.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::FocusLast),
None,
))
.unwrap()
})
});
c.bench_function("leave and enter directory", |b| {
b.iter(|| {
app.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::Back),
None,
))
.unwrap()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::Enter),
None,
))
.unwrap()
})
});
}
fn draw_benchmark(c: &mut Criterion) {
fs::create_dir_all(PWD).unwrap();
(1..10000).for_each(|i| {
fs::File::create(std::path::Path::new(PWD).join(i.to_string())).unwrap();
});
let lua = mlua::Lua::new();
let mut ui = ui::UI::new(&lua);
let mut app =
app::App::create("xplr".into(), None, PWD.into(), &lua, None, [].into())
.expect("failed to create app");
app = app
.clone()
.handle_task(app::Task::new(
app::MsgIn::External(app::ExternalMsg::ChangeDirectory(PWD.into())),
None,
))
.unwrap();
term::enable_raw_mode().unwrap();
let mut stdout = get_tty().unwrap();
// let mut stdout = stdout.lock();
execute!(stdout, term::EnterAlternateScreen).unwrap();
// let stdout = MouseTerminal::from(stdout);
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend).unwrap();
terminal.hide_cursor().unwrap();
c.bench_function("draw on terminal", |b| {
b.iter(|| {
terminal.draw(|f| ui.draw(f, &app)).unwrap();
})
});
terminal.clear().unwrap();
terminal.set_cursor(0, 0).unwrap();
execute!(terminal.backend_mut(), term::LeaveAlternateScreen).unwrap();
term::disable_raw_mode().unwrap();
terminal.show_cursor().unwrap();
}
criterion_group!(benches, navigation_benchmark, draw_benchmark);
criterion_main!(benches);

@ -1,94 +0,0 @@
use criterion::{criterion_group, criterion_main, Criterion};
use std::fs;
use xplr::*;
fn criterion_benchmark(c: &mut Criterion) {
fs::create_dir_all("/tmp/xplr_bench").unwrap();
(1..10000).for_each(|i| {
fs::File::create(format!("/tmp/xplr_bench/{}", i)).unwrap();
});
let app = app::App::create("/tmp/xplr_bench".into())
.expect("failed to create app")
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::ChangeDirectory("/tmp/xplr_bench".into())),
None,
))
.possibly_mutate()
.unwrap();
c.bench_function("focus next item", |b| {
b.iter(|| {
app.clone()
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::FocusNext),
None,
))
.possibly_mutate()
.unwrap()
})
});
c.bench_function("focus previous item", |b| {
b.iter(|| {
app.clone()
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::FocusPrevious),
None,
))
.possibly_mutate()
.unwrap()
})
});
c.bench_function("focus first item", |b| {
b.iter(|| {
app.clone()
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::FocusFirst),
None,
))
.possibly_mutate()
.unwrap()
})
});
c.bench_function("focus last item", |b| {
b.iter(|| {
app.clone()
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::FocusLast),
None,
))
.possibly_mutate()
.unwrap()
})
});
c.bench_function("leave and enter directory", |b| {
b.iter(|| {
app.clone()
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::Back),
None,
))
.possibly_mutate()
.unwrap()
.enqueue(app::Task::new(
1,
app::MsgIn::External(app::ExternalMsg::Enter),
None,
))
.possibly_mutate()
.unwrap()
})
});
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);

@ -0,0 +1,10 @@
(import
(
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
sha256 = lock.nodes.flake-compat.locked.narHash;
}
)
{ src = ./.; }
).defaultNix

@ -0,0 +1 @@
xplr.dev

@ -0,0 +1,16 @@
[book]
authors = ['Arijit Basu <hi@arijitbasu.in>']
title = 'xplr book'
description = 'A hackable, minimal, fast TUI file explorer'
src = 'src'
language = 'en'
[output.html]
site-url = '/xplr/en/'
git-repository-url = 'https://github.com/sayanarijit/xplr'
edit-url-template = 'https://github.com/sayanarijit/xplr/edit/main/docs/en/{path}'
default-theme = 'dark'
preferred-dark-theme = 'coal'
[output.linkcheck]
optional = true

@ -0,0 +1,81 @@
# A hackable, minimal, fast TUI file explorer
- [Introduction][1]
- [Quickstart][2]
- [Install][3]
- [Post Install][4]
- [Configuration][5]
- [General][6]
- [Node Types][10]
- [Layouts][9]
- [Modes][7]
- [Concept][32]
- [Sum Type][42]
- [Key Bindings][27]
- [Configure Key Bindings][28]
- [Default Key Bindings][14]
- [Debug Key Bindings][29]
- [Node Type][33]
- [Layout][34]
- [Mode][35]
- [Message][8]
- [Full List of Messages][38]
- [Input Operation][39]
- [Borders][31]
- [Style][11]
- [Searching][41]
- [Sorting][12]
- [Filtering][13]
- [Column Renderer][26]
- [Lua Function Calls][36]
- [xplr.util][40]
- [Environment Variables and Pipes][37]
- [Awesome Hacks][30]
- [Plugin][15]
- [Installing Plugins][16]
- [Writing Plugins][17]
- [Awesome Plugins][18]
- [Integration][19]
- [Awesome Integrations][20]
- [Alternatives][22]
- [Upgrade Guide][23]
[1]: introduction.md
[2]: quickstart.md
[3]: install.md
[4]: post-install.md
[5]: configuration.md
[6]: general-config.md
[7]: modes.md
[8]: message.md
[9]: layouts.md
[10]: node_types.md
[11]: style.md
[12]: sorting.md
[13]: filtering.md
[14]: default-key-bindings.md
[15]: plugin.md
[16]: installing-plugins.md
[17]: writing-plugins.md
[18]: awesome-plugins.md
[19]: integration.md
[20]: awesome-integrations.md
[22]: alternatives.md
[23]: upgrade-guide.md
[26]: column-renderer.md
[27]: key-bindings.md
[28]: configure-key-bindings.md
[29]: debug-key-bindings.md
[30]: awesome-hacks.md
[31]: borders.md
[32]: concept.md
[33]: node-type.md
[34]: layout.md
[35]: mode.md
[36]: lua-function-calls.md
[37]: environment-variables-and-pipes.md
[38]: messages.md
[39]: input-operation.md
[40]: xplr.util.md
[41]: searching.md
[42]: sum-type.md

@ -0,0 +1,36 @@
# Alternatives
These are the alternative TUI/CLI file managers/explorers you might want to check out (in no particular order).
- [nnn][1]
- [vifm][2]
- [ranger][3]
- [lf][4]
- [joshuto][5]
- [fff][6]
- [mc][7]
- [broot][8]
- [hunter][9]
- [noice][10]
- [clifm][11]
- [clifm][12] (non curses)
- [felix][14]
- [yazi][15]
[add more][13]
[1]: https://github.com/jarun/nnn/
[2]: https://github.com/vifm/vifm
[3]: https://github.com/ranger/ranger
[4]: https://github.com/gokcehan/lf
[5]: https://github.com/kamiyaa/joshuto
[6]: https://github.com/dylanaraps/fff
[7]: https://github.com/MidnightCommander/mc
[8]: https://github.com/Canop/broot
[9]: https://github.com/rabite0/hunter
[10]: https://git.2f30.org/noice/
[11]: https://github.com/pasqu4le/clifm
[12]: https://github.com/leo-arch/clifm
[13]: https://github.com/sayanarijit/xplr/edit/dev/docs/en/src/alternatives.md
[14]: https://github.com/kyoheiu/felix
[15]: https://github.com/sxyazi/yazi

@ -0,0 +1,542 @@
# Awesome Hacks
Here's a list of cool xplr hacks, i.e. snippets of code that you can just copy
and paste into your [configuration][1] or the appropriate file, that are
too small or too niche for a full fledge [plugin][2].
Do you have something cool to share?
[Edit this file][3] or [share them here][4].
You can try these hacks by writing them to a file, say `hack.lua` and passing
it to xplr with `--extra-config` or `-C`.
```bash
xplr -C hack.lua
```
### cd on quit
Change directory using xplr.
<details>
<summary>Expand for details</summary>
- Author: [@sayanarijit][8]
- Tested on: Linux
NOTE: This is a shell hack, rather than Lua config hack. Add this in
`.bashrc` or `.profile` file in your home directory.
With this alias set, you can navigate directories using xplr by entering
xcd command, and when you quit by pressing enter, you will enter the
directory.
You can of course, quit with plain Quit (i.e. by pressing q) to
gracefully cancel "cd on quit".
```bash
alias xcd='cd "$(xplr --print-pwd-as-result)"'
```
</details>
### Spawn multiple sessions in different tabs (iTerm2)
Creating a new session that starts with iTerm2.
<details>
<summary>Expand for details</summary>
- Author: [@lmburns][9]
- Requires: iTerm2
- Tested on: MacOS
```lua
xplr.config.modes.builtin.default.key_bindings.on_key["ctrl-n"] = {
help = "new session",
messages = {
{ BashExecSilently = [[
osascript <<EOF
tell application "iTerm2"
tell current window
create tab with default profile
tell current session to write text "xplr"
end tell
end tell
]]
},
},
}
```
</details>
### Bookmark
Bookmark files using `m` and fuzzy search bookmarks using backtick.
<details>
<summary>Expand for details</summary>
[![xplr-bookmark.gif][7]][6]
- Author: [@sayanarijit][8]
- Requires: fzf
- Tested on: Linux
```lua
xplr.config.modes.builtin.default.key_bindings.on_key.m = {
help = "bookmark",
messages = {
{
BashExecSilently0 = [===[
PTH="${XPLR_FOCUS_PATH:?}"
PTH_ESC=$(printf %q "$PTH")
if echo "${PTH:?}" >> "${XPLR_SESSION_PATH:?}/bookmarks"; then
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC added to bookmarks"
else
"$XPLR" -m 'LogError: %q' "Failed to bookmark $PTH_ESC"
fi
]===],
},
},
}
xplr.config.modes.builtin.default.key_bindings.on_key["`"] = {
help = "go to bookmark",
messages = {
{
BashExec0 = [===[
PTH=$(cat "${XPLR_SESSION_PATH:?}/bookmarks" | fzf --no-sort)
PTH_ESC=$(printf %q "$PTH")
if [ "$PTH" ]; then
"$XPLR" -m 'FocusPath: %q' "$PTH"
fi
]===],
},
},
}
```
</details>
### Persistent, multi-session bookmark
A bookmark mode that allows for a bookmark file to be used throughout multiples
sessions. It is set to the environment variable `$XPLR_BOOKMARK_FILE`. A
bookmark can be added, deleted, or jumped to.
<details>
<summary>Expand for details</summary>
- Author: [@lmburns][9]
- Requires: fzf, sd
- Tested on: MacOS
```lua
-- With `export XPLR_BOOKMARK_FILE="$HOME/bookmarks"`
-- Bookmark: mode binding
xplr.config.modes.builtin.default.key_bindings.on_key["b"] = {
help = "bookmark mode",
messages = {
{ SwitchModeCustom = "bookmark" },
},
}
xplr.config.modes.custom.bookmark = {
name = "bookmark",
key_bindings = {
on_key = {
m = {
help = "bookmark dir",
messages = {
{
BashExecSilently0 = [[
PTH="${XPLR_FOCUS_PATH:?}"
if [ -d "${PTH}" ]; then
PTH="${PTH}"
elif [ -f "${PTH}" ]; then
PTH=$(dirname "${PTH}")
fi
PTH_ESC=$(printf %q "$PTH")
if echo "${PTH:?}" >> "${XPLR_BOOKMARK_FILE:?}"; then
"$XPLR" -m 'LogSuccess: %q' "$PTH_ESC added to bookmarks"
else
"$XPLR" -m 'LogError: %q' "Failed to bookmark $PTH_ESC"
fi
]],
},
"PopMode",
},
},
g = {
help = "go to bookmark",
messages = {
{
BashExec0 = [===[
PTH=$(cat "${XPLR_BOOKMARK_FILE:?}" | fzf --no-sort)
if [ "$PTH" ]; then
"$XPLR" -m 'FocusPath: %q' "$PTH"
fi
]===],
},
"PopMode",
},
},
d = {
help = "delete bookmark",
messages = {
{
BashExec0 = [[
PTH=$(cat "${XPLR_BOOKMARK_FILE:?}" | fzf --no-sort)
sd "$PTH\n" "" "${XPLR_BOOKMARK_FILE:?}"
]],
},
"PopMode",
},
},
esc = {
help = "cancel",
messages = {
"PopMode",
},
},
},
},
}
```
</details>
### Another bookmark manager type thing, taken from [wfxr's zsh plugin][13].
Another bookmark manager type thing, taken from [wfxr's zsh plugin][13] which has colored output with fzf.
<details>
<summary>Expand for details</summary>
- Author: [@lmburns][9]
- Requires: fzf, exa
- Tested on: MacOS
```lua
xplr.config.modes.builtin.go_to.key_bindings.on_key.b = {
help = "bookmark jump",
messages = {
"PopMode",
{ BashExec0 = [===[
field='\(\S\+\s*\)'
esc=$(printf '\033')
N="${esc}[0m"
R="${esc}[31m"
G="${esc}[32m"
Y="${esc}[33m"
B="${esc}[34m"
pattern="s#^${field}${field}${field}${field}#$Y\1$R\2$N\3$B\4$N#"
PTH=$(sed 's#: # -> #' "$PATHMARKS_FILE"| nl| column -t \
| gsed "${pattern}" \
| fzf --ansi \
--height '40%' \
--preview="echo {}|sed 's#.*-> ##'| xargs exa --color=always" \
--preview-window="right:50%" \
| sed 's#.*-> ##')
if [ "$PTH" ]; then
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
fi
]===]
},
}
}
```
</details>
### Fuzzy search history
Fuzzy search the last visited directories.
<details>
<summary>Expand for details</summary>
- Author: [@sayanarijit][8]
- Requires: fzf
- Tested on: Linux
```lua
xplr.config.modes.builtin.go_to.key_bindings.on_key.h = {
help = "history",
messages = {
"PopMode",
{
BashExec0 = [===[
PTH=$(cat "${XPLR_PIPE_HISTORY_OUT:?}" | sort -z -u | fzf --read0)
if [ "$PTH" ]; then
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
fi
]===],
},
},
}
```
</details>
### Batch rename
Batch rename the selected or visible files and directories in $PWD.
<details>
<summary>Expand for details</summary>
[![xplr-rename.gif][11]][10]
- Author: [@sayanarijit][8]
- Requires: [pipe-rename][12]
- Tested on: Linux
```lua
xplr.config.modes.builtin.default.key_bindings.on_key.R = {
help = "batch rename",
messages = {
{
BashExec = [===[
SELECTION=$(cat "${XPLR_PIPE_SELECTION_OUT:?}")
NODES=${SELECTION:-$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}")}
if [ "$NODES" ]; then
echo -e "$NODES" | renamer
"$XPLR" -m ExplorePwdAsync
fi
]===],
},
},
}
```
</details>
### Serve $PWD
Serve $PWD using a static web server via LAN.
<details>
<summary>Expand for details</summary>
- Author: [@sayanarijit][8]
- Requires: [sfz][14], fzf
- Tested on: Linux
```lua
xplr.config.modes.builtin.default.key_bindings.on_key.S = {
help = "serve $PWD",
messages = {
{
BashExec0 = [===[
IP=$(ip addr | grep -w inet | cut -d/ -f1 | grep -Eo '[0-9]{1,3}\.[0-9]{ 1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | fzf --prompt 'Select IP > ')
echo "IP: ${IP:?}"
read -p "Port (default 5000): " PORT
echo
sfz --all --cors --no-ignore --bind ${IP:?} --port ${PORT:-5000} . &
sleep 1 && read -p '[press enter to exit]'
kill -9 %1
]===],
},
},
}
```
</details>
### Image viewer (imv)
Preview images using [imv][17].
<details>
<summary>Expand for details</summary>
- Author: [@sayanarijit][8]
- Requires: [imv][17], [xdotool][18]
- Tested on: Linux, FreeBSD 13.1-RELEASE
```lua
xplr.config.modes.builtin.default.key_bindings.on_key.P = {
help = "preview",
messages = {
{
BashExecSilently0 = [===[
FIFO_PATH="/tmp/xplr.fifo"
if [ -e "$FIFO_PATH" ]; then
"$XPLR" -m StopFifo
rm -f -- "$FIFO_PATH"
else
mkfifo "$FIFO_PATH"
"$HOME/.local/bin/imv-open.sh" "$FIFO_PATH" "$XPLR_FOCUS_PATH" &
"$XPLR" -m 'StartFifo: %q' "$FIFO_PATH"
fi
]===],
},
},
}
```
$HOME/.local/bin/imv-open.sh
```bash
#!/usr/bin/env bash
FIFO_PATH="$1"
IMAGE="$2"
MAINWINDOW="$(xdotool getactivewindow)"
IMV_PID="$(pgrep imv)"
if [ ! "$IMV_PID" ]; then
imv "$IMAGE" &
IMV_PID=$!
fi
sleep 0.5
xdotool windowactivate "$MAINWINDOW"
while read -r path; do
imv-msg "$IMV_PID" close all
imv-msg "$IMV_PID" open "$path"
done < "$FIFO_PATH"
imv-msg "$IMV_PID" quit
[ -e "$FIFO_PATH" ] && rm -f -- "$FIFO_PATH"
```
</details>
### Text preview pane
Preview text files in a native xplr pane (should be fast enough).
<details>
<summary>Expand for details</summary>
- Author: [@sayanarijit][8]
- Requires: none
- Tested on: Linux, FreeBSD 13.1-RELEASE
```lua
local function stat(node)
return xplr.util.to_yaml(xplr.util.node(node.absolute_path))
end
local function read(path, height)
local p = io.open(path)
if p == nil then
return nil
end
local i = 0
local res = ""
for line in p:lines() do
if line:match("[^ -~\n\t]") then
p:close()
return
end
res = res .. line .. "\n"
if i == height then
break
end
i = i + 1
end
p:close()
return res
end
xplr.fn.custom.preview_pane = {}
xplr.fn.custom.preview_pane.render = function(ctx)
local title = nil
local body = ""
local n = ctx.app.focused_node
if n and n.canonical then
n = n.canonical
end
if n then
title = { format = n.absolute_path, style = xplr.util.lscolor(n.absolute_path) }
if n.is_file then
body = read(n.absolute_path, ctx.layout_size.height) or stat(n)
else
body = stat(n)
end
end
return { CustomParagraph = { ui = { title = title }, body = body } }
end
local preview_pane = { Dynamic = "custom.preview_pane.render" }
local split_preview = {
Horizontal = {
config = {
constraints = {
{ Percentage = 60 },
{ Percentage = 40 },
},
},
splits = {
"Table",
preview_pane,
},
},
}
xplr.config.layouts.builtin.default =
xplr.util.layout_replace(xplr.config.layouts.builtin.default, "Table", split_preview)
```
</details>
### Tere Navigation
Navigate using the [tere][19] file explorer (defaults to type-to-nav system).
<details>
<summary>Expand for details</summary>
- Author: [@sayanarijit][8]
- Requires: [tere][19]
- Tested on: Linux
```lua
xplr.config.modes.builtin.default.key_bindings.on_key.T = {
help = "tere nav",
messages = {
{ BashExec0 = [["$XPLR" -m 'ChangeDirectory: %q' "$(tere)"]] },
},
}
```
</details>
## Also See:
- [Awesome Plugins][15]
- [Awesome Integrations][16]
[1]: configuration.md
[2]: plugin.md
[3]: https://github.com/sayanarijit/xplr/edit/main/docs/en/src/awesome-hacks.md
[4]: https://github.com/sayanarijit/xplr/discussions/categories/show-and-tell
[6]: https://gifyu.com/image/rGSR
[7]: https://s4.gifyu.com/images/xplr-bookmark.gif
[8]: https://github.com/sayanarijit
[9]: https://github.com/lmburns
[10]: https://gifyu.com/image/rGbo
[11]: https://s4.gifyu.com/images/xplr-rename.gif
[12]: https://github.com/marcusbuffett/pipe-rename
[13]: https://github.com/wfxr/formarks
[14]: https://github.com/weihanglo/sfz
[15]: awesome-plugins.md
[16]: awesome-integrations.md
[17]: https://sr.ht/~exec64/imv
[18]: https://www.semicomplete.com/projects/xdotool
[19]: https://github.com/mgunyho/tere

@ -0,0 +1,42 @@
# Awesome Integrations
Here's a list of awesome xplr integrations that you might want to check out.
If none of the following integrations work for you, you can create your own and
[let us know][1].
### Editor
- [**fm-nvim**][10] Neovim plugin that lets you use your favorite terminal file managers from within Neovim.
- [**vim-floaterm**][6] xplr integrated in vim-floaterm (Neo)vim plugin.
- [**xplr.vim**][5] Pick files in Vim using xplr.
### Github
- [**gh-xplr**][13] Explore GitHub repos using xplr via GitHub CLI.
### Shell
- [**powerlevel10k**][7] Powerlevel10k prompt for xplr shell.
### Security Tools
- [**gpg-tui**][8] Import GPG certificates using xplr.
## Also See:
- [Awesome Hacks][11]
- [Awesome Plugins][12]
[1]: https://github.com/sayanarijit/xplr/discussions/categories/show-and-tell
[2]: #editor
[3]: #shell
[4]: #security-tools
[5]: https://github.com/sayanarijit/xplr.vim
[6]: https://github.com/voldikss/vim-floaterm#xplr
[7]: https://github.com/romkatv/powerlevel10k/blob/191d1b89e325ee3b6d2d75a394654aaf4f077a7c/internal/p10k.zsh#L4756-L4768
[8]: https://github.com/orhun/gpg-tui#importreceive
[10]: https://github.com/is0n/fm-nvim
[11]: awesome-hacks.md
[12]: awesome-plugins.md
[13]: https://github.com/sayanarijit/gh-xplr

@ -0,0 +1,128 @@
# Awesome Plugins
Here's a list of awesome xplr plugins that you might want to [check out][48]. If none
of the following plugins work for you, it's very easy to
[write your own][1].
### Extension
- [**sayanarijit/command-mode.xplr**][37] The missing command mode for xplr.
- [**igorepst/context-switch.xplr**][42] Context switch plugin for xplr.
- [**sayanarijit/dual-pane.xplr**][43] Implements support for dual-pane navigation into xplr.
- [**sayanarijit/map.xplr**][38] Visually inspect and interactively execute batch commands using xplr.
- [**sayanarijit/offline-docs.xplr**][51] Fetch the appropriate version of xplr docs and browse offline.
- [**sayanarijit/regex-search.xplr**][55] Bring back the regex based search in xplr.
- [**sayanarijit/registers.xplr**][49] Use multiple registers to store the selected paths.
- [**sayanarijit/tree-view.xplr**][61] Hackable tree view for xplr
- [**sayanarijit/tri-pane.xplr**][56] xplr plugin that implements ranger-like three pane layout.
- [**sayanarijit/type-to-nav.xplr**][28] Inspired by [nnn's type-to-nav mode][29] for xplr,
with some tweaks.
- [**dtomvan/term.xplr**][39] Terminal integration for xplr.
- [**sayanarijit/wl-clipboard.xplr**][52] Copy and paste with system clipboard using wl-clipboard
- [**dtomvan/xpm.xplr**][47] The XPLR Plugin Manager.
- [**emsquid/style.xplr**][57] Helper plugin that allows you to integrate xplr's [Style][58] anywhere.
### Integration
- [**sayanarijit/alacritty.xplr**][33] [Alacritty][34] integration for xplr.
- [**sayanarijit/dragon.xplr**][4] Drag and drop files using [dragon][5].
- [**sayanarijit/dua-cli.xplr**][6] Get the disk usage using [dua-cli][7] with selection
support.
- [**sayanarijit/fzf.xplr**][8] Fuzzy search using [fzf][9] to focus on a file or enter.
- [**sayanarijit/find.xplr**][44] An interactive finder plugin to complement [map.xplr][38].
- [**Junker/nuke.xplr**][53] Open files in apps by file type or mime.
- [**sayanarijit/nvim-ctrl.xplr**][35] Send files to running Neovim sessions using
[nvim-ctrl][36].
- [**dtomvan/ouch.xplr**][40] This plugin uses [ouch][41] to compress and decompress files.
- [**dtomvan/paste-rs.xplr**][23] Use this plugin to paste your files to
[paste.rs][24], and open/delete them later using [fzf][9].
- [**sayanarijit/preview-tabbed.xplr**][10] Preview paths using suckless [tabbed][11] and
[nnn preview-tabbed][12].
- [**sayanarijit/qrcp.xplr**][26] Send and receive files via QR code using [qrcp][27].
- [**sayanarijit/scp.xplr**][54] Integrate xplr with scp.
- [**sayanarijit/trash-cli.xplr**][13] Trash files and directories using [trash-cli][14].
- [**sayanarijit/xclip.xplr**][15] Copy and paste with system clipboard using [xclip][16].
- [**sayanarijit/zoxide.xplr**][17] Change directory using the [zoxide][18] database.
### Theme
- [**sayanarijit/material-landscape.xplr**][19] Material Landscape
- [**sayanarijit/material-landscape2.xplr**][20] Material Landscape 2
- [**sayanarijit/zentable.xplr**][31] A clean, distraction free xplr table UI
- [**dy-sh/dysh-style.xplr**][63] Complements xplr theme with icons and highlighting.
- [**prncss-xyz/icons.xplr**][30] An icon theme for xplr.
- [**dtomvan/extra-icons.xplr**][50] Adds more icons to icons.xplr, compatible
with zentable.xplr.
- [**hartan/web-devicons.xplr**][59] Adds [nvim-web-devicons][60] to xplr with
optional coloring
- [**duganchen/one-table-column.xplr**][62] Moves file stats to a status bar.
- [**dy-sh/get-rid-of-index.xplr**][64] Removes the index column.
## Also See:
- [Awesome Hacks][45]
- [Awesome Integrations][46]
[1]: writing-plugins.md
[2]: #integration
[3]: #theme
[4]: https://github.com/sayanarijit/dragon.xplr
[5]: https://github.com/mwh/dragon
[6]: https://github.com/sayanarijit/dua-cli.xplr
[7]: https://github.com/Byron/dua-cli
[8]: https://github.com/sayanarijit/fzf.xplr
[9]: https://github.com/junegunn/fzf
[10]: https://github.com/sayanarijit/preview-tabbed.xplr
[11]: https://tools.suckless.org/tabbed/
[12]: https://github.com/jarun/nnn/blob/master/plugins/preview-tabbed
[13]: https://github.com/sayanarijit/trash-cli.xplr
[14]: https://github.com/andreafrancia/trash-cli
[15]: https://github.com/sayanarijit/xclip.xplr
[16]: https://github.com/astrand/xclip
[17]: https://github.com/sayanarijit/zoxide.xplr
[18]: https://github.com/ajeetdsouza/zoxide
[19]: https://github.com/sayanarijit/material-landscape.xplr
[20]: https://github.com/sayanarijit/material-landscape2.xplr
[22]: https://github.com/sayanarijit/xargs.xplr
[23]: https://github.com/dtomvan/paste-rs.xplr
[24]: https://paste.rs
[25]: https://github.com/sayanarijit/completion.xplr
[26]: https://github.com/sayanarijit/qrcp.xplr
[27]: https://github.com/claudiodangelis/qrcp
[28]: https://github.com/sayanarijit/type-to-nav.xplr
[29]: https://github.com/jarun/nnn/wiki/concepts#type-to-nav
[30]: https://github.com/prncss-xyz/icons.xplr
[31]: https://github.com/sayanarijit/zentable.xplr
[32]: #extension
[33]: https://github.com/sayanarijit/alacritty.xplr
[34]: https://github.com/alacritty/alacritty
[35]: https://github.com/sayanarijit/nvim-ctrl.xplr
[36]: https://github.com/chmln/nvim-ctrl
[37]: https://github.com/sayanarijit/command-mode.xplr
[38]: https://github.com/sayanarijit/map.xplr
[39]: https://github.com/dtomvan/term.xplr
[40]: https://github.com/dtomvan/ouch.xplr
[41]: https://github.com/ouch-org/ouch
[42]: https://github.com/igorepst/context-switch.xplr
[43]: https://github.com/sayanarijit/dual-pane.xplr
[44]: https://github.com/sayanarijit/find.xplr
[45]: awesome-hacks.md
[46]: awesome-integrations.md
[47]: https://github.com/dtomvan/xpm.xplr
[48]: installing-plugins.md
[49]: https://github.com/sayanarijit/registers.xplr
[50]: https://github.com/dtomvan/extra-icons.xplr
[51]: https://github.com/sayanarijit/offline-docs.xplr
[52]: https://github.com/sayanarijit/wl-clipboard.xplr
[53]: https://github.com/Junker/nuke.xplr
[54]: https://github.com/sayanarijit/scp.xplr
[55]: https://github.com/sayanarijit/regex-search.xplr
[56]: https://github.com/sayanarijit/tri-pane.xplr
[57]: https://github.com/emsquid/style.xplr
[58]: style.md
[59]: https://gitlab.com/hartan/web-devicons.xplr
[60]: https://github.com/nvim-tree/nvim-web-devicons
[61]: https://github.com/sayanarijit/tree-view.xplr
[62]: https://github.com/duganchen/one-table-column.xplr
[63]: https://github.com/dy-sh/dysh-style.xplr
[64]: https://github.com/dy-sh/get-rid-of-index.xplr

@ -0,0 +1,37 @@
# Borders
xplr allows customizing the shape and style of the borders.
### Border
A border is a [sum type][2] that can be one of the following:
- "Top"
- "Right"
- "Bottom"
- "Left"
### Border Type
A border type is a [sum type][2] that can be one of the following:
- "Plain"
- "Rounded"
- "Double"
- "Thick"
### Border Style
The [style][1] of the borders.
## Example
```lua
xplr.config.general.panel_ui.default.borders = { "Top", "Right", "Bottom", "Left" }
xplr.config.general.panel_ui.default.border_type = "Thick"
xplr.config.general.panel_ui.default.border_style.fg = "Black"
xplr.config.general.panel_ui.default.border_style.bg = "Gray"
```
[1]: style.md#style
[2]: sum-type.md

@ -0,0 +1,344 @@
# Column Renderer
A column renderer is a Lua function that receives a [special argument][1] and
returns a string that will be displayed in each specific field of the
[files table][2].
## Example: Customizing Table Renderer
```lua
xplr.fn.custom.fmt_simple_column = function(m)
return m.prefix .. m.relative_path .. m.suffix
end
xplr.config.general.table.header.cols = {
{ format = " path" }
}
xplr.config.general.table.row.cols = {
{ format = "custom.fmt_simple_column" }
}
xplr.config.general.table.col_widths = {
{ Percentage = 100 }
}
-- With this config, you should only see a single column displaying the
-- relative paths.
```
xplr by default provides the following column renderers:
- `xplr.fn.builtin.fmt_general_table_row_cols_0`
- `xplr.fn.builtin.fmt_general_table_row_cols_1`
- `xplr.fn.builtin.fmt_general_table_row_cols_2`
- `xplr.fn.builtin.fmt_general_table_row_cols_3`
- `xplr.fn.builtin.fmt_general_table_row_cols_4`
You can either overwrite these functions, or create new functions in
`xplr.fn.custom` and point to them.
Terminal colors are supported.
## Table Renderer Argument
The special argument contains the following fields
- [parent][3]
- [relative_path][4]
- [absolute_path][5]
- [extension][6]
- [is_symlink][7]
- [is_broken][8]
- [is_dir][9]
- [is_file][10]
- [is_readonly][11]
- [mime_essence][12]
- [size][13]
- [human_size][14]
- [permissions][15]
- [created][34]
- [last_modified][35]
- [uid][36]
- [gid][37]
- [canonical][16]
- [symlink][17]
- [index][18]
- [relative_index][19]
- [is_before_focus][20]
- [is_after_focus][21]
- [tree][22]
- [prefix][23]
- [suffix][24]
- [is_selected][25]
- [is_focused][26]
- [total][27]
- [style][38]
- [meta][28]
### parent
Type: string
The parent path of the node.
### relative_path
Type: string
The path relative to the parent, i.e. the file/directory name with extension.
### absolute_path
Type: string
The absolute path (without resolving symlinks) of the node.
### extension
Type: string
The extension of the node.
### is_symlink
Type: boolean
`true` if the node is a symlink.
### is_broken
Type: boolean
`true` if the node is a broken symlink.
### is_dir
Type: boolean
`true` if the node is a directory.
### is_file
Type: boolean
`true` if the node is a file.
### is_readonly
Type: boolean
`true` if the node is real-only.
### mime_essence
Type: string
The mime type of the node. For e.g. `text/csv`, `image/jpeg` etc.
### size
Type: integer
The size of the exact node. The size of a directory won't be calculated
recursively.
### human_size
Type: string
Like [size][29] but in human readable format.
### permissions
Type: [Permission][30]
The [permissions][30] applied to the node.
### created
Type: nullable integer
Creation time in nanosecond since UNIX epoch.
### last_modified
Type: nullable integer
Last modification time in nanosecond since UNIX epoch.
### uid
Type: integer
User ID of the file owner.
### gid
Type: integer
Group ID of the file owner.
### canonical
Type: nullable [Resolved Node Metadata][31]
If the node is a symlink, it will hold information about the symlink resolved
node. Else, it will hold information the actual node. It the symlink is broken,
it will be null.
### symlink
Type: nullable [Resolved Node Metadata][31]
If the node is a symlink and is not broken, it will hold information about the
symlink resolved node. However, it will never hold information about the actual
node. It will instead be null.
### index
Type: integer
Index (starting from 0) of the node.
### relative_index
Type: integer
Relative index from the focused node (i.e. 0th node).
### is_before_focus
Type: boolean
`true` if the node is before the focused node.
### is_after_focus
Type: boolean
`true` if the node is after the focused node.
### tree
Type: string
The [tree component][32] based on the node's index.
### prefix
Type: string
The prefix applicable for the node.
### suffix
Type: string
The suffix applicable for the node.
### is_selected
Type: boolean
`true` if the node is selected.
### is_focused
Type: boolean
`true` if the node is under focus.
### total
Type: integer
The total number of the nodes.
### style
Type: [Style][39]
The applicable [style object][39] for the node.
### meta
Type: mapping of string and string
The applicable [meta object][33] for the node.
## Permission
Permission contains the following fields:
- user_read
- user_write
- user_execute
- group_read
- group_write
- group_execute
- other_read
- other_write
- other_execute
- sticky
- setgid
- setuid
Each field holds a boolean value.
## Resolved Node Metadata
It contains the following fields.
- [absolute_path][5]
- [extension][6]
- [is_dir][9]
- [is_file][10]
- [is_readonly][11]
- [mime_essence][12]
- [size][13]
- [human_size][14]
- [created][34]
- [last_modified][35]
- [uid][36]
- [gid][37]
[1]: #table-renderer-argument
[2]: layout.md#table
[3]: #parent
[4]: #relative_path
[5]: #absolute_path
[6]: #extension
[7]: #is_symlink
[8]: #is_broken
[9]: #is_dir
[10]: #is_file
[11]: #is_readonly
[12]: #mime_essence
[13]: #size
[14]: #human_size
[15]: #permissions
[16]: #canonical
[17]: #symlink
[18]: #index
[19]: #relative_index
[20]: #is_before_focus
[21]: #is_after_focus
[22]: #tree
[23]: #prefix
[24]: #suffix
[25]: #is_selected
[26]: #is_focused
[27]: #total
[28]: #meta
[29]: #size
[30]: #permission
[31]: #resolved-node-metadata
[32]: general-config.md#tabletree
[33]: node-type.md#meta
[34]: #created
[35]: #last_modified
[36]: #uid
[37]: #gid
[38]: #style
[39]: style.md#style

@ -0,0 +1,30 @@
# Concept
These are the concepts that make xplr probably the most hackable terminal file
explorer.
- [Key Bindings][1]
- [Node Type][2]
- [Layout][3]
- [Mode][4]
- [Message][5]
- [Borders][6]
- [Style][7]
- [Sorting][8]
- [Filtering][9]
- [Column Renderer][10]
- [Lua Function Calls][11]
- [Environment Variables and Pipes][12]
[1]: key-bindings.md
[2]: node-type.md
[3]: layout.md
[4]: mode.md
[5]: message.md
[6]: borders.md
[7]: style.md
[8]: sorting.md
[9]: filtering.md
[10]: column-renderer.md
[11]: lua-function-calls.md
[12]: environment-variables-and-pipes.md

@ -0,0 +1,156 @@
# Configuration
xplr can be configured using [Lua][1] via a special file named `init.lua`,
which can be placed in `~/.config/xplr/` (local to user) or `/etc/xplr/`
(global) depending on the use case.
When xplr loads, it first executes the [built-in init.lua][2] to set the
default values, which is then overwritten by another config file, if found
using the following lookup order:
1. `--config /path/to/init.lua`
2. `~/.config/xplr/init.lua`
3. `/etc/xplr/init.lua`
The first one found will be loaded by xplr and the lookup will stop.
The loaded config can be further extended using the `-C` or `--extra-config`
command-line option.
[1]: https://www.lua.org
[2]: https://github.com/sayanarijit/xplr/blob/main/src/init.lua
[3]: https://xplr.dev/en/upgrade-guide
## Config
The xplr configuration, exposed via `xplr.config` Lua API contains the
following sections.
See:
- [xplr.config.general](https://xplr.dev/en/general-config)
- [xplr.config.node_types](https://xplr.dev/en/node_types)
- [xplr.config.layouts](https://xplr.dev/en/layouts)
- [xplr.config.modes](https://xplr.dev/en/modes)
## Function
While `xplr.config` defines all the static parts of the configuration,
`xplr.fn` defines all the dynamic parts using functions.
See: [Lua Function Calls](https://xplr.dev/en/lua-function-calls)
As always, `xplr.fn.builtin` is where the built-in functions are defined
that can be overwritten.
#### xplr.fn.builtin.try_complete_path
Tries to auto complete the path in the input buffer
#### xplr.fn.builtin.fmt_general_table_row_cols_0
Renders the first column in the table
#### xplr.fn.builtin.fmt_general_table_row_cols_1
Renders the second column in the table
#### xplr.fn.builtin.fmt_general_table_row_cols_2
Renders the third column in the table
#### xplr.fn.builtin.fmt_general_table_row_cols_3
Renders the fourth column in the table
#### xplr.fn.builtin.fmt_general_table_row_cols_4
Renders the fifth column in the table
#### xplr.fn.custom
This is where the custom functions can be added.
There is currently no restriction on what kind of functions can be defined
in `xplr.fn.custom`.
You can also use nested tables such as
`xplr.fn.custom.my_plugin.my_function` to define custom functions.
## Hooks
This section of the configuration cannot be overwritten by another config
file or plugin, since this is an optional lua return statement specific to
each config file. It can be used to define things that should be explicit
for reasons like performance concerns, such as hooks.
Plugins should expose the hooks, and require users to subscribe to them
explicitly.
Example:
```lua
return {
-- Add messages to send when the xplr loads.
-- This is similar to the `--on-load` command-line option.
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_load = {
{ LogSuccess = "Configuration successfully loaded!" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_load" },
},
-- Add messages to send when the directory changes.
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_directory_change = {
{ LogSuccess = "Changed directory" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_directory_change" },
},
-- Add messages to send when the focus changes.
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_focus_change = {
{ LogSuccess = "Changed focus" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_focus_change" },
}
-- Add messages to send when the mode is switched.
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_mode_switch = {
{ LogSuccess = "Switched mode" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_mode_switch" },
}
-- Add messages to send when the layout is switched
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_layout_switch = {
{ LogSuccess = "Switched layout" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_layout_switch" },
}
-- Add messages to send when the selection changes
--
-- Type: list of [Message](https://xplr.dev/en/message#message)s
on_selection_change = {
{ LogSuccess = "Selection changed" },
{ CallLuaSilently = "custom.some_plugin_with_hooks.on_selection_change" },
}
}
```
---
> Note:
>
> It's not recommended to copy the entire configuration, unless you want to
> freeze it and miss out on useful updates to the defaults.
>
> Instead, you can use this as a reference to overwrite only the parts you
> want to update.
>
> If you still want to copy the entire configuration, make sure to put your
> customization before the return statement.

@ -0,0 +1,233 @@
# Configure Key Bindings
In xplr, each keyboard input passes through a bunch of handlers (e.g. `on_key`,
`on_number`, `default` etc.) in a given order. If any of the handlers is
configured to with an [action][16], it will intercept the key and produce
[messages][18] for xplr to handle.
Try [debug key bindings][31] to understand how key bindings actually work.
## Key Bindings
Key bindings contains the following information:
- [on_key][10]
- [on_alphabet][11]
- [on_number][12]
- [on_alphanumeric][32]
- [on_special_character][13]
- [on_character][33]
- [on_navigation][34]
- [on_function][35]
- [default][14]
### on_key
Type: mapping of [Key][15] to nullable [Action][16]
Defines what to do when an exact key is pressed.
### on_alphabet
Type: nullable [Action][16]
An action to perform if the keyboard input is an alphabet and is not mapped via
the [on_key][10] field.
### on_number
Type: nullable [Action][16]
An action to perform if the keyboard input is a number and is not mapped via
the [on_key][10] field.
### on_alphanumeric
Type: nullable [Action][16]
An action to perform if the keyboard input is alphanumeric and is not mapped
via the [on_key][10], [on_alphabet][11] or [on_number][12] field.
### on_special_character
Type: nullable [Action][16]
An action to perform if the keyboard input is a special character and is not
mapped via the [on_key][10] field.
### on_character
Type: nullable [Action][16]
An action to perform if the keyboard input is a character and is not mapped
via the [on_key][10], [on_alphabet][11], [on_number][12], [on_alphanumeric][32]
or [on_special_character][13] field.
### on_navigation
Type: nullable [Action][16]
An action to perform if the keyboard input is a navigation key and is not
mapped via the [on_key][10] field.
### on_function
Type: nullable [Action][16]
An action to perform if the keyboard input is a function key and is not mapped
via the [on_key][10] field.
### default
Type: nullable [Action][16]
Default action to perform in case if a keyboard input not mapped via any of the
`on_*` fields mentioned above.
## Key
A key is a [sum type][36] can be one of the following:
- 0, 1, ... 9
- a, b, ... z
- A, B, ... Z
- f1, f2, ... f12
- backspace
- left
- right
- up
- down
- home
- end
- page-up
- page-down
- back-tab
- delete
- insert
- enter
- tab
- esc
- ctrl-a, ctrl-b, ... ctrl-z
- ctrl-backspace, ctrl-left, ... ctrl-esc
- alt-a, alt-b, ... alt-z
And finally, the special characters - including space (`" "`) with their `ctrl`
bindings.
## Action
An action contains the following information:
- [help][1]
- [messages][17]
### help
Type: nullable string
Description of what it does. If unspecified, it will be excluded from the help
menu.
### messages
Type: A list of [Message][18] to send.
The list of messages to send when a key is pressed.
## Tutorial: Adding a New Mode
Assuming xplr is [installed][19] and [setup][20], let's
add our own mode to integrate xplr with [fzf][21].
We'll call it `fzxplr` mode.
First, let's add a custom mode called `fzxplr`, and map the key `F` to an
action that will call `fzf` to search and focus on a file or enter into a
directory.
```lua
xplr.config.modes.custom.fzxplr = {
name = "fzxplr",
key_bindings = {
on_key = {
F = {
help = "search",
messages = {
{
BashExec = [===[
PTH=$(cat "${XPLR_PIPE_DIRECTORY_NODES_OUT:?}" | awk -F/ '{print $NF}' | fzf)
if [ -d "$PTH" ]; then
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
else
"$XPLR" -m 'FocusPath: %q' "$PTH"
fi
]===]
},
"PopMode",
},
},
},
default = {
messages = {
"PopMode",
},
},
},
}
```
As you can see, the key `F` in mode `fzxplr` (the name can be anything)
executes a script in `bash`.
`BashExec`, `PopMode`, `SwitchModeBuiltin`, `ChangeDirectory` and `FocusPath`
are [messages][18], `$XPLR`, `$XPLR_PIPE_DIRECTORY_NODES_OUT` are
[environment variables][22] exported by `xplr` before executing the command.
They contain the path to the [input][23] and [output][24] pipes that allows
external tools to interact with `xplr`.
Now that we have our new mode ready, let's add an entry point to this mode via
the `default` mode.
```lua
xplr.config.modes.builtin.default.key_bindings.on_key["F"] = {
help = "fzf mode",
messages = {
{ SwitchModeCustom = "fzxplr" },
},
}
```
Now let's try out the new `xplr`-`fzf` integration.
[![xplr-fzf.gif][25]][26]
---
Visit [Awesome Plugins][27] for more [integration][28] options.
[1]: #help
[10]: #on_key
[11]: #on_alphabet
[12]: #on_number
[13]: #on_special_character
[14]: #default
[15]: #key
[16]: #action
[17]: #messages
[18]: message.md#message
[19]: install.md
[20]: post-install.md
[21]: https://github.com/junegunn/fzf
[22]: environment-variables-and-pipes.md#environment-variables
[23]: environment-variables-and-pipes.md#input-pipe
[24]: environment-variables-and-pipes.md#output-pipes
[25]: https://s3.gifyu.com/images/xplr-fzf.gif
[26]: https://gifyu.com/image/tW86
[27]: awesome-plugins.md
[28]: awesome-plugins.md#integration
[31]: debug-key-bindings.md
[32]: #on_alphanumeric
[33]: #on_character
[34]: #on_navigation
[35]: #on_function
[36]: sum-type.md

@ -0,0 +1,109 @@
# Debug Key Bindings
If you need help debugging or understanding key bindings DYI way, you can
create a `test.lua` file with the following script, launch xplr with
`xplr --extra-config test.lua`, press `#` and play around.
```lua
-- The global key bindings inherited by all the modes.
xplr.config.general.global_key_bindings = {
on_key = {
esc = {
help = "escape",
messages = {
{ LogInfo = "global on_key(esc) called" },
"PopMode",
},
},
["ctrl-c"] = {
help = "terminate",
messages = {
"Terminate",
},
},
},
}
-- Press `#` to enter the `debug key bindings` mode.
xplr.config.modes.builtin.default.key_bindings.on_key["#"] = {
help = "test",
messages = {
"PopMode",
{ SwitchModeCustom = "debug_key_bindings" },
},
}
-- The `debug key bindings` mode.
xplr.config.modes.custom.debug_key_bindings = {
name = "debug key bindings",
key_bindings = {
on_key = {
["1"] = {
messages = {
{ LogInfo = "on_key(1) called" },
},
},
a = {
messages = {
{ LogInfo = "on_key(a) called" },
},
},
["`"] = {
messages = {
{ LogInfo = "on_key(`) called" },
},
},
tab = {
messages = {
{ LogInfo = "on_key(tab) called" },
},
},
f1 = {
messages = {
{ LogInfo = "on_key(f1) called" },
},
},
},
on_alphabet = {
messages = {
{ LogInfo = "on_alphabet called" },
},
},
on_number = {
messages = {
{ LogInfo = "on_number called" },
},
},
-- on_alphanumeric = {
-- messages = {
-- { LogInfo = "on_alphanumeric called" },
-- },
-- },
on_special_character = {
messages = {
{ LogInfo = "on_special_character called" },
},
},
-- on_character = {
-- messages = {
-- { LogInfo = "on_character called" },
-- },
-- },
on_navigation = {
messages = {
{ LogInfo = "on_navigation called" },
},
},
on_function = {
messages = {
{ LogInfo = "on_function called" },
},
},
default = {
messages = {
{ LogInfo = "default called" },
},
},
},
}
```

@ -0,0 +1,348 @@
# Default Key Bindings
The default key binding is inspired by [vim][1] and slightly
overlaps with [nnn][2], but it's supposed to be customized as per user
requirements.
When you press `?` in [default mode][3], you can see the complete list
of [modes][4] and the key mappings for each mode.
[1]: https://www.vim.org/
[2]: https://github.com/jarun/nnn/
[3]: #default
[4]: modes.md
### default
| key | remaps | action |
| --------- | ------ | ------------------- |
| ( | | prev deep branch |
| ) | | next deep branch |
| . | | show hidden |
| / | ctrl-f | search |
| : | | action |
| ? | f1 | global help menu |
| G | | go to bottom |
| V | ctrl-a | select/unselect all |
| c | | copy to |
| ctrl-d | | duplicate as |
| ctrl-i | tab | next visited path |
| ctrl-n | | next selection |
| ctrl-o | | last visited path |
| ctrl-p | | prev selection |
| ctrl-r | | refresh screen |
| ctrl-u | | clear selection |
| ctrl-w | | switch layout |
| d | | delete |
| down | j | down |
| enter | | quit with result |
| f | | filter |
| g | | go to |
| h | left | back |
| k | up | up |
| l | right | enter |
| m | | move to |
| page-down | | scroll down |
| page-up | | scroll up |
| q | | quit |
| r | | rename |
| s | | sort |
| space | v | toggle selection |
| { | | scroll up half |
| } | | scroll down half |
| ~ | | go home |
| [0-9] | | input |
### go_to_path
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |
### rename
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |
### recover
| key | remaps | action |
| --- | ------ | ---------------- |
| f1 | | global help menu |
### go_to
| key | remaps | action |
| --- | ------ | ---------------- |
| f | | follow symlink |
| f1 | | global help menu |
| g | | top |
| i | | initial $PWD |
| p | | path |
| x | | open in gui |
### relative_path_does_match_regex
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
### action
| key | remaps | action |
| ----- | ------ | -------------------- |
| ! | | shell |
| c | | create |
| e | | open in editor |
| f1 | | global help menu |
| l | | logs |
| m | | toggle mouse |
| p | | edit permissions |
| q | | quit options |
| s | | selection operations |
| v | | vroot |
| [0-9] | | go to index |
### default
| key | remaps | action |
| --------- | ------ | ------------------- |
| ( | | prev deep branch |
| ) | | next deep branch |
| . | | show hidden |
| / | ctrl-f | search |
| : | | action |
| ? | f1 | global help menu |
| G | | go to bottom |
| V | ctrl-a | select/unselect all |
| c | | copy to |
| ctrl-d | | duplicate as |
| ctrl-i | tab | next visited path |
| ctrl-n | | next selection |
| ctrl-o | | last visited path |
| ctrl-p | | prev selection |
| ctrl-r | | refresh screen |
| ctrl-u | | clear selection |
| ctrl-w | | switch layout |
| d | | delete |
| down | j | down |
| enter | | quit with result |
| f | | filter |
| g | | go to |
| h | left | back |
| k | up | up |
| l | right | enter |
| m | | move to |
| page-down | | scroll down |
| page-up | | scroll up |
| q | | quit |
| r | | rename |
| s | | sort |
| space | v | toggle selection |
| { | | scroll up half |
| } | | scroll down half |
| ~ | | go home |
| [0-9] | | input |
### debug_error
| key | remaps | action |
| ----- | ------ | ------------------- |
| enter | | open logs in editor |
| f1 | | global help menu |
| q | | quit |
### create_directory
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |
### selection_ops
| key | remaps | action |
| --- | ------ | ---------------- |
| c | | copy here |
| e | | edit selection |
| f1 | | global help menu |
| h | | hardlink here |
| l | | list selection |
| m | | move here |
| s | | softlink here |
| u | | clear selection |
### relative_path_does_not_match_regex
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
### create_file
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |
### quit
| key | remaps | action |
| ----- | ------ | ----------------------- |
| enter | | just quit |
| f | | quit printing focus |
| f1 | | global help menu |
| p | | quit printing pwd |
| r | | quit printing result |
| s | | quit printing selection |
### create
| key | remaps | action |
| --- | ------ | ---------------- |
| d | | create directory |
| f | | create file |
| f1 | | global help menu |
### vroot
| key | remaps | action |
| ------ | ------ | ---------------- |
| . | | vroot $PWD |
| / | | vroot / |
| ctrl-r | | reset vroot |
| ctrl-u | | unset vroot |
| f1 | | global help menu |
| v | | toggle vroot |
| ~ | | vroot $HOME |
### search
| key | remaps | action |
| ------ | ------ | ----------------------- |
| ctrl-a | | toggle search algorithm |
| ctrl-f | | fuzzy search |
| ctrl-n | down | down |
| ctrl-p | up | up |
| ctrl-r | | regex search |
| ctrl-s | | sort (no search order) |
| ctrl-z | | toggle ordering |
| enter | | submit |
| esc | | cancel |
| f1 | | global help menu |
| left | | back |
| right | | enter |
| tab | | toggle selection |
### switch_layout
| key | remaps | action |
| --- | ------ | -------------------- |
| 1 | | default |
| 2 | | no help menu |
| 3 | | no selection panel |
| 4 | | no help or selection |
| f1 | | global help menu |
### sort
| key | remaps | action |
| --------- | ------ | --------------------------------- |
| ! | | reverse sorters |
| C | | by created reverse |
| E | | by canonical extension reverse |
| L | | by last modified reverse |
| M | | by canonical mime essence reverse |
| N | | by node type reverse |
| R | | by relative path reverse |
| S | | by size reverse |
| backspace | | remove last sorter |
| c | | by created |
| ctrl-r | | reset sorters |
| ctrl-u | | clear sorters |
| e | | by canonical extension |
| enter | | submit |
| f1 | | global help menu |
| l | | by last modified |
| m | | by canonical mime essence |
| n | | by node type |
| r | | by relative path |
| s | | by size |
### number
| key | remaps | action |
| ----- | ------ | ---------------- |
| down | j | to down |
| enter | | to index |
| f1 | | global help menu |
| k | up | to up |
| [0-9] | | input |
### copy_to
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |
### edit_permissions
| key | remaps | action |
| ------ | ------ | ---------------- |
| G | | -group |
| M | | min |
| O | | -other |
| U | | -user |
| ctrl-r | | reset |
| enter | | submit |
| f1 | | global help menu |
| g | | +group |
| m | | max |
| o | | +other |
| u | | +user |
### delete
| key | remaps | action |
| --- | ------ | ---------------- |
| D | | force delete |
| d | | delete |
| f1 | | global help menu |
### move_to
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |
### filter
| key | remaps | action |
| --------- | ------ | ---------------------------------- |
| R | | relative path does not match regex |
| backspace | | remove last filter |
| ctrl-r | | reset filters |
| ctrl-u | | clear filters |
| f1 | | global help menu |
| r | | relative path does match regex |
### duplicate_as
| key | remaps | action |
| ----- | ------ | ---------------- |
| enter | | submit |
| f1 | | global help menu |
| tab | | try complete |

@ -0,0 +1,230 @@
# Environment Variables and Pipes
Alternative to `CallLua`, `CallLuaSilently` messages that call Lua functions,
there are `Call0`, `CallSilently0`, `BashExec0`, `BashExecSilently0` messages
that call shell commands.
### Example: Simple file opener using xdg-open and $XPLR_FOCUS_PATH
```lua
xplr.config.modes.builtin.default.key_bindings.on_key["X"] = {
help = "open",
messages = {
{
BashExecSilently0 = [===[
xdg-open "${XPLR_FOCUS_PATH:?}"
]===],
},
},
}
```
However, unlike the Lua functions, these shell commands have to read the useful
information and send messages via environment variables and temporary files
called "pipe"s. These environment variables and files are only available when
a command is being executed.
### Example: Using Environment Variables and Pipes
```lua
xplr.config.modes.builtin.default.key_bindings.on_key["space"] = {
help = "ask name and greet",
messages = {
{
BashExec0 = [===[
echo "What's your name?"
read name
greeting="Hello $name!"
message="$greeting You are inside $PWD"
"$XPLR" -m 'LogSuccess: %q' "$message"
]===]
}
}
}
-- Now, when you press "space" in default mode, you will be prompted for your
-- name. Enter your name to receive a nice greeting and to know your location.
```
Visit the [**fzf integration tutorial**][19] for another example.
To see the environment variables and pipes, invoke the shell by typing `:!` in default
mode and run the following command:
```
env | grep ^XPLR
```
You will see something like:
```
XPLR=xplr
XPLR_FOCUS_INDEX=0
XPLR_MODE=action to
XPLR_PIPE_SELECTION_OUT=/run/user/1000/xplr/session/122278/pipe/selection_out
XPLR_INPUT_BUFFER=
XPLR_PIPE_GLOBAL_HELP_MENU_OUT=/run/user/1000/xplr/session/122278/pipe/global_help_menu_out
XPLR_PID=122278
XPLR_PIPE_MSG_IN=/run/user/1000/xplr/session/122278/pipe/msg_in
XPLR_PIPE_LOGS_OUT=/run/user/1000/xplr/session/122278/pipe/logs_out
XPLR_PIPE_RESULT_OUT=/run/user/1000/xplr/session/122278/pipe/result_out
XPLR_PIPE_HISTORY_OUT=/run/user/1000/xplr/session/122278/pipe/history_out
XPLR_FOCUS_PATH=/home/sayanarijit/Documents/GitHub/xplr/docs/en/book
XPLR_SESSION_PATH=/run/user/1000/xplr/session/122278
XPLR_APP_VERSION=0.14.3
XPLR_PIPE_DIRECTORY_NODES_OUT=/run/user/1000/xplr/session/122278/pipe/directory_nodes_out
```
The environment variables starting with `XPLR_PIPE_` are the temporary files
called ["pipe"s][18].
The other variables are single-line variables containing simple information:
- [XPLR][38]
- [XPLR_APP_VERSION][30]
- [XPLR_FOCUS_INDEX][31]
- [XPLR_FOCUS_PATH][32]
- [XPLR_INPUT_BUFFER][33]
- [XPLR_INITIAL_PWD][40]
- [XPLR_MODE][34]
- [XPLR_PID][35]
- [XPLR_SESSION_PATH][36]
- [XPLR_VROOT][39]
### Environment variables
#### XPLR
The binary path of xplr command.
#### XPLR_APP_VERSION
Self-explanatory.
#### XPLR_FOCUS_INDEX
Contains the index of the currently focused item, as seen in
[column-renderer/index][10].
#### XPLR_FOCUS_PATH
Contains the full path of the currently focused node.
#### XPLR_INITIAL_PWD
The $PWD then xplr started.
#### XPLR_INPUT_BUFFER
The line currently in displaying in the xplr input buffer. For e.g. the search
input while searching. See [Reading Input][37].
#### XPLR_MODE
Contains the mode xplr is currently in, see [modes][11].
#### XPLR_PID
Contains the process ID of the current xplr process.
#### XPLR_SESSION_PATH
Contains the current session path, like /tmp/runtime-"$USER"/xplr/session/"$XPLR_PID"/,
you can find temporary files here, such as pipes.
#### XPLR_VROOT
Contains the path of current virtual root, is set.
### Pipes
#### Input pipe
Currently there is only one input pipe.
- [XPLR_PIPE_MSG_IN][20]
#### Output pipes
`XPLR_PIPE_*_OUT` are the output pipes that contain data which cannot be
exposed directly via environment variables, like multi-line strings.
These pipes can be accessed as plain text files located in $XPLR_SESSION_PATH.
Depending on the message (e.g. `Call` or `Call0`), each line will be separated
by newline or null character (`\n` or `\0`).
- [XPLR_PIPE_SELECTION_OUT][21]
- [XPLR_PIPE_GLOBAL_HELP_MENU_OUT][22]
- [XPLR_PIPE_LOGS_OUT][23]
- [XPLR_PIPE_RESULT_OUT][24]
- [XPLR_PIPE_HISTORY_OUT][25]
- [XPLR_PIPE_DIRECTORY_NODES_OUT][26]
#### XPLR_PIPE_MSG_IN
Append new messages to this pipe in [YAML][27] (or [JSON][7]) syntax. These
messages will be read and handled by xplr after the command execution.
Depending on the message (e.g. `Call` or `Call0`) you need to separate each
message using newline or null character (`\n` or `\0`).
> **_NOTE:_** Since version `v0.20.0`, it's recommended to avoid writing
> directly to this file, as safely escaping YAML strings is a lot of work. Use
> `xplr -m` / `xplr --pipe-msg-in` to pass messages to xplr in a safer way.
>
> It uses [jf][41] syntax to safely convert an YAML template into a valid message.
>
> Example: `"$XPLR" -m 'ChangeDirectory: %q' "${HOME:?}"`
#### XPLR_PIPE_SELECTION_OUT
List of selected paths.
#### XPLR_PIPE_GLOBAL_HELP_MENU_OUT
The full help menu.
#### XPLR_PIPE_LOGS_OUT
List of logs.
#### XPLR_PIPE_RESULT_OUT
Result (selected paths if any, else the focused path)
#### XPLR_PIPE_HISTORY_OUT
List of last visited paths (similar to jump list in vim).
#### XPLR_PIPE_DIRECTORY_NODES_OUT
List of paths, filtered and sorted as displayed in the [files table][28].
[7]: https://www.json.org
[10]: column-renderer.md#index
[11]: modes.md#modes
[18]: #pipes
[19]: configure-key-bindings.md#tutorial-adding-a-new-mode
[20]: #xplr_pipe_msg_in
[21]: #xplr_pipe_selection_out
[22]: #xplr_pipe_global_help_menu_out
[23]: #xplr_pipe_logs_out
[24]: #xplr_pipe_result_out
[25]: #xplr_pipe_history_out
[26]: #xplr_pipe_directory_nodes_out
[27]: https://www.yaml.org
[28]: layout.md#table
[30]: #xplr_app_version
[31]: #xplr_focus_index
[32]: #xplr_focus_path
[33]: #xplr_input_buffer
[34]: #xplr_mode
[35]: #xplr_pid
[36]: #xplr_session_path
[37]: messages.md#reading-input
[38]: #xplr
[39]: #xplr_vroot
[40]: #xplr_initial_pwd
[41]: https://github.com/sayanarijit/jf

@ -0,0 +1,93 @@
# Filtering
xplr supports filtering paths by different properties. The filtering mechanism
works like a pipeline, which in visible in the `Sort & filter` panel.
Example:
```
rel!^. [i]abs=~abc [i]rel!~xyz
```
This line means that the nodes visible on the table will first be filtered by
the condition: _relative path does not start with `.`_, then by the condition:
_absolute path contains `abc` (case insensitive)_, and finally by the
condition: _relative path does not contain `xyz`_ (case insensitive).
Each part of this pipeline is called [Node Filter Applicable][1].
## Node Filter Applicable
It contains the following information:
- [filter][2]
- [input][3]
### filter
A filter is a [sum type][5] that can be one of the following:
- "RelativePathIs"
- "RelativePathIsNot"
- "IRelativePathIs"
- "IRelativePathIsNot"
- "RelativePathDoesStartWith"
- "RelativePathDoesNotStartWith"
- "IRelativePathDoesStartWith"
- "IRelativePathDoesNotStartWith"
- "RelativePathDoesContain"
- "RelativePathDoesNotContain"
- "IRelativePathDoesContain"
- "IRelativePathDoesNotContain"
- "RelativePathDoesEndWith"
- "RelativePathDoesNotEndWith"
- "IRelativePathDoesEndWith"
- "IRelativePathDoesNotEndWith"
- "RelativePathDoesMatchRegex"
- "RelativePathDoesNotMatchRegex"
- "IRelativePathDoesMatchRegex"
- "IRelativePathDoesNotMatchRegex"
- "AbsolutePathIs"
- "AbsolutePathIsNot"
- "IAbsolutePathIs"
- "IAbsolutePathIsNot"
- "AbsolutePathDoesStartWith"
- "AbsolutePathDoesNotStartWith"
- "IAbsolutePathDoesStartWith"
- "IAbsolutePathDoesNotStartWith"
- "AbsolutePathDoesContain"
- "AbsolutePathDoesNotContain"
- "IAbsolutePathDoesContain"
- "IAbsolutePathDoesNotContain"
- "AbsolutePathDoesEndWith"
- "AbsolutePathDoesNotEndWith"
- "IAbsolutePathDoesEndWith"
- "IAbsolutePathDoesNotEndWith"
- "AbsolutePathDoesMatchRegex"
- "AbsolutePathDoesNotMatchRegex"
- "IAbsolutePathDoesMatchRegex"
- "IAbsolutePathDoesNotMatchRegex"
### input
Type: string
The input for the condition.
## Example:
```lua
ToggleNodeFilter = {
filter = "RelativePathDoesNotStartWith",
input = "."
}
```
Here, `ToggleNodeFilter` is a [message][4] that adds or removes
(toggles) the filter applied.
[1]: #node-filter-applicable
[2]: #filter
[3]: #input
[4]: message.md
[5]: sum-type.md

@ -0,0 +1,628 @@
### General Configuration
The general configuration properties are grouped together in
`xplr.config.general`.
#### xplr.config.general.disable_debug_error_mode
Set it to `true` if you want to ignore the startup errors. You can still see
the errors in the logs.
Type: boolean
#### xplr.config.general.enable_mouse
Set it to `true` if you want to enable mouse scrolling.
Type: boolean
#### xplr.config.general.show_hidden
Set it to `true` to show hidden files by default.
Type: boolean
#### xplr.config.general.read_only
Set it to `true` to use only a subset of selected operations that forbids
executing commands or performing write operations on the file-system.
Type: boolean
#### xplr.config.general.enable_recover_mode
Set it to `true` if you want to enable a safety feature that will save you
from yourself when you type recklessly.
Type: boolean
#### xplr.config.general.hide_remaps_in_help_menu
Set it to `true` if you want to hide all remaps in the help menu.
Type: boolean
#### xplr.config.general.paginated_scrolling
Set it to `true` if you want paginated scrolling.
Type: boolean
#### xplr.config.general.scroll_padding
Set the padding value to the scroll area.
Only applicable when `xplr.config.general.paginated_scrolling = false`.
Type: boolean
#### xplr.config.general.enforce_bounded_index_navigation
Set it to `true` if you want the cursor to stay in the same position when
the focus is on the first path and you navigate to the previous path
(by pressing `up`/`k`), or when the focus is on the last path and you
navigate to the next path (by pressing `down`/`j`).
The default behavior is to rotate from the last/first path.
Type: boolean
#### xplr.config.general.prompt.format
This is the shape of the prompt for the input buffer.
Type: nullable string
#### xplr.config.general.prompt.style
This is the style of the prompt for the input buffer.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.logs.info.format
The string to indicate an information in logs.
Type: nullable string
#### xplr.config.general.logs.info.style
The style for the information logs.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.logs.success.format
The string to indicate an success in logs.
Type: nullable string
#### xplr.config.general.logs.success.style
The style for the success logs.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.logs.warning.format
The string to indicate an warnings in logs.
Type: nullable string
#### xplr.config.general.logs.warning.style
The style for the warnings logs.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.logs.error.format
The string to indicate an error in logs.
Type: nullable string
#### xplr.config.general.logs.error.style
The style for the error logs.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.header.cols
Columns to display in the table header.
Type: nullable list of tables with the following fields:
- format: nullable string
- style: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.header.style
Style of the table header.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.header.height
Height of the table header.
Type: nullable integer
#### xplr.config.general.table.row.cols
Columns to display in each row in the table.
Type: nullable list of tables with the following fields:
- format: nullable string
- style: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.row.style
Default style of the table.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.row.height
Height of the table rows.
Type: nullable integer
#### xplr.config.general.table.style
Default style of the table.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.tree
Tree to display in the table.
Type: nullable list of tables with the following fields:
- format: nullable string
- style: [Style](https://xplr.dev/en/style)
#### xplr.config.general.table.col_spacing
Spacing between the columns in the table.
Type: nullable integer
#### xplr.config.general.table.col_widths
Constraint for the column widths.
Type: nullable list of [Constraint](https://xplr.dev/en/layouts#constraint)
#### xplr.config.general.selection.item.format
Renderer for each item in the selection list.
Type: nullable string
#### xplr.config.general.selection.item.style
Style for each item in the selection list.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.search.algorithm
The default search algorithm
Type: [Search Algorithm](https://xplr.dev/en/searching#algorithm)
#### xplr.config.general.search.unordered
The default search ordering
Type: boolean
#### xplr.config.general.default_ui.prefix
The content that is placed before the item name for each row by default.
Type: nullable string
#### xplr.config.general.default_ui.suffix
The content which is appended to each item name for each row by default.
Type: nullable string
#### xplr.config.general.default_ui.style
The default style of each item for each row.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.focus_ui.prefix
The string placed before the item name for a focused row.
Type: nullable string
#### xplr.config.general.focus_ui.suffix
The string placed after the item name for a focused row.
Type: nullable string
#### xplr.config.general.focus_ui.style
Style for focused item.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.selection_ui.prefix
The string placed before the item name for a selected row.
Type: nullable string
#### xplr.config.general.selection_ui.suffix
The string placed after the item name for a selected row.
Type: nullable string
#### xplr.config.general.selection_ui.style
Style for selected rows.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.focus_selection_ui.prefix
The string placed before item name for a selected row that gets the focus.
Type: nullable string
#### xplr.config.general.focus_selection_ui.suffix
The string placed after the item name for a selected row that gets the focus.
Type: nullable string
#### xplr.config.general.focus_selection_ui.style
Style for a selected row that gets the focus.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.separator.format
The shape of the separator for the Sort & filter panel.
Type: nullable string
#### xplr.config.general.sort_and_filter_ui.separator.style
The style of the separator for the Sort & filter panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.default_identifier.format
The content of the default identifier in Sort & filter panel.
Type: nullable string
#### xplr.config.general.sort_and_filter_ui.default_identifier.style
Style for the default identifier in Sort & filter panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.forward.format
The shape of the forward direction indicator for sort identifiers in Sort & filter panel.
Type: nullable string
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.forward.style
Style of forward direction indicator in Sort & filter panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.reverse.format
The shape of the reverse direction indicator for sort identifiers in Sort & filter panel.
Type: nullable string
#### xplr.config.general.sort_and_filter_ui.sort_direction_identifiers.reverse.style
Style of reverse direction indicator in Sort & filter panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.sorter_identifiers
The identifiers used to denote applied sorters in the Sort & filter panel.
Type: nullable mapping of the following key-value pairs:
- key: [Sorter](https://xplr.dev/en/sorting#sorter)
- value:
- format: nullable string
- style: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.filter_identifiers
The identifiers used to denote applied filters in the Sort & filter panel.
Type: nullable mapping of the following key-value pairs:
- key: [Filter](https://xplr.dev/en/filtering#filter)
- value:
- format: nullable string
- style: [Style](https://xplr.dev/en/style)
#### xplr.config.general.sort_and_filter_ui.search_identifiers
The identifiers used to denote applied search input.
Type: { format = nullable string, style = [Style](https://xplr.dev/en/style) }
#### xplr.config.general.sort_and_filter_ui.search_direction_identifiers.ordered.format
The shape of ordered indicator for search ordering identifiers in Sort & filter panel.
Type: nullable string
#### xplr.config.general.sort_and_filter_ui.search_direction_identifiers.unordered.format
The shape of unordered indicator for search ordering identifiers in Sort & filter panel.
Type: nullable string
#### xplr.config.general.panel_ui.default.title.format
The content for panel title by default.
Type: nullable string
#### xplr.config.general.panel_ui.default.title.style
The style for panel title by default.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.default.style
Style of the panels by default.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.default.borders
Defines where to show borders for the panels by default.
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
#### xplr.config.general.panel_ui.default.border_type
Type of the borders by default.
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
#### xplr.config.general.panel_ui.default.border_style
Style of the panel borders by default.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.table.title.format
The content for the table panel title.
Type: nullable string
#### xplr.config.general.panel_ui.table.title.style
Style of the table panel title.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.table.style
Style of the table panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.table.borders
Defines where to show borders for the table panel.
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
#### xplr.config.general.panel_ui.table.border_type
Type of the borders for table panel.
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
#### xplr.config.general.panel_ui.table.border_style
Style of the table panel borders.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.help_menu.title.format
The content for the help menu panel title.
Type: nullable string
#### xplr.config.general.panel_ui.help_menu.title.style
Style of the help menu panel title.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.help_menu.style
Style of the help menu panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.help_menu.borders
Defines where to show borders for the help menu panel.
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
#### xplr.config.general.panel_ui.help_menu.border_type
Type of the borders for help menu panel.
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
#### xplr.config.general.panel_ui.help_menu.border_style
Style of the help menu panel borders.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.input_and_logs.title.format
The content for the input & logs panel title.
Type: nullable string
#### xplr.config.general.panel_ui.input_and_logs.title.style
Style of the input & logs panel title.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.input_and_logs.borders
#### xplr.config.general.panel_ui.input_and_logs.style
Style of the input & logs panel.
Type: [Style](https://xplr.dev/en/style)
Defines where to show borders for the input & logs panel.
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
#### xplr.config.general.panel_ui.input_and_logs.border_type
Type of the borders for input & logs panel.
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
#### xplr.config.general.panel_ui.input_and_logs.border_style
Style of the input & logs panel borders.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.selection.title.format
The content for the selection panel title.
Type: nullable string
#### xplr.config.general.panel_ui.selection.title.style
Style of the selection panel title.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.selection.borders
#### xplr.config.general.panel_ui.selection.style
Style of the selection panel.
Type: [Style](https://xplr.dev/en/style)
Defines where to show borders for the selection panel.
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
#### xplr.config.general.panel_ui.selection.border_type
Type of the borders for selection panel.
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
#### xplr.config.general.panel_ui.selection.border_style
Style of the selection panel borders.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.sort_and_filter.title.format
The content for the sort & filter panel title.
Type: nullable string
#### xplr.config.general.panel_ui.sort_and_filter.title.style
Style of the sort & filter panel title.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.sort_and_filter.style
Style of the sort & filter panel.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.panel_ui.sort_and_filter.borders
Defines where to show borders for the sort & filter panel.
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
#### xplr.config.general.panel_ui.sort_and_filter.border_type
Type of the borders for sort & filter panel.
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
#### xplr.config.general.panel_ui.sort_and_filter.border_style
Style of the sort & filter panel borders.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.general.initial_sorting
Initial group if sorters applied to the nodes list in the table.
Type: nullable list of [Node Sorter](https://xplr.dev/en/sorting#node-sorter-applicable)
#### xplr.config.general.initial_mode
The name of one of the modes to use when xplr loads.
This isn't the default mode. To modify the default mode, overwrite
[xplr.config.modes.builtin.default](https://xplr.dev/en/modes#xplrconfigmodesbuiltindefault).
Type: nullable string
#### xplr.config.general.initial_layout
The name of one of the layouts to use when xplr loads.
This isn't the default layout. To modify the default layout, overwrite
[xplr.config.layouts.builtin.default](https://xplr.dev/en/layouts#xplrconfiglayoutsbuiltindefault).
Type: nullable string
#### xplr.config.general.start_fifo
Set it to a file path to start fifo when xplr loads.
Generally it is used to integrate with external tools like previewers.
Type: nullable string
#### xplr.config.general.global_key_bindings
Use it to define a set of key bindings that are available by default in
every [mode](https://xplr.dev/en/mode). They can be overwritten.
Type: [Key Bindings](https://xplr.dev/en/configure-key-bindings#key-bindings)

@ -0,0 +1,27 @@
# Input Operation
Cursor based input operation is a [sum type][3] can be one of the following:
- { SetCursor = int }
- { InsertCharacter = str }
- "GoToPreviousCharacter"
- "GoToNextCharacter"
- "GoToPreviousWord"
- "GoToNextWord"
- "GoToStart"
- "GoToEnd"
- "DeletePreviousCharacter"
- "DeleteNextCharacter"
- "DeletePreviousWord"
- "DeleteNextWord"
- "DeleteLine"
- "DeleteTillEnd"
## Also See:
- [Message][1]
- [Full List of Messages][2]
[1]: message.md
[2]: messages.md
[3]: sum-type.md

@ -0,0 +1,269 @@
# Try in Docker
If you prefer to try it before installing, here's the snippet for your
convenience.
```bash
docker run -w / -it --rm ubuntu sh -uec '
apt-get update -y
apt-get install -y wget tar vim less
wget https://github.com/sayanarijit/xplr/releases/latest/download/xplr-linux.tar.gz
tar -xzvf xplr-linux.tar.gz
./xplr
'
```
# Install
You can install xplr using one of the following ways. Each has their own
advantages and limitations.
For example, the [Direct Download][1], [From crates.io][2], and
[Build From Source][3] methods allow the users to install the latest possible
version of xplr, but they have one common drawback - the user will need to keep
an eye on the releases, and manually upgrade xplr when a new version is
available.
One way to keep an eye on the releases is to [watch the repository][4].
## Community Maintained Repositories
xplr can be installed from one of the following community maintained
repositories:
[![packaging status][5]][6]
### Cross-platform
#### [Nixpkgs][10]
```
nix-env -f https://github.com/NixOS/nixpkgs/tarball/master -iA xplr
```
Or
```nix
# configuration.nix or darwin-configuration.nix
environment.systemPackages = with nixpkgs; [
xplr
# ...
];
```
#### [Home Manager][30]
```nix
# home.nix
home.packages = with nixpkgs; [
xplr
# ...
];
```
Or
```nix
# home.nix
programs.xplr = {
enable = true;
# Optional params:
plugins = {
tree-view = fetchFromGitHub {
owner = "sayanarijit";
repo = "tree-view.xplr";
};
local-plugin = "/home/user/.config/xplr/plugins/local-plugin";
};
extraConfig = ''
require("tree-view").setup()
require("local-plugin").setup()
'';
};
```
### Arch Linux
(same for Manjaro Linux)
#### [Official Community Repo][7]
```
sudo pacman -S xplr
```
#### [AUR][8]
Git version:
```
paru -S xplr-git
```
### Alpine Linux
#### [Edge Testing Repo][27]
```
# Add the following line in /etc/apk/repositories:
# https://dl-cdn.alpinelinux.org/alpine/edge/testing
apk add xplr bash less
```
### Void Linux
#### [void-templates by shubham][9]
### Gentoo
#### [Overlay GURU][28]
### macOS
Make sure you have the latest version of [GNU core utilities][29] installed.
#### [MacPorts][11]
```
sudo port selfupdate
sudo port install xplr
```
#### [Homebrew][12]
Stable branch:
```
brew install xplr
```
HEAD branch:
```
brew install --head xplr
```
### FreeBSD
#### [ports][13]
```
pkg install xplr
```
Or
```
cd /usr/ports/misc/xplr
make install clean
```
### NetBSD
#### [pkgsrc][14]
```
pkgin install xplr
```
Or
```
cd /usr/pkgsrc/sysutils/xplr
make install
```
## Direct Download
One can directly download the standalone binary from the
[releases][15].
Currently, the following options are available for direct download:
- [GNU/Linux][16]
- [Linux musl][26]
- [macOS][17]
Command-line instructions:
```bash
platform="linux" # or "macos" / "linux-musl"
# Download
wget https://github.com/sayanarijit/xplr/releases/latest/download/xplr-$platform.tar.gz
# Extract
tar xzvf xplr-$platform.tar.gz
# Place in $PATH
sudo mv xplr /usr/local/bin/
```
## From [crates.io][18]
Prerequisites:
- [Rust toolchain][19],
- [gcc][20]
- [make][21]
Command-line instructions:
```bash
cargo install --locked --force xplr
```
## Build From Source
Prerequisites:
- [git][22]
- [Rust toolchain][19]
- [gcc][20]
- [make][21]
Command-line instructions:
```bash
# Clone the repository
git clone https://github.com/sayanarijit/xplr.git
cd xplr
# Build
cargo build --locked --release --bin xplr
# Place in $PATH
sudo cp target/release/xplr /usr/local/bin/
```
[1]: #direct-download
[2]: #from-cratesio
[3]: #build-from-source
[4]: https://github.com/sayanarijit/xplr/watchers
[5]: https://repology.org/badge/vertical-allrepos/xplr.svg
[6]: https://repology.org/project/xplr/versions
[7]: https://archlinux.org/packages/extra/x86_64/xplr
[8]: https://aur.archlinux.org/packages/?O=0&SeB=n&K=xplr&outdated=&SB=n&SO=a&PP=50&do_Search=Go
[9]: https://github.com/shubham-cpp/void-pkg-templates
[10]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/xp/xplr/package.nix
[11]: https://ports.macports.org/port/xplr
[12]: https://formulae.brew.sh/formula/xplr
[13]: https://cgit.freebsd.org/ports/plain/misc/xplr/
[14]: https://pkgsrc.se/sysutils/xplr
[15]: https://github.com/sayanarijit/xplr/releases
[16]: https://github.com/sayanarijit/xplr/releases/latest/download/xplr-linux.tar.gz
[17]: https://github.com/sayanarijit/xplr/releases/latest/download/xplr-macos.tar.gz
[18]: https://crates.io/crates/xplr
[19]: https://www.rust-lang.org/tools/install
[20]: https://gcc.gnu.org/
[21]: https://www.gnu.org/software/make/
[22]: https://git-scm.com/
[23]: https://github.com/sayanarijit/xplr/assets/11632726/3b61e8c8-76f0-48e8-8734-50e9e7e495b7
[25]: https://gifyu.com/image/tF2D
[26]: https://github.com/sayanarijit/xplr/releases/latest/download/xplr-linux-musl.tar.gz
[27]: https://pkgs.alpinelinux.org/packages?name=xplr
[28]: https://gpo.zugaina.org/Overlays/guru/app-misc/xplr
[29]: https://formulae.brew.sh/formula/coreutils
[30]: https://github.com/nix-community/home-manager/blob/master/modules/programs/xplr.nix

@ -0,0 +1,61 @@
# Installing Plugins
One way to install plugins is to use a plugin manager like [dtomvan/xpm.xplr][1].
But you can also install and manage plugins manually.
## Install Manually
- Add the following line in `~/.config/xplr/init.lua`
```lua
local home = os.getenv("HOME")
package.path = home
.. "/.config/xplr/plugins/?/init.lua;"
.. home
.. "/.config/xplr/plugins/?.lua;"
.. package.path
```
- Clone the plugin
```bash
mkdir -p ~/.config/xplr/plugins
git clone https://github.com/sayanarijit/material-landscape2.xplr ~/.config/xplr/plugins/material-landscape2
```
- Require the module in `~/.config/xplr/init.lua`
```lua
require("material-landscape2").setup()
-- The setup arguments might differ for different plugins.
-- Visit the project README for setup instructions.
```
## Luarocks Support
Some plugins may require [luarocks][2] to work.
Setup luarocks with the following steps:
- Install luarocks (via your package managers or follow the [official guide][2]).
- Add `eval "$(luarocks path --lua-version 5.1)"` in your `.bashrc` or `.zshrc`.
- Add the following lines in `~/.config/xplr/init.lua`
```lua
package.path = os.getenv("LUA_PATH") .. ";" .. package.path
package.cpath = os.getenv("LUA_CPATH") .. ";" .. package.cpath
```
Now you can install packages using luarocks. Be sure to append `--lua-version`.
Example:
```bash
luarocks install luafilesystem --local --lua-version 5.1
```
[1]: https://github.com/dtomvan/xpm.xplr
[2]: https://luarocks.org

@ -0,0 +1,8 @@
# Integration
xplr is designed to integrate well with other tools and commands. It can be
used as a file picker or a pluggable file manager.
- [Awesome Integrations][1]
[1]: awesome-integrations.md

@ -0,0 +1,107 @@
# Introduction
xplr is a terminal UI based file explorer that aims to increase our terminal
productivity by being a flexible, interactive orchestrator for the ever growing
awesome command-line utilities that work with the file-system.
To achieve its goal, xplr strives to be a fast, minimal and more importantly,
hackable file explorer.
xplr is not meant to be a replacement for the standard shell commands or the
GUI file managers. Rather, it aims to [integrate them all][14] and expose an
intuitive, scriptable, [keyboard controlled][2],
[real-time visual interface][1], also being an ideal candidate for [further
integration][15], enabling you to achieve insane terminal productivity.
## Concept
### Hackable
xplr is built with configurability in mind. So it allows you to perform a vast
set of operations and make it look and behave just the way you want.
A few things you can do with the xplr configuration
- [Hacks][16]
- [Plugins][3]
- [Integrations][15]
### Fast
Although speed is not the primary concern, xplr is already fast enough so that
you can take it out for a walk into your `node_modules` or `/nix/store` any
time you want, and it will only get faster. Still, if you feel like it's
somehow making you slow, just report it. Most probably we're just waiting for
someone to complain.
**Tip:** A quick and easy way to optimize the UI rendering is reducing the
number of columns in the table.
### Minimal
xplr is being referred to as a _File Explorer_, not a _File Manager_. This
is because at the core, xplr is only an explorer, and [outsources][18] the file
management operations to external commands. This helps xplr stay minimal, and
focus only on doing what it does best.
So, just like speed, minimalism isn't as as aggressively pursued as
hackability. xplr simply prefers to stay minimal and looks for the opportunity
to lose some kb if it makes sense.
## Features
Some of the coolest features xplr provide beside the basic stuff:
- [Embedded LuaJIT][5] for portability and extensibility.
- [A simple modal system based on message passing][10] to control xplr session
using:
- [Keyboard inputs][11]
- [Shell Commands][12]
- [Lua Functions][13]
- [Hooks][22]
- Easy, typesafe message passing with `-m MSG` or `-M MSG` subcommands.
- [Readline-like input buffer][9] with customizable behavior to read user
inputs.
- [Switchable recover mode][7] that saves you from doing unwanted things when
in a hurry.
- [Customizable layouts][1] with built-in panels. For e.g.
- **Selection list** to show you the selected paths in real-time.
- **Help menu** to show you the available keys bindings in each mode.
- **Input & logs** to read input and display logs.
- **Filter and sort pipeline** to show you the applied filters and sorters.
- [Custom file properties][17] with custom colors can be displayed in the table.
- [FIFO manager][19] to manage a FIFO file that can be used to
[integrate with previewers][6].
- [Virtual root][21] with `--vroot` and `:v` key bindings.
- **Different quit options:**
- Quit with success without any output (`q`).
- Quit with success and the result printed on stdout (`enter`).
- Quit with success and the present working directory printed on stdout
(`:` `q` `p`).
- Quit with success and the path under focus printed on stdout
(`:` `q` `f`).
- Quit with success and the selection printed on stdout
(`:` `q` `s`).
- Quit with failure (`ctrl-c`).
[1]: layouts.md
[2]: configure-key-bindings.md
[3]: awesome-plugins.md
[4]: https://github.com/sayanarijit/xplr/tree/main/benches
[5]: https://github.com/sayanarijit/xplr/discussions/183
[6]: https://github.com/sayanarijit/xplr/pull/229
[7]: modes.md#xplrconfigmodesbuiltinrecover
[8]: default-key-bindings.md
[9]: https://github.com/sayanarijit/xplr/pull/397
[10]: messages.md
[11]: configure-key-bindings.md
[12]: mode.md#input-pipe
[13]: lua-function-calls.md
[14]: awesome-plugins.md#integration
[15]: awesome-integrations.md
[16]: awesome-hacks.md
[17]: node_types.md
[18]: https://github.com/sayanarijit/xplr/blob/main/src/init.lua
[19]: messages.md#startfifo
[21]: messages.md#virtual-root
[22]: configuration.md#hooks

@ -0,0 +1,16 @@
# Key Bindings
Key bindings define how each keyboard input will be handled while in a specific
[mode][4].
See the [Default key bindings][1] for example.
To configure or work with key bindings, visit [Configure Key Bindings][2].
In case you need help debugging key bindings or to understand the system DYI
way, refer to the [Debug Key Bindings][3] guide.
[1]: default-key-bindings.md
[2]: configure-key-bindings.md
[3]: debug-key-bindings.md
[4]: modes.md#mode

@ -0,0 +1,597 @@
# Layout
#### Example: Defining Custom Layout
```lua
xplr.config.layouts.builtin.default = {
Horizontal = {
config = {
margin = 1,
horizontal_margin = 1,
vertical_margin = 1,
constraints = {
{ Percentage = 50 },
{ Percentage = 50 },
}
},
splits = {
"Table",
"HelpMenu",
}
}
}
```
Result:
```
╭ /home ─────────────╮╭ Help [default] ────╮
│ ╭─── path ││. show hidden │
│ ├▸[ð Desktop/] ││/ search │
│ ├ ð Documents/ ││: action │
│ ├ ð Downloads/ ││? global help │
│ ├ ð GitHub/ ││G go to bottom │
│ ├ ð Music/ ││V select/unselect│
│ ├ ð Pictures/ ││ctrl duplicate as │
│ ├ ð Public/ ││ctrl next visit │
╰────────────────────╯╰────────────────────╯
```
A layout is a [sum type][56] can be one of the following:
- [Nothing][8]
- [Table][9]
- [InputAndLogs][10]
- [Selection][11]
- [HelpMenu][12]
- [SortAndFilter][13]
- [Static][25]
- [Dynamic][26]
- [Horizontal][14]
- [Vertical][16]
- CustomContent (deprecated, use `Static` or `Dynamic`)
### Nothing
This layout contains a blank panel.
Type: "Nothing"
### Table
This layout contains the table displaying the files and directories in the current
directory.
### InputAndLogs
This layout contains the panel displaying the input prompt and logs.
Type: "InputAndLogs"
### Selection
This layout contains the panel displaying the selected paths.
Type: "Selection"
### HelpMenu
This layout contains the panel displaying the help menu for the current mode in
real-time.
Type: "HelpMenu"
### SortAndFilter
This layout contains the panel displaying the pipeline of sorters and filters applied on
the list of paths being displayed.
Type: "SortAndFilter"
### Static
This is a custom layout to render static content.
Type: { Static = [Custom Panel][27] }
### Dynamic
This is a custom layout to render dynamic content using a function defined in
[xplr.fn][28] that takes [Content Renderer Argument][36] and returns [Custom Panel][27].
Type: { Dynamic = "[Content Renderer][35]" }
### Horizontal
This is a special layout that splits the panel into two horizontal parts.
It contains the following information:
- [config][15]
- [splits][17]
Type: { Vertical = { config = [Layout Config][15], splits = { [Layout][17], ... } }
### Vertical
This is a special layout that splits the panel into two vertical parts.
It contains the following information:
- [config][15]
- [splits][17]
Type: { Vertical = { config = [Layout Config][15], splits = { [Layout][17], ... } }
## Layout Config
A layout config contains the following information:
- [margin][18]
- [horizontal_margin][19]
- [vertical_margin][20]
- [constraints][21]
### margin
Type: nullable integer
The width of the margin in all direction.
### horizontal_Margin
Type: nullable integer
The width of the horizontal margins. Overwrites the [margin][18] value.
### vertical_Margin
Type: nullable integer
The width of the vertical margins. Overwrites the [margin][18] value.
### constraints
Type: nullable list of [Constraint][22]
The constraints applied on the layout.
## Constraint
A constraint is a [sum type][56] can be one of the following:
- { Percentage = int }
- { Ratio = { int, int } }
- { Length = { int }
- { LengthLessThanScreenHeight = int }
- { LengthLessThanScreenWidth = int }
- { LengthLessThanLayoutHeight = int }
- { LengthLessThanLayoutWidth = int }
- { Max = int }
- { MaxLessThanScreenHeight = int }
- { MaxLessThanScreenWidth = int }
- { MaxLessThanLayoutHeight = int }
- { MaxLessThanLayoutWidth = int }
- { Min = int }
- { MinLessThanScreenHeight = int }
- { MinLessThanScreenWidth = int }
- { MinLessThanLayoutHeight = int }
- { MinLessThanLayoutWidth = int }
## splits
Type: list of [Layout][3]
The list of child layouts to fit into the parent layout.
## Custom Panel
Custom panel is a [sum type][56] can be one of the following:
- [CustomParagraph][29]
- [CustomList][30]
- [CustomTable][31]
- [CustomLayout][55]
### CustomParagraph
A paragraph to render. It contains the following fields:
- **ui** (nullable [Panel UI Config][32]): Optional UI config for the panel.
- **body** (string): The string to render.
#### Example: Render a custom static paragraph
```lua
xplr.config.layouts.builtin.default = {
Static = {
CustomParagraph = {
ui = { title = { format = " custom title " } },
body = "custom body",
},
},
}
```
Result:
```
╭ custom title ────────╮
│custom body │
│ │
│ │
╰──────────────────────╯
```
#### Example: Render a custom dynamic paragraph
```lua
xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }
xplr.fn.custom.render_layout = function(ctx)
return {
CustomParagraph = {
ui = { title = { format = ctx.app.pwd } },
body = xplr.util.to_yaml(ctx.app.focused_node),
},
}
end
```
Result:
```
╭/home/sayanarijit───────────────────────────╮
│mime_essence: inode/directory │
│relative_path: Desktop │
│is_symlink: false │
│is_readonly: false │
│parent: /home/sayanarijit │
│absolute_path: /home/sayanarijit/Desktop │
│is_broken: false │
│created: 1668087850396758714 │
│size: 4096 │
│gid: 100 │
╰────────────────────────────────────────────╯
```
### CustomList
A list to render. It contains the following fields:
- **ui** (nullable [Panel UI Config][32]): Optional UI config for the panel.
- **body** (list of string): The list of strings to display.
#### Example: Render a custom static list
```lua
xplr.config.layouts.builtin.default = {
Static = {
CustomList = {
ui = { title = { format = " custom title " } },
body = { "1", "2", "3" },
},
},
}
```
Result:
```
╭ custom title ─────────────╮
│1 │
│2 │
│3 │
│ │
╰───────────────────────────╯
```
#### Example: Render a custom dynamic list
```lua
xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }
xplr.fn.custom.render_layout = function(ctx)
return {
CustomList = {
ui = { title = { format = ctx.app.pwd } },
body = {
(ctx.app.focused_node or {}).relative_path or "",
ctx.app.version,
tostring(ctx.app.pid),
},
},
}
end
```
Result:
```
╭/home/sayanarijit──────────╮
│Desktop │
│0.21.2 │
│17336 │
│ │
│ │
╰───────────────────────────╯
```
## CustomTable
A custom table to render. It contains the following fields:
- **ui** (nullable [Panel UI Config][32]): Optional UI config for the panel.
- **widths** (list of [Constraint][22]): Width of the columns.
- **col_spacing** (nullable int): Spacing between columns. Defaults to 1.
- **body** (list of list of string): The rows and columns to render.
#### Example: Render a custom static table
```lua
xplr.config.layouts.builtin.default = {
Static = {
CustomTable = {
ui = { title = { format = " custom title " } },
widths = {
{ Percentage = 50 },
{ Percentage = 50 },
},
body = {
{ "a", "b" },
{ "c", "d" },
},
},
},
}
```
Result:
```
╭ custom title ────────────────────╮
│a b │
│c d │
│ │
│ │
│ │
╰──────────────────────────────────╯
```
#### Example: Render a custom dynamic table
```lua
xplr.config.layouts.builtin.default = {Dynamic = "custom.render_layout" }
xplr.fn.custom.render_layout = function(ctx)
return {
CustomTable = {
ui = { title = { format = ctx.app.pwd } },
widths = {
{ Percentage = 50 },
{ Percentage = 50 },
},
body = {
{ "", "" },
{ "Layout height", tostring(ctx.layout_size.height) },
{ "Layout width", tostring(ctx.layout_size.width) },
{ "", "" },
{ "Screen height", tostring(ctx.screen_size.height) },
{ "Screen width", tostring(ctx.screen_size.width) },
},
},
}
end
```
Result:
```
╭/home/sayanarijit───────────────────────────╮
│ │
│Layout height 12 │
│Layout width 46 │
│ │
│Screen height 12 │
│Screen width 46 │
│ │
│ │
│ │
│ │
╰────────────────────────────────────────────╯
```
### CustomLayout
A whole custom layout to render. It doesn't make sense to use it as a
[Static][25] layout, but can be very useful to render as a [Dynamic][26] layout
for use cases where the structure of the layout needs to change without having
to switch modes.
> WARNING: Rendering the same dynamic custom layout recursively will result in
> a ugly crash.
#### Example: Render a custom dynamic layout
```lua
xplr.config.layouts.builtin.default = { Dynamic = "custom.render_layout" }
xplr.fn.custom.render_layout = function(ctx)
local inner = {
config = {
constraints = {
{ Percentage = 50 },
{ Percentage = 50 },
},
},
splits = {
{ Static = { CustomParagraph = { body = "Try your luck..." } } },
{ Static = { CustomParagraph = { body = "Press ctrl-r" } } },
},
}
local layout_type = "Vertical"
if math.random(1, 2) == 1 then
layout_type = "Horizontal"
end
return { CustomLayout = { [layout_type] = inner } }
end
```
Result:
```
╭─────────────────────╮╭─────────────────────╮
│Try your luck... ││Press ctrl-r │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
│ ││ │
╰─────────────────────╯╰─────────────────────╯
```
Or
```
╭────────────────────────────────────────────╮
│Try your luck... │
│ │
│ │
│ │
╰────────────────────────────────────────────╯
╭────────────────────────────────────────────╮
│Press ctrl-r │
│ │
│ │
│ │
╰────────────────────────────────────────────╯
```
## Panel UI Config
It contains the following optional fields:
- **title** ({ format = "string", style = [Style][33] }): the title of the panel.
- **style** ([Style][33]): The style of the panel body.
- **borders** (nullable list of [Border][34]): The shape of the borders.
- **border_type** ([Border Type][54]): The type of the borders.
- **border_style** ([Style][33]): The style of the borders.
## Content Renderer
It is a Lua function that receives [a special argument][36] as input and
returns some output that can be rendered in the UI. It is used to render
content body for the custom dynamic layouts.
## Content Renderer Argument
It contains the following information:
- [layout_size][37]
- [screen_size][37]
- [scrolltop][57]
- [app][38]
### Size
It contains the following information:
- x
- y
- height
- width
Every field is of integer type.
### scrolltop
Type: integer
The start index of the visible nodes in the table.
### app
This is a lightweight version of the [Lua Context][39]. In this context, the
heavyweight fields like [directory_buffer][50] are omitted for performance
reasons.
Hence, only the following fields are available.
- [version][40]
- [pwd][41]
- [initial_pwd][53]
- [vroot][52]
- [focused_node][42]
- [selection][43]
- [mode][44]
- [layout][45]
- [input_buffer][46]
- [pid][47]
- [session_path][48]
- [explorer_config][49]
## Also See:
- [xplr.config.layouts][51]
[1]: #builtin
[2]: #custom
[3]: #layout
[4]: #default
[5]: #no_help
[6]: #no_selection
[7]: #no_help_no_selection
[8]: #nothing
[9]: #table
[10]: #inputandlogs
[11]: #selection
[12]: #helpmenu
[13]: #sortandfilter
[14]: #horizontal
[15]: #layout-config
[16]: #vertical
[17]: #splits
[18]: #margin
[19]: #horizontal_margin
[20]: #vertical_margin
[21]: #constraints
[22]: #constraint
[25]: #static
[26]: #dynamic
[27]: #custom-panel
[28]: configuration.md#function
[29]: #customparagraph
[30]: #customlist
[31]: #customtable
[32]: #panel-ui-config
[33]: style.md#style
[34]: borders.md#border
[35]: #content-renderer
[36]: #content-renderer-argument
[37]: #size
[38]: #app
[39]: lua-function-calls.md#lua-context
[40]: lua-function-calls.md#version
[41]: lua-function-calls.md#pwd
[42]: lua-function-calls.md#focused_node
[43]: lua-function-calls.md#selection
[44]: lua-function-calls.md#mode
[45]: lua-function-calls.md#layout
[46]: lua-function-calls.md#input_buffer
[47]: lua-function-calls.md#pid
[48]: lua-function-calls.md#session_path
[49]: lua-function-calls.md#explorer_config
[50]: lua-function-calls.md#directory_buffer
[51]: layouts.md
[52]: lua-function-calls.md#vroot
[53]: lua-function-calls.md#initial_pwd
[54]: borders.md#border-type
[55]: #customlayout
[56]: sum-type.md
[57]: #scrolltop

@ -0,0 +1,88 @@
### Layouts
xplr layouts define the structure of the UI, i.e. how many panel we see,
placement and size of the panels, how they look etc.
This is configuration exposed via the `xplr.config.layouts` API.
`xplr.config.layouts.builtin` contain some built-in panels which can be
overridden, but you can't add or remove panels in it.
You can add new panels in `xplr.config.layouts.custom`.
##### Example: Defining Custom Layout
```lua
xplr.config.layouts.builtin.default = {
Horizontal = {
config = {
margin = 1,
horizontal_margin = 1,
vertical_margin = 1,
constraints = {
{ Percentage = 50 },
{ Percentage = 50 },
}
},
splits = {
"Table",
"HelpMenu",
}
}
}
```
Result:
```
╭ /home ─────────────╮╭ Help [default] ────╮
│ ╭─── path ││. show hidden │
│ ├▸[ð Desktop/] ││/ search │
│ ├ ð Documents/ ││: action │
│ ├ ð Downloads/ ││? global help │
│ ├ ð GitHub/ ││G go to bottom │
│ ├ ð Music/ ││V select/unselect│
│ ├ ð Pictures/ ││ctrl duplicate as │
│ ├ ð Public/ ││ctrl next visit │
╰────────────────────╯╰────────────────────╯
```
#### xplr.config.layouts.builtin.default
The default layout
Type: [Layout](https://xplr.dev/en/layout)
#### xplr.config.layouts.builtin.no_help
The layout without help menu
Type: [Layout](https://xplr.dev/en/layout)
#### xplr.config.layouts.builtin.no_selection
The layout without selection panel
Type: [Layout](https://xplr.dev/en/layout)
#### xplr.config.layouts.builtin.no_help_no_selection
The layout without help menu and selection panel
Type: [Layout](https://xplr.dev/en/layout)
#### xplr.config.layouts.custom
This is where you can define custom layouts
Type: mapping of the following key-value pairs:
- key: string
- value: [Layout](https://xplr.dev/en/layout)
Example:
```lua
xplr.config.layouts.custom.example = "Nothing" -- Show a blank screen
xplr.config.general.initial_layout = "example" -- Load the example layout
```

@ -0,0 +1,444 @@
# Lua Function Calls
xplr allows you to define lua functions using the `xplr.fn.custom` Lua API.
These functions can be called using messages like `CallLua`, `CallLuaSilently`.
When called the function receives a [special argument][14] that
contains some useful information. The function can optionally return a list of
messages which will be handled by xplr.
## Example: Using Lua Function Calls
```lua
-- Define the function
xplr.fn.custom.ask_name_and_greet = function(app)
print("What's your name?")
local name = io.read()
local greeting = "Hello " .. name .. "!"
local message = greeting .. " You are inside " .. app.pwd
return {
{ LogSuccess = message },
}
end
-- Map the function to a key (space)
xplr.config.modes.builtin.default.key_bindings.on_key.space = {
help = "ask name and greet",
messages = {
{ CallLua = "custom.ask_name_and_greet" }
}
}
-- Now, when you press "space" in default mode, you will be prompted for your
-- name. Enter your name to receive a nice greeting and to know your location.
```
Visit the [xplr.util][85] API docs for some useful utility / helper functions
that you can use in your Lua function calls.
## Lua Context
This is a special argument passed to the lua functions when called using the
`CallLua`, `CallLuaSilently` messages.
It contains the following information:
- [version][29]
- [pwd][31]
- [initial_pwd][76]
- [vroot][75]
- [focused_node][32]
- [directory_buffer][33]
- [selection][34]
- [mode][35]
- [layout][36]
- [input_buffer][37]
- [pid][38]
- [session_path][39]
- [explorer_config][40]
- [history][41]
- [last_modes][42]
### version
Type: string
xplr version. Can be used to test compatibility.
### pwd
Type: string
The present working directory.
### initial_pwd
Type: string
The initial working directory when xplr started.
### vroot
Type: nullable string
The current virtual root.
### focused_node
Type: nullable [Node][44]
The node under focus.
### directory_buffer
Type: nullable [Directory Buffer][62]
The directory buffer being rendered.
### selection
Type: list of selected [Node][44]s
The selected nodes.
### mode
Type: [Mode][8]
Current mode.
### layout
Type: [Layout][11]
Current layout.
### input_buffer
Type: nullable string
The input buffer.
### pid
Type: integer
The xplr session PID.
### session_path
Type: string
The session path.
### explorer_config
Type: [Explorer Config][66]
The configuration for exploring paths.
### history
Type: [History][70]
### last_modes
Type: list of [Mode][8]
Last modes, not popped yet.
## Node
A node contains the following fields:
- [parent][45]
- [relative_path][46]
- [absolute_path][47]
- [extension][48]
- [is_symlink][49]
- [is_broken][50]
- [is_dir][51]
- [is_file][52]
- [is_readonly][53]
- [mime_essence][54]
- [size][55]
- [human_size][56]
- [permissions][57]
- [created][71]
- [last_modified][72]
- [uid][73]
- [gid][74]
- [canonical][58]
- [symlink][59]
### parent
Type: string
The parent path of the node.
### relative_path
Type: string
The path relative to the parent, i.e. the file/directory name with extension.
### absolute_path
Type: string
The absolute path (without resolving symlinks) of the node.
### extension
Type: string
The extension of the node.
### is_symlink
Type: boolean
`true` if the node is a symlink.
### is_broken
Type: boolean
`true` if the node is a broken symlink.
### is_dir
Type: boolean
`true` if the node is a directory.
### is_file
Type: boolean
`true` if the node is a file.
### is_readonly
Type: boolean
`true` if the node is real-only.
### mime_essence
Type: string
The mime type of the node. For e.g. `text/csv`, `image/jpeg` etc.
### size
Type: integer
The size of the exact node. The size of a directory won't be calculated
recursively.
### human_size
Type: string
Like size but in human readable format.
### permissions
Type: [Permission][60]
The [permissions][60] applied to the node.
### created
Type: nullable integer
Creation time in nanosecond since UNIX epoch.
### last_modified
Type: nullable integer
Last modification time in nanosecond since UNIX epoch.
### uid
Type: integer
User ID of the file owner.
### gid
Type: integer
Group ID of the file owner.
### canonical
Type: nullable [Resolved Node Metadata][61]
If the node is a symlink, it will hold information about the symlink resolved
node. Else, it will hold information the actual node. It the symlink is broken,
it will be null.
### symlink
Type: nullable [Resolved Node Metadata][61]
If the node is a symlink and is not broken, it will hold information about the
symlink resolved node. However, it will never hold information about the actual
node. It will instead be null.
## Directory Buffer
Directory buffer contains the following fields:
- [parent][45]
- [nodes][63]
- [total][64]
- [focus][65]
### parent
Type: string
The parent path of the node.
### nodes
Type: list of [Node][44]s
A list of visible nodes.
### total
Type: int
The count of nodes being rendered.
### focus
Type: int
The index of the node under focus. It can be `0` even when there's no node to
focus on.
## History
History contains the following fields:
- [loc][68]
- [paths][69]
### loc
Type: int
Location of the current path in history.
### paths
Type: list of string
Visited paths.
## Explorer Config
Explorer config contains the following fields:
- [filters][77]
- [sorters][78]
- [searcher][79]
### filters
List of filters to apply.
Type: list of [Node Filter Applicable][80]
### sorters
Add list or sorters to the pipeline.
Type: list of [Node Sorter Applicable][81]
### searcher
The searcher to use (if any).
Type: nullable [Node Searcher Applicable][82]
## Also See:
- [xplr.util][85]
[7]: https://www.json.org
[8]: modes.md#mode
[9]: modes.md#builtin
[10]: modes.md#custom
[11]: layouts.md
[12]: layouts.md#builtin
[13]: layouts.md#custom
[14]: #lua-context
[15]: filtering.md#filter
[16]: filtering.md
[17]: sorting.md#sorter
[29]: #version
[30]: #config
[31]: #pwd
[32]: #focused_node
[33]: #directory_buffer
[34]: #selection
[35]: #mode
[36]: #layout
[37]: #input_buffer
[38]: #pid
[39]: #session_path
[40]: #explorer_config
[41]: #history
[42]: #last_modes
[43]: configuration.md#config
[44]: #node
[45]: #parent
[46]: #relative_path
[47]: #absolute_path
[48]: #extension
[49]: #is_symlink
[50]: #is_broken
[51]: #is_dir
[52]: #is_file
[53]: #is_readonly
[54]: #mime_essence
[55]: #size
[56]: #human_size
[57]: #permissions
[58]: #canonical
[59]: #symlink
[60]: column-renderer.md#permission
[61]: column-renderer.md#resolved-node-metadata
[62]: #directory-buffer
[63]: #nodes
[64]: #total
[65]: #focus
[66]: #explorer-config
[67]: #history
[68]: #loc
[69]: #paths
[70]: #history-1
[71]: #created
[72]: #last_modified
[73]: #uid
[74]: #gid
[75]: #vroot
[76]: #initial_pwd
[77]: #filters
[78]: #sorters
[79]: #searcher
[80]: filtering.md#node-filter-applicable
[81]: sorting.md#node-sorter-applicable
[82]: searching.md#node-searcher-applicable
[85]: xplr.util.md

@ -0,0 +1,59 @@
# Message
You can think of xplr as a server. Just like web servers listen to HTTP
requests, xplr listens to messages.
A message is a [sum type][9] that can have [these possible values][1].
You can send these messages to an xplr session in the following ways:
- Via command-line (currently during start-up only, using `--on-load`)
- Via [key bindings][2]
- Via [Lua function calls][3]
- Via shell command using the [input pipe][4]
- Via socket (coming soon)
### Format
To send messages using the [key bindings][2] or [Lua function calls][3],
messages are represented in [Lua][5] syntax.
For example:
- `"Quit"`
- `{ FocusPath = "/path/to/file" }`
- `{ Call = { command = "bash", args = { "-c", "read -p test" } } }`
However, to send messages using the [input pipe][4], they need to be
represented using [YAML][6] (or [JSON][7]) syntax.
For example:
- `Quit`
- `FocusPath: "/path/to/file"`
- `Call: { command: bash, args: ["-c", "read -p test"] }`
Use `"$XPLR" -m TEMPLATE [VALUE]...` command-line option to safely format
`TEMPLATE` into a valid message. If uses [jf][8] to parse and render the
template. And `$XPLR` (rather than `xplr`) makes sure that the correct version
of the binary is being used.
For example:
- `"$XPLR" -m Quit`
- `"$XPLR" -m 'FocusPath: %q' "/path/to/file"`
- `"$XPLR" -m 'Call: { command: %q, args: [%*q] }' bash -c "read -p test"`
## Also See:
- [Full List of Messages][1]
[1]: messages.md
[2]: key-bindings.md
[3]: lua-function-calls.md
[4]: environment-variables-and-pipes.md#input-pipe
[5]: https://www.lua.org/
[6]: http://yaml.org/
[7]: https://www.json.org
[8]: https://github.com/sayanarijit/jf
[9]: sum-type.md

File diff suppressed because it is too large Load Diff

@ -0,0 +1,66 @@
# Mode
A mode contains the following information:
- [name][5]
- [help][6]
- [extra_help][7]
- [key_bindings][9]
- [layout][10]
- [prompt][13]
### name
Type: string
This is the name of the mode visible in the help menu.
### help
Type: nullable string
If specified, the help menu will display this instead of the auto generated
mappings.
### extra_help
Type: nullable string
If specified, the help menu will display this along-side the auto generated
help menu.
### key_bindings
Type: [Key Bindings][8]
The key bindings available in that mode.
### layout
Type: nullable [Layout][11]
If specified, this layout will be used to render the UI.
### prompt
Type: nullable string
If set, this prompt will be displayed in the input buffer when in this mode.
## Also See:
- [xplr.config.modes][12]
[1]: #builtin
[2]: #custom
[3]: #mode
[4]: default-key-bindings.md
[5]: #name
[6]: #help
[7]: #extra_help
[8]: configure-key-bindings.md#key-bindings
[9]: #key_bindings
[10]: #layout
[11]: layout.md#layout
[12]: modes.md
[13]: #prompt

@ -0,0 +1,192 @@
### Modes
xplr is a modal file explorer. That means the users switch between different
modes, each containing a different set of key bindings to avoid clashes.
Users can switch between these modes at run-time.
The modes can be configured using the `xplr.config.modes` Lua API.
`xplr.config.modes.builtin` contain some built-in modes which can be
overridden, but you can't add or remove modes in it.
#### xplr.config.modes.builtin.default
The builtin default mode.
Visit the [Default Key Bindings](https://xplr.dev/en/default-key-bindings)
to see what each mode does.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.debug_error
The builtin debug error mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.recover
The builtin recover mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.go_to_path
The builtin go to path mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.move_to
The builtin move_to mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.copy_to
The builtin copy_to mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.selection_ops
The builtin selection ops mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.create
The builtin create mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.create_directory
The builtin create directory mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.create_file
The builtin create file mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.number
The builtin number mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.go_to
The builtin go to mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.rename
The builtin rename mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.duplicate_as
The builtin duplicate as mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.delete
The builtin delete mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.action
The builtin action mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.quit
The builtin quit mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.search
The builtin search mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.filter
The builtin filter mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.relative_path_does_match_regex
The builtin relative_path_does_match_regex mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.relative_path_does_not_match_regex
The builtin relative_path_does_not_match_regex mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.sort
The builtin sort mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.switch_layout
The builtin switch layout mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.vroot
The builtin vroot mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.edit_permissions
The builtin edit permissions mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.custom
This is where you define custom modes.
Type: mapping of the following key-value pairs:
- key: string
- value: [Mode](https://xplr.dev/en/mode)
Example:
```lua
xplr.config.modes.custom.example = {
name = "example",
key_bindings = {
on_key = {
enter = {
help = "default mode",
messages = {
"PopMode",
{ SwitchModeBuiltin = "default" },
},
},
},
},
}
xplr.config.general.initial_mode = "example"
```

@ -0,0 +1,36 @@
# Node Type
A node-type contains the following fields:
- [meta][4]
- [style][5]
### meta
Type: mapping of string and string
A meta field can contain custom metadata about a node. By default, the "icon"
metadata is set for the [directory][1], [file][2], and
[symlink][3] nodes.
Example:
```lua
xplr.config.node_types.file = {
meta = {
icon = "f",
foo = "bar",
}
}
```
## Also See:
- [xplr.config.node_types][6]
[1]: node_types.md#directory
[2]: node_types.md#file
[3]: node_types.md#symlink
[4]: #meta
[5]: style.md
[6]: node_types.md

@ -0,0 +1,132 @@
### Node Types
This section defines how to deal with different kinds of nodes (files,
directories, symlinks etc.) based on their properties.
One node can fall into multiple categories. For example, a node can have the
_extension_ `md`, and also be a _file_. In that case, the properties from
the more specific category i.e. _extension_ will be used.
This can be configured using the `xplr.config.node_types` Lua API.
#### xplr.config.node_types.directory.style
The style for the directory nodes
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.node_types.directory.meta.icon
Metadata for the directory nodes.
You can set as many metadata as you want.
Type: nullable string
Example:
```lua
xplr.config.node_types.directory.meta.foo = "foo"
xplr.config.node_types.directory.meta.bar = "bar"
```
#### xplr.config.node_types.file.style
The style for the file nodes.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.node_types.file.meta.icon
Metadata for the file nodes.
You can set as many metadata as you want.
Type: nullable string
Example:
```lua
xplr.config.node_types.file.meta.foo = "foo"
xplr.config.node_types.file.meta.bar = "bar"
```
#### xplr.config.node_types.symlink.style
The style for the symlink nodes.
Type: [Style](https://xplr.dev/en/style)
#### xplr.config.node_types.symlink.meta.icon
Metadata for the symlink nodes.
You can set as many metadata as you want.
Type: nullable string
Example:
```lua
xplr.config.node_types.symlink.meta.foo = "foo"
xplr.config.node_types.symlink.meta.bar = "bar"
```
#### xplr.config.node_types.mime_essence
Metadata and style based on mime types.
It is possible to use the wildcard `*` to match all mime sub types. It will
be overwritten by the more specific sub types that are defined.
Type: mapping of the following key-value pairs:
- key: string
- value:
- key: string
- value: [Node Type](https://xplr.dev/en/node-type)
Example:
```lua
xplr.config.node_types.mime_essence = {
application = {
-- application/*
["*"] = { meta = { icon = "a" } },
-- application/pdf
pdf = { meta = { icon = "" }, style = { fg = "Blue" } },
-- application/zip
zip = { meta = { icon = ""} },
},
}
```
#### xplr.config.node_types.extension
Metadata and style based on extension.
Type: mapping of the following key-value pairs:
- key: string
- value: [Node Type](https://xplr.dev/en/node-type)
Example:
```lua
xplr.config.node_types.extension.md = { meta = { icon = "" }, style = { fg = "Blue" } }
xplr.config.node_types.extension.rs = { meta = { icon = "🦀" } }
```
#### xplr.config.node_types.special
Metadata and style based on special file names.
Type: mapping of the following key-value pairs:
- key: string
- value: [Node Type](https://xplr.dev/en/node-type)
Example:
```lua
xplr.config.node_types.special["Cargo.toml"] = { meta = { icon = "" } }
xplr.config.node_types.special["Downloads"] = { meta = { icon = "" }, style = { fg = "Blue" } }
```

@ -0,0 +1,12 @@
# Plugin
xplr supports pluggable Lua modules that can be used to easily configure or
extend xplr UI and functionalities.
- [Installing Plugins][1]
- [Writing Plugins][2]
- [Awesome Plugins][3]
[1]: installing-plugins.md
[2]: writing-plugins.md
[3]: awesome-plugins.md

@ -0,0 +1,27 @@
# Post Install
Once [installed][1], use the following steps to setup and run xplr.
## Create the customizable config file
```bash
mkdir -p ~/.config/xplr
version="$(xplr --version | awk '{print $2}')"
echo "version = '${version:?}'" > ~/.config/xplr/init.lua
```
Then
**[copy from here][2]**
and remove / comment out what you don't want to customize.
## Run
```
xplr
```
[1]: install.md
[2]: https://github.com/sayanarijit/xplr/blob/main/src/init.lua
[3]: upgrade-guide.md

@ -0,0 +1,10 @@
# Quickstart
Nice to you have here! Let's quickly start with the
following steps:
- [Install][1]
- [Post Install][2]
[1]: install.md
[2]: post-install.md

@ -0,0 +1,77 @@
# Searching
xplr supports searching paths using different algorithm. The search mechanism
generally appears between filters and sorters in the `Sort & filter` panel.
Example:
```
fzy:foo↓
```
This line means that the nodes visible on the table are being filtered using the
[fuzzy matching][1] algorithm on the input `foo`. The arrow means that ranking based
ordering is being applied, i.e. [sorters][2] are being ignored.
## Node Searcher Applicable
Node Searcher contains the following fields:
- [pattern][3]
- [recoverable_focus][4]
- [algorithm][5]
- [unordered][7]
### pattern
The patterns used to search.
Type: string
### recoverable_focus
Where to focus when search is cancelled.
Type: nullable string
### algorithm
Search algorithm to use. Defaults to the value set in
[xplr.config.general.search.algorithm][8].
It can be one of the following:
- Fuzzy
- Regex
### unordered
Whether to skip ordering the search result by algorithm based ranking. Defaults
to the value set in [xplr.config.general.search.unordered][9].
Type: boolean
## Example:
```lua
local searcher = {
pattern = "pattern to search",
recoverable_focus = "/path/to/focus/on/cancel",
algorithm = "Fuzzy",
unordered = false,
}
xplr.util.explore({ searcher = searcher })
```
See [xplr.util.explore][6].
[1]: https://en.wikipedia.org/wiki/Approximate_string_matching
[2]: sorting.md
[3]: #pattern
[4]: #recoverable_focus
[5]: #algorithm
[6]: xplr.util.md#xplrutilexplore
[7]: #unordered
[8]: general-config.md#xplrconfiggeneralsearchalgorithm
[9]: general-config.md#xplrconfiggeneralsearchunordered

@ -0,0 +1,85 @@
# Sorting
xplr supports sorting paths by different properties. The sorting mechanism
works like a pipeline, which in visible in the `Sort & filter` panel.
Example:
```
size↑ [i]rel↓ [c]dir↑ [c]file↑ sym↑
```
This line means that the nodes visible in the table will be first sorted by
it's size, then by case insensitive relative path, then by the
canonical (symlink resolved) type of the node, and finally by whether or not
the node is a symlink.
The arrows denote the order.
Each part of this pipeline is called [Node Sorter Applicable][1].
## Node Sorter Applicable
It contains the following information:
- [sorter][2]
- [reverse][3]
### sorter
A sorter is a [sum type][4] that can be one of the following:
- "ByRelativePath"
- "ByIRelativePath"
- "ByExtension"
- "ByIsDir"
- "ByIsFile"
- "ByIsSymlink"
- "ByIsBroken"
- "ByIsReadonly"
- "ByMimeEssence"
- "BySize"
- "ByCreated"
- "ByLastModified"
- "ByCanonicalAbsolutePath"
- "ByICanonicalAbsolutePath"
- "ByCanonicalExtension"
- "ByCanonicalIsDir"
- "ByCanonicalIsFile"
- "ByCanonicalIsReadonly"
- "ByCanonicalMimeEssence"
- "ByCanonicalSize"
- "ByCanonicalCreated"
- "ByCanonicalLastModified"
- "BySymlinkAbsolutePath"
- "ByISymlinkAbsolutePath"
- "BySymlinkExtension"
- "BySymlinkIsDir"
- "BySymlinkIsFile"
- "BySymlinkIsReadonly"
- "BySymlinkMimeEssence"
- "BySymlinkSize"
- "BySymlinkCreated"
- "BySymlinkLastModified"
### reverse
Type: boolean
It defined the direction of the order.
## Example
```lua
xplr.config.general.initial_sorting = {
{ sorter = "ByCanonicalIsDir", reverse = true },
{ sorter = "ByIRelativePath", reverse = false },
}
```
This snippet defines the initial sorting logic to be applied when xplr loads.
[1]: #node-sorter-applicable
[2]: #sorter
[3]: #reverse
[4]: sum-type.md

@ -0,0 +1,87 @@
# Style
A style object contains the following information:
- [fg][1]
- [bg][2]
- [add_modifiers][3]
- [sub_modifiers][4]
### fg
Type: nullable [Color][5]
The foreground color.
### bg
Type: nullable [Color][5]
The background color.
### add_modifiers
Type: nullable list of [Modifier][6]
Modifiers to add.
### sub_modifiers
Type: nullable list of [Modifier][6]
Modifiers to remove.
## Color
Color is a [sum type][7] that can be one of the following:
- "Reset"
- "Black"
- "Red"
- "Green"
- "Yellow"
- "Blue"
- "Magenta"
- "Cyan"
- "Gray"
- "DarkGray"
- "LightRed"
- "LightGreen"
- "LightYellow"
- "LightBlue"
- "LightMagenta"
- "LightCyan"
- "White"
- { Rgb = { int, int, int } }
- { Indexed = int }
## Modifier
Modifier is a [sum type][7] that can be one of the following:
- "Bold"
- "Dim"
- "Italic"
- "Underlined"
- "SlowBlink"
- "RapidBlink"
- "Reversed"
- "Hidden"
- "CrossedOut"
## Example
```lua
xplr.config.general.prompt.style.fg = "Red"
xplr.config.general.prompt.style.bg = { Rgb = { 100, 150, 200 } }
xplr.config.general.prompt.style.add_modifiers = { "Bold", "Italic" }
xplr.config.general.prompt.style.sub_modifiers = { "Hidden" }
```
[1]: #fg
[2]: #bg
[3]: #add_modifiers
[4]: #sub_modifiers
[5]: #color
[6]: #modifier
[7]: sum-type.md

@ -0,0 +1,96 @@
# Sum Type
> This section isn't specific to xplr. However, since xplr configuration makes
> heavy use of this particular data type, even though it isn't available in
> most of the mainstream programming languages (yet), making it a wild or
> unfamiliar concept for many, it's worth doing a quick introduction here.
>
> If you're already familiar with [Sum Type / Tagged Union][1] (e.g. Rust's
> enum), you can skip ahead.
While reading this doc, you'll come across some data types like [Layout][2],
[Color][4], [Message][3] etc. that says something like "x is a sum type that
can be any of the following", and then you'll see a list of strings and/or lua
tables just below.
Yes, they are actually sum types, i.e. they can be any of the given set of
tagged variants listed there.
Notice the word "be". Unlike classes or structs (aka product types), they can't
"have" values, they can only "be" the value, or rather, be one of the possible
set of values.
Also notice the word "tagged". Unlike the single variant `null`, or the dual
variant `boolean` types, the variants of sum types are tagged (i.e. named), and
may further have, or be, value or values of any data type.
A simple example of a sum type is an enum. Many programming languages have
them, but only a few modern programming languages allow nesting other types
into a sum type.
```rust
enum Color {
Red,
Green,
}
```
Here, `Color` can be one of two possible set of values: `Red` and `Green`, just
like `boolean`, but unlike `boolean`, being tagged allows `Color` to have more
than two variants if required, by changing the definition.
e.g.
```rust
enum Color {
Red,
Green,
Blue,
}
```
We'd document it here as:
> Result is a sum type that can be one of the following:
>
> - "Red"
> - "Green"
> - "Blue"
But some languages (like Rust, Haskell, Elm etc.) go even further, allowing us
to associate each branch of the enum with further nested types like:
```rust
enum Layout {
Table,
HelpMenu,
Horizontal {
config: LayoutConfig, // A product type (similar to class/struct)
splits: Vec<Layout> // A list of "Layout"s (i.e. list of sum types)
},
}
```
Here, as we can see, unlike the first example, some of `Layout`'s possible
variants can have further nested types associated with them. Note that
`Horizontal` here can have a sum type (e.g. enum), or a product type (e.g.
class/struct), or both (any number of them actually) nested in it. But the
nested values will only exist when `Layout` is `Horizontal`.
We'd document it here as:
> Layout is a sum type that can be one of the following:
>
> - "Table"
> - "HelpMenu"
> - { Horizontal = { config = Layout Config, splits = { Layout, ... } }
And then we'd go on documenting whatever `Layout Config` is.
So, there you go. This is exactly what sum types are - glorified enums that can
have nested types in each branch.
[1]: https://en.wikipedia.org/wiki/Tagged_union
[2]: layout.md
[3]: message.md
[4]: style.md#color

@ -0,0 +1,533 @@
# Upgrade Guide
When you upgrade xplr, you might see an error like this
```
Incompatible script version in: /home/sayanarijit/.config/xplr/init.lua. The script version is: 0.9.0, the required version is: 0.10.1. Visit https://github.com/sayanarijit/xplr/wiki/Upgrade-Guide
```
All you need to do is follow the [instructions][1] starting from
your config version, all the way to the required version.
<details>
<summary>Expand for more information</summary>
With every update, we either implement a `major` breaking change (e.g.
deprecating or replacing messages), or a `minor` feature addition (e.g. adding
new messages) or `patch`, fixes, and optimization (e.g. performance
optimization).
Knowing that we use the `{major}.{minor}.{patch}` versioning format,
- Major version mismatch are generally incompatible. xplr will fail
with error.
- Minor version upgrades (not downgrades) and patch fixes are backwards
compatible. You might get notified by log a message which you can disable by
updating the version in your config file.
- However, if the config file has a higher value for the minor version
than the app, then also xplr will fail with error, suggesting you
to visit this page.
Though in that case, you will be downgrading your config file based on your
app version.
e.g.
- `1.0.0` -> `1.0.x`: Patch (fully compatible).
- `1.0.0` -> `1.x.x`: Only backwards compatible. You can't generally use for
e.g. `app-1.0.0` with `config-1.1.0`. But vice versa is fine.
- `1.0.0` -> `x.x.x`: Not compatible at all.
Note that until we're `v1`, we'll be using the `{minor}` version number as
`{major}`, and the `{patch}` number as `{minor}` to determine
compatibility.
</details>
### Instructions
#### [v0.20.2][48] -> [v0.21.9][49]
- Some plugins might stop rendering colors. Wait for them to update.
- Rename `xplr.config.general.sort_and_filter_ui.search_identifier` to
`xplr.config.general.sort_and_filter_ui.search_identifiers`.
- Resolved Node API will not contain the `permissions` field anymore.
Use the utility function `xplr.util.node` to get its permissions.
- Layout `CustomContent` has been undocumented. It will stay for compatibility,
but you should prefer using the following new layouts, because they support
custom title:
- Static
- Dynamic
- Use the new messages for improved search operations:
- Search
- SearchFromInput
- SearchFuzzyUnordered
- SearchFuzzyUnorderedFromInput
- SearchRegex
- SearchRegexFromInput
- SearchRegexUnordered
- SearchRegexUnorderedFromInput
- ToggleSearchAlgorithm
- EnableSearchOrder
- DisableSearchOrder
- ToggleSearchOrder
- Use skim's [search syntax][50] to customize the search.
- Set your preferred search algorithm and ordering:
`xplr.config.general.search.algorithm = "Fuzzy" -- or "Regex"`.
`xplr.config.general.search.unordered = false -- or true`
- You need to clear the selection list manually after performing batch
operation like copy, softlink creation etc.
- Use the following new key bindings:
- `:sl` to list selection in a $PAGER.
- `:ss` to create softlink of the selected items.
- `:sh` to create hardlink of the selected items.
- `:se` to edit selection list in your $EDITOR.
- Better conflict handling: prompt for action.
- Navigate between the selected paths using the following messages:
- FocusPreviousSelection (`ctrl-p`)
- FocusNextSelection (`ctrl-n`)
- Use `LS_COLORS` environment variable, along with the following utility
- functions for applying better styling/theaming.
- xplr.util.lscolor
- xplr.util.paint
- xplr.util.textwrap
- xplr.util.style_mix
- Use new the fields in Column Renderer Argument:
- style
- permissions
- Use the following config to specify how the paths in selection list should be
rendered:
- xplr.config.general.selection.item.format
- xplr.config.general.selection.item.style
- Use the following utility functions to work with the file permissions:
- xplr.util.permissions_rwx
- xplr.util.permissions_octal
- Type `:p` to edit file permissions interactively.
- Also check out the following utility functions:
- xplr.util.layout_replace
- xplr.util.relative_to
- xplr.util.shorthand
- xplr.util.clone
- xplr.util.exists
- xplr.util.is_dir
- xplr.util.is_file
- xplr.util.is_symlink
- xplr.util.is_absolute
- xplr.util.path_split
- xplr.util.node
- xplr.util.node_type
- xplr.util.shell_escape
- Executables will me marked with the mime type: `application/x-executable`.
- macOS legacy coreutils will be generally supported, but please update it.
- Since v0.21.2 you can use the on_selection_change hook.
- Since v0.21.4 you can use function keys upto F24 and the following new
messages:
- NextVisitedDeepBranch (bound to `)` key)
- PreviousVisitedDeepBranch (bound to `(` key)
- Since v0.21.6:
- You can use `c` and `m` keys in default mode to quickly copy
and move focused or selected files, without having to change directory.
- Use `xplr.util.debug()` to debug lua values.
- Since v0.21.8:
- Scroll behavior will default to vim-like continuous scrolling. You can set
`xplr.config.general.paginated_scrolling = true` to revert back to the
paginated scrolling.
- Set `xplr.config.general.scroll_padding` to customize the scroll padding.
- The calculated `scrolltop` value will be passed as part of the
`Content Rendeder Argument` in `Dynamic` layout renderer functions.
Thanks to @noahmayr for contributing to a major part of this release.
#### [v0.19.4][47] -> [v0.20.2][48]
- BREAKING: xplr shell (`:!`) will default to null (`\0`) delimited pipes, as
opposed to newline (`\n`) delimited ones (i.e. will use `Call0` instead of
`Call`).
- Use new messages for safer file path handling (`\0` delimited):
- Call0
- CallSilently0
- BashExec0
- BashExecSilently0
- Use new sub-commands for safer message passing:
- `-m FORMAT [ARGUMENT]...` / `--pipe-msg-in FORMAT [ARGUMENT]...`
- `-M FORMAT [ARGUMENT]...` / `--print-msg-in FORMAT [ARGUMENT]...`
Where FORMAT is a YAML string that may contain `%s`, `%q` and `%%`
placeholders and ARGUMENT is the value per placeholder. See `init.lua`.
- Following hooks can be defined in the config files using an optional
`return { on_* = { list, of, messages }, ... }` statement at the end.
- on_load
- on_focus_change
- on_directory_change
- on_mode_switch (since v0.20.2)
- on_layout_switch (since v0.20.2)
- Use `--vroot` to isolate navigation of an xplr session inside a specific
directory. Interaction still requires passing full path, and shell,
lua functions etc still can access paths outside vroot.
- Use the following messages to switch vroot at runtime, or the use key
bindings available in the new builtin mode "vroot" (mapped to `:` `v`).
- SetVroot
- UnsetVroot
- ToggleVroot
- ResetVroot
- Use `$XPLR_INITIAL_PWD` and Lua equivalent to implement workspace like
features without using virtual root. Use keys `gi` to go to the initial
working directory from anywhere.
- Use the convenient `xplr.util` utility functions in your Lua function calls.
See xplr.util API docs.
#### [v0.18.0][46] -> [v0.19.4][47]
- BREAKING: The builtin modes cannot be accessed using space separated names
anymore. Use underscore separated mode names. For e.g.
`SwitchModeBuiltin: create file` becomes `SwitchModeBuiltin: create_file` and
so on. Kindly go through your config, find and update them, or copy from the
latest `init.lua`.
- Now you can use `xplr.config.general.global_key_bindings` to define a set of
key bindings that are available by default in every mode. e.g. `esc`
and `ctrl-c`, and remove boilerplate code from your config.
- You can use the new builtin mode `go_to_path` which can be used for typing or
pasting paths to enter into or to focus on. Type `g` `p` to enter this mode.
- Now you can use basic tab completion in the `go_to_path`, `create_file`,
`create_directory`, `rename` and `duplicate_as` modes.
- Use the builtin function `xplr.fn.builtin.try_complete_path` to add easy tab
completion support into your own configuration.
- Now you can open OSC 7 compatible terminals into the `xplr`'s current working
directory by spawning new terminal sessions via the terminal supported key
bindings.
- Use `NO_COLOR` environment variable to disable OSC 7 compliance along with
colors.
- If you have fully copied the default `init.lua` locally, you might want to
go through the latest improvements in `init.lua`. Specifically the `search`,
`filter` and `sort` modes. Also, search for `SetInputPrompt` and the `tab`
key bindings.
- Since version 0.19.1, you can access uid and gid of the file owner in the Lua
API.
- The input buffer will support more readline-like keys.
Also, added "DeleteTillEnd" as another cursor based "InputOperation" option.
- Fixed applying regex based filters via the CLI and `$XPLR_PIPE_MSG_IN` pipe.
- You can use the `prompt` field to define input prompt for each mode, instead
of using the `SetInputPrompt` message.
- Since version v0.19.4, the native search will default to skim-v2 based fuzzy
matching. `esc` while in search mode will recover the initial focus. People
who prefer the regex based search, can use the `regex-search.xplr` plugin.
The following messages will be available for search based operations:
- SearchFuzzy
- SearchFuzzyFromInput
- AcceptSearch
- CancelSearch
- Since version v0.19.4, quick scrolling operations are supported using the
following messages and keys:
- ScrollUp -------- page-up
- ScrollDown ------ page-down
- ScrollUpHalf ---- {
- ScrollDownHalf -- }
#### [v0.17.6][45] -> [v0.18.0][46]
- Key binding `f` `r` and `f` `R` will now filter using regex.
- Key binding `f` `backspace` will now remove the last filter.
- Search mode now defaults to regex search.
- `Node` metadata in the Lua API will contain two new fields:
- `created`
- `last_modified`
- The last column in the files table now displays the last modification time.
- You can now use `--read0`, `--write0` and `-0`/`--null` to read and/or print
null character delimited paths.
- You can now the following regex filters:
- `RelativePathDoesMatchRegex`
- `RelativePathDoesNotMatchRegex`
- `IRelativePathDoesMatchRegex`
- `IRelativePathDoesNotMatchRegex`
- `AbsolutePathDoesMatchRegex`
- `AbsolutePathDoesNotMatchRegex`
- `IAbsolutePathDoesMatchRegex`
- `IAbsolutePathDoesNotMatchRegex`
- You can use a new `SetInputPrompt` to set the input prompt dynamically.
- You can now use the following timestamp based sorters:
- "ByCreated"
- "ByLastModified"
- "ByCanonicalCreated"
- "ByCanonicalLastModified"
- "BySymlinkCreated"
- "BySymlinkLastModified"
#### [v0.16.4][44] -> [v0.17.6][45]
- Deprecated `app.directory_buffer`, `app.history`, and `app.last_modes` in
the custom dynamic layout renderer context.
As of now, there's no way to access these fields in dynamic layouts. While
`app.history` and `app.last_modes` can be re-added upon request
(with justification), `app.directory_buffer` has been deprecated for good.
However, there's no change in the `CallLua*` context.
- Set `xplr.config.general.hide_remaps_in_help_menu` to `true` to hide the
remaps in help menu.
- `None` will be serialized to `nil` in Lua.
- `LuaEval` can now return a function that will be called with the Lua Context
argument. Refer to the `Full List of Messages` doc for example.
- From version v0.17.1, set `xplr.config.general.disable_debug_error_mode` to
`true` to disable switching to the "debug error" mode when startup errors
occur.
- From version v0.17.2, you can use CLI argument `--print-pwd-as-result` for cd
on quit, and key binding `ctrl-d` to duplicate a path in the same directory
with a different name.
- Since version v0.17.3, you can use `border_type`, `border_style` to customize
borders, and `enforce_bounded_index_navigation` to customize up/down
navigation behavior when focus is on the top or bottom node.
#### [v0.15.2][43] -> [v0.16.4][44]
- Deprecated `config.general.cursor`. The default terminal cursor will be used
for the time being.
- Opening xplr inside a symlink will not resolve the path.
- You can now replace most boilerplate configuration handling keys to send
`BufferInputFromKey`, `RemoveInputBufferLastCharacter`,
`RemoveInputBufferLastWord`, `SetInputBuffer = ""` etc. messages with a
single `UpdateInputBufferFromKey` message.
- You can now pass multiple paths as command-line arguments or via stdin to
select paths, e.g. `xplr -- $PWD /path/to/select/1 /path/to/select/2`.
- Pass `--force-focus` to focus on the first path even if it's a directory,
e.g. `xplr . --force-focus`.
- Use new messages `LuaEval` and `LuaEvalSilently` to run Lua code without
needing to define a function. However, the `app` context won't be available.
- You can now use new key handlers in the config:
- on_alphanumeric
- on_character
- on_navigation
- on_function
#### [v0.14.7][3] -> [v0.15.2][43]
- Deprecated `config` field from `CallLua` argument. Use the globally available
`xplr.config` instead.
- `xplr.config.general.disable_recover_mode` has been deprecated. Use
`xplr.config.general.enable_recover_mode` instead.
- Use `xplr.config.general.focus_selection_ui` to highlight selected files
under focus differently than files under focus that are not selected.
- Use `PopModeKeepingInputBuffer`, and SwitchMode alternatives to switching to
different modes without resetting the input buffer.
- Use the new `CustomContent` layout option to render custom content.
- Use the new `layout` field in a mode to define custom layout for a specific
mode.
- Library users please refer to the latest API docs and examples.
#### [v0.13.7][2] -> [v0.14.7][3]
- macOS users need to place their config file (`init.lua`) in
`$HOME/.config/xplr/` or `/etc/xplr/`.
- Library users please refer to the latest API docs.
- Check out the new messages: `{Start|Stop|Toggle}Fifo`. These enable support
for [FIFO based file previews][4].
- You can disable the recover mode using `config.general.disable_recover_mode = true`.
- Try running `xplr --help`. Yes, CLI has been implemented.
- Since version `v0.14.3`, `StartFifo` and `ToggleFifo` will write to the FIFO
path when called. So, there's no need to pipe the focus path explicitly.
- Since version `v0.14.3`, general config `xplr.config.start_fifo` is available
which can be set to a file path to start a fifo when xplr starts.
- Since version `v0.14.4`, `$XPLR_SESSION_PATH` can be used to dump session
related data.
- Since version `v0.14.6`, the `-C` or `--extra-config` CLI argument is
available.
#### [v0.12.1][6] -> [v0.13.7][2]
- Lua functions called using [`CallLua`][7] and [`CallLuaSilently`][8] messages will receive [`CallLuaArg`][9] object as the function argument (instead of the [`App`][10] object).
- Each `node_types` config will inherit defaults from matching less specific `node_types` config and overwrite them.
- Since version `v0.13.2`, you don't need to use/send `Refresh` anymore. It will be auto-handled by xplr.
#### [v0.11.1][11] -> [v0.12.1][6]
- `xplr.config.node_types.mime_essence` has split into type and subtype. Hence, instead of `xplr.config.node_types.mime_essence["text/plain"] = ..` use `xplr.config.node_types.mime_essence["text"] = { plain = .. }`.
- You can also define `xplr.config.node_types.mime_essence["text"]["*"]` that will match all text types (`text/*`).
#### [v0.10.2][12] -> [v0.11.1][11]
- `remaps:` has been removed to avoid confusion. Use lua assignments instead.
For e.g.
```
xplr.config.modes.builtin.default.key_bindings.on_key["v"] = xplr.config.modes.builtin.default.key_bindings.on_key.space
```
#### [v0.9.1][13] -> [v0.10.2][12]
- [`config.yml`][14] has been fully replaced with [`init.lua`][15]. If you have a lot of customization in your `config.yml`, [xplr-yml2lua][16] can help you with migrating it to `init.lua`.
- `Handlebars` templates has been replaced with [Lua functions][17]. You can either remove the customizations or overwrite the functions accordingly.
- Added new messages `CallLua` and `CallLuaSilently` to call lua functions. The app state will be passed as input to the functions, and the returned messages will be handled by xplr. `CallLua` and `CallLuaSilently` are more flexible (and probably faster) alternatives to `Call`, `CallSilently`, `BashExec` and `BashExecSilently`. [e.g.][18]
#### [v0.9.0][19] -> [v0.9.1][13]
- You can now set `remaps: {key: null}` to un-map a key.
- `gx` will open the item under focus.
- New key map `:sx` will open the selected items.
#### [v0.8.0][20] -> [v0.9.0][19]
Your previous config should mostly work fine. However, in case you are using `SwitchMode` heavily in your custom config, follow along.
- Introduced new message `PopMode`. You might want to use this message instead of `SwitchMode*` when returning back to the previous mode.
- After using (the group of) `PopMode` and `SwitchMode*` messages, you are now required to `Refresh` manually to avoid the UI lag.
- Pressing any invalid key will now lead you to the `recover` mode and will protect you from typing further invalid keys. Press `esc` to escape the `recover` mode.
- Introduced new message `LogWarning`, similar to other `Log*` messages.
- Creating files and directories has been optimized for batch creation.
#### [v0.7.2][21] -> [v0.8.0][20]
If you have made changes to the config file,
- Replace message `Explore` with `ExplorePwd` or `ExplorePwdAsync` or probably `ExploreParentsAsync`.
- Pipe `$XPLR_PIPE_FOCUS_OUT` has been removed. Use `$XPLR_FOCUS_PATH` env var instead.
- You might want to review your path escaping logics. For e.g. use `echo FocusPath: "'"$PWD"'" >> $PIPE` instead of `echo "FocusPath: $PWD" >> $PIPE`.
#### [v0.7.0][22] -> [v0.7.2][21]
- Just update the `version` in your config file.
- For version >= `v0.7.1`, you might want to free up or remap the `tab` key in `search` mode to enable easy selection during search.
#### [v0.6.0][23] -> [v0.7.0][22]
If you haven't made any changes in the config file, you should be fine just updating the version number. Else,
- You can make the `Table: ...`, `InputAndLogs: ...` layout values null and define the common properties in the `general.panel_ui` instead.
#### [v0.5.13][24] -> [v0.6.0][23]
If you haven't made any changes in the config file, you should be fine just updating the version number. Else,
- Rename `add_modifier: {bits: 1}` to `add_modifiers: [Bold]`, `sub_modifier: {bits: 1}` to `sub_modifiers: [Bold]` and so on.
- Rename `percentage: 10` to `Percentage: 10`, `ratio: 1` to `Ratio: 1` and so on.
- You might want to free up or remap the `ctrl-w` key binding in `default` mode to enable layout switching.
Optionally, checkout this new theme to learn more about what's new.
#### [v0.5.0][25] -> [v0.5.13][24]
- Just update the `version` in your config file.
- For versions >= `v0.5.8`, you can set `$OPENER` env var to declare a global GUI file opener (to open files using keys `gx`).
- You might also want to update other mappings to handle files with names starting with `-` (hiphen). For example, instead of `rm ${filename}` use `rm -- ${filename}`. Same goes for `cp`, `mv`, `cat`, `touch` etc.
- For version >= `v0.5.13`, you might want to use the more specific `SwitchModeBuiltin` and `SwitchModeCustom` messages instead of the general `SwitchMode` message.
#### [v0.4.3][26] -> [v0.5.0][25]
If you haven't have any changes in the config file, you should be fine just updating the version number.
Else do the following
- Replace `{RelativePathIs, case_sensitive: true}` with `RelativePathIs`.
- Replace `{RelativePathIs, case_sensitive: false}` with `IRelativePathIs`.
- Do the same with other filters you are using.
- You might want to update your `backspace` handling to use the `RemoveInputBufferLastCharacter` message.
- You might want to free-up `f`, `s`, `ctrl-r` and `ctrl-u` key bindings in the default mode, or remap them.
- You might want to use the new UI variables.
- Update your config version to `v0.5.0`.
#### [v0.4.2][27] -> [v0.4.3][26]
If you have customized `general.table.row.cols`, you might want to [update it][28] to use the new variables with better symlink support.
#### [v0.4.1][29] -> [v0.4.2][27]
In case you have mapped the keys `q`, `ctrl-i` and `ctrl-o`, you may want to revisit the default mode key bindings and remap accordingly to use the new functionalities.
#### [v0.3.13][30] -> [v0.4.1][29]
A lot has changed (apologies). But I promise from now on, upgrading will be much less painful (thanks to [@maximbaz][31]'s valuable [inputs][32] and [code reviews][33]).
So, to start with the upgrade, let's remove everything from your config file except the `version` field and your custom modifications. If `version` is the only thing remaining, update it to `v0.4.1` and you are done.
Else, do the following
- Rename `general.focused_ui` to `general.focus_ui` ([see here][34]).
- Rename `filetypes` to `node_types`. ([see here][35])
- Rename `custom` field to `meta`. ([see here][36])
- Move `icon` to `meta.icon`. ([see here][37])
- Rename `normal_ui` to `default_ui`. ([see here][38])
- Split `modes` into `modes.builtin` and `modes.custom` ([see here][39]). Migrate your custom modes to `modes.custom`. And copy only the changes in the in-built modes in `modes.builtin`.
- Finally, update the `version` to `v0.4.1`.
#### [v0.3.8][40] -> [v0.3.13][30]
Your current config should work fine. However, you might want to replace some `Call` and `BashExec` messages with `CallSilently` and `BashExecSilently` to remove the flickering of the screen.
If you haven't made any changes to the configuration, you can delete and regenerate it.
Else, do the following
- Check the new default config by temporarily removing your current config (with backup) and dumping the new config.
- Search for `Call` and `BashExec` in the new config.
- Compare and probably replace the associated actions in your current config
#### [v0.3.0][41] -> [v0.3.8][40]
Your current config should work fine. However, you might want to replace some `ResetNodeFilters` messages with `RemoveNodeFilter` and `RemoveNodeFilterFromInput` to get a better search and filter experience.
If you haven't made any changes to the configuration, you can delete and regenerate it.
Else, do the following
- Check the new default config by temporarily removing your current config (with backup) and dumping the new config.
- Search for `RemoveNodeFilterFromInput` in the new config.
- Compare and probably replace the associated actions in your current config.
#### v0.2.14 -> [v0.3.0][41]
If you haven't made any changes to the configuration, you can delete and regenerate it.
Else do the following:
- `$XPLR_APP_YAML` has been removed. You can use `Debug` to export the app state.
- `$XPLR_RESULT` has been ported to file `$XPLR_PIPE_RESULT_OUT`. Use `cat` instead of `echo`, `<` instead of `<<<` etc.
- `$XPLR_GLOBAL_HELP_MENU` has been ported to
file `$XPLR_PIPE_GLOBAL_HELP_MENU_OUT`. Use `cat` instead of `echo`, `<` instead of `<<<` etc.
- `$XPLR_DIRECTORY_NODES` has been ported to
file `$XPLR_PIPE_DIRECTORY_NODES_OUT`. Use `cat` instead of `echo`, `<` instead of `<<<` etc.
- `$XPLR_LOGS` has been ported to file `$XPLR_PIPE_LOGS_OUT`. Use `cat` instead of `echo`, `<` instead of `<<<` etc.
- `$XPLR_PIPE_RESULT` has been ported to file `$XPLR_PIPE_RESULT_OUT`. Use `cat` instead of `echo`, `<` instead of `<<<` etc.
- Finally, update the `version` in your config file.
[1]: #instructions
[2]: https://github.com/sayanarijit/xplr/releases/tag/v0.13.7
[3]: https://github.com/sayanarijit/xplr/releases/tag/v0.14.7
[4]: https://github.com/sayanarijit/xplr/pull/229#issue-662426960
[6]: https://github.com/sayanarijit/xplr/releases/tag/v0.12.1
[7]: https://docs.rs/xplr/latest/xplr/app/enum.ExternalMsg.html#variant.CallLua
[8]: https://docs.rs/xplr/latest/xplr/app/enum.ExternalMsg.html#variant.CallLuaSilently
[9]: https://docs.rs/xplr/latest/xplr/app/struct.CallLuaArg.html
[10]: https://docs.rs/xplr/latest/xplr/app/struct.App.html
[11]: https://github.com/sayanarijit/xplr/releases/tag/v0.11.1
[12]: https://github.com/sayanarijit/xplr/releases/tag/v0.10.2
[13]: https://github.com/sayanarijit/xplr/releases/tag/v0.9.1
[14]: https://github.com/sayanarijit/xplr/blob/85696ded7a/src/config.yml
[15]: https://github.com/sayanarijit/xplr/blob/main/src/init.lua
[16]: https://github.com/sayanarijit/xplr-yml2lua
[17]: https://github.com/sayanarijit/xplr/blob/bfdb7736b99bc3c5ae53e7d621ba0e7ca2299b14/src/init.lua#L2005-L2064
[18]: https://github.com/sayanarijit/xplr/pull/177#issue-650643573
[19]: https://github.com/sayanarijit/xplr/releases/tag/v0.9.0
[20]: https://github.com/sayanarijit/xplr/releases/tag/v0.8.0
[21]: https://github.com/sayanarijit/xplr/releases/tag/v0.7.2
[22]: https://github.com/sayanarijit/xplr/releases/tag/v0.7.0
[23]: https://github.com/sayanarijit/xplr/releases/tag/v0.6.0
[24]: https://github.com/sayanarijit/xplr/releases/tag/v0.5.13
[25]: https://github.com/sayanarijit/xplr/releases/tag/v0.5.0
[26]: https://github.com/sayanarijit/xplr/releases/tag/v0.4.3
[27]: https://github.com/sayanarijit/xplr/releases/tag/v0.4.2
[28]: https://github.com/sayanarijit/xplr/blob/af1cda5762/src/config.yml#L46-L48
[29]: https://github.com/sayanarijit/xplr/releases/tag/v0.4.1
[30]: https://github.com/sayanarijit/xplr/releases/tag/v0.3.13
[31]: https://github.com/maximbaz
[32]: https://github.com/sayanarijit/xplr/issues/45#issue-854447104
[33]: https://github.com/sayanarijit/xplr/pull/47
[34]: https://github.com/sayanarijit/xplr/blob/055c1083d6/src/config.yml#L124
[35]: https://github.com/sayanarijit/xplr/blob/055c1083d6/src/config.yml#L145
[36]: https://github.com/sayanarijit/xplr/blob/055c1083d6/src/config.yml#L154-L155
[37]: https://github.com/sayanarijit/xplr/blob/055c1083d6/src/config.yml#L45
[38]: https://github.com/sayanarijit/xplr/blob/055c1083d6/src/config.yml#L114
[39]: https://github.com/sayanarijit/xplr/blob/055c1083d6/src/config.yml#L180-L181
[40]: https://github.com/sayanarijit/xplr/releases/tag/v0.3.8
[41]: https://github.com/sayanarijit/xplr/releases/tag/v0.3.0
[42]: https://github.com/sayanarijit/xplr/releases/tag/v0.14.4
[43]: https://github.com/sayanarijit/xplr/releases/tag/v0.15.2
[44]: https://github.com/sayanarijit/xplr/releases/tag/v0.16.4
[45]: https://github.com/sayanarijit/xplr/releases/tag/v0.17.6
[46]: https://github.com/sayanarijit/xplr/releases/tag/v0.18.0
[47]: https://github.com/sayanarijit/xplr/releases/tag/v0.19.4
[48]: https://github.com/sayanarijit/xplr/releases/tag/v0.20.2
[49]: https://github.com/sayanarijit/xplr/releases/tag/v0.21.9
[50]: https://github.com/lotabout/skim#search-syntax

@ -0,0 +1,94 @@
# Writing Plugins
Anyone who can write [Lua][1] code, can write xplr plugins.
Just follow the instructions and best practices:
## Naming
xplr plugins are named using hiphen (`-`) separated words that may also include
integers. They will be plugged using the `require()` function in Lua.
## Structure
A minimal plugin should confirm to the following structure:
```
.
├── README.md
└── init.lua
```
You can also use [this template][2].
### README.md
This is where you document what the plugin does, how to use it, etc.
### init.lua
This file is executed to load the plugin. It should expose a `setup()`
function, which will be used by the users to setup the plugin.
Example:
```lua
local function setup(args)
local xplr = xplr
-- do stuff with xplr
end
return { setup = setup }
```
## Publishing
When publishing plugins on GitHub or other repositories, it's a best practice
to append `.xplr` to the name to make them distinguishable. Similar to the
`*.nvim` naming convention for [Neovim][3] plugins.
Finally, after publishing, don't hesitate to
[let us know][4].
## Best practices
- Try not to execute a lot of commands at startup, it may make xplr slow to
start.
- When executing commands, prefer `Call0` over `Call`, `BashExec0` over
`BashExec` and so on. File names may contain newline characters
(e.g. `foo$'\n'bar`).
- File names may also contain quotes. Avoid writing directly to
`$XPLR_PIPE_MSG_IN`. Use `xplr -m` / `xplr --pipe-msg-in` instead.
- Check for empty variables using the syntax `${FOO:?}` or use a default value
`${FOO:-defaultvalue}`.
## Examples
Visit [Awesome Plugins][5] for xplr plugin examples.
## Also See
- [Tip: A list of hacks yet to make it as Lua plugins][15]
- [Tip: Some UI and theming tips][12]
- [Tutorial: Adding a New Mode][6]
- [Example: Using Environment Variables and Pipes][7]
- [Example: Using Lua Function Calls][8]
- [Example: Defining Custom Layout][9]
- [Example: Customizing Table Renderer][10]
- [Example: Render a custom dynamic table][11]
- [Example: Implementing a directory visit counter][16]
[1]: https://www.lua.org
[2]: https://github.com/sayanarijit/plugin-template1.xplr
[3]: https://neovim.io
[4]: https://github.com/sayanarijit/xplr/discussions/categories/show-and-tell
[5]: awesome-plugins.md
[6]: configure-key-bindings.md#tutorial-adding-a-new-mode
[7]: environment-variables-and-pipes.md#example-using-environment-variables-and-pipes
[8]: lua-function-calls.md#example-using-lua-function-calls
[9]: layout.md#example-defining-custom-layout
[10]: column-renderer.md#example-customizing-table-renderer
[11]: layout.md#example-render-a-custom-dynamic-table
[12]: https://github.com/sayanarijit/xplr/discussions/274
[15]: awesome-hacks.md
[16]: https://github.com/sayanarijit/xplr/discussions/529#discussioncomment-4073734

@ -0,0 +1,528 @@
### xplr.util.version
Get the xplr version details.
Type: function() -> { major: number, minor: number, patch: number }
Example:
```lua
xplr.util.version()
-- { major = 0, minor = 0, patch = 0 }
```
### xplr.util.debug
Print the given value to the console, and return it as a string.
Useful for debugging.
Type: function( value ) -> string
Example:
```lua
xplr.util.debug({ foo = "bar", bar = function() end })
-- {
-- ["bar"] = function: 0x55e5cebdeae0,
-- ["foo"] = "bar",
-- }
```
### xplr.util.clone
Clone/deepcopy a Lua value. Doesn't work with functions.
Type: function( value ) -> value
Example:
```lua
local val = { foo = "bar" }
local val_clone = xplr.util.clone(val)
val.foo = "baz"
print(val_clone.foo)
-- "bar"
```
### xplr.util.exists
Check if the given path exists.
Type: function( path:string ) -> boolean
Example:
```lua
xplr.util.exists("/foo/bar")
-- true
```
### xplr.util.is_dir
Check if the given path is a directory.
Type: function( path:string ) -> boolean
Example:
```lua
xplr.util.is_dir("/foo/bar")
-- true
```
### xplr.util.is_file
Check if the given path is a file.
Type: function( path:string ) -> boolean
Example:
```lua
xplr.util.is_file("/foo/bar")
-- true
```
### xplr.util.is_symlink
Check if the given path is a symlink.
Type: function( path:string ) -> boolean
Example:
```lua
xplr.util.is_file("/foo/bar")
-- true
```
### xplr.util.is_absolute
Check if the given path is an absolute path.
Type: function( path:string ) -> boolean
Example:
```lua
xplr.util.is_absolute("/foo/bar")
-- true
```
### xplr.util.path_split
Split a path into its components.
Type: function( path:string ) -> boolean
Example:
```lua
xplr.util.path_split("/foo/bar")
-- { "/", "foo", "bar" }
xplr.util.path_split(".././foo")
-- { "..", "foo" }
```
### xplr.util.node
Get [Node][5] information of a given path.
Doesn't check if the path exists.
Returns nil if the path is "/".
Errors out if absolute path can't be obtained.
Type: function( path:string ) -> [Node][5]|nil
Example:
```lua
xplr.util.node("./bar")
-- { parent = "/pwd", relative_path = "bar", absolute_path = "/pwd/bar", ... }
xplr.util.node("/")
-- nil
```
### xplr.util.node_type
Get the configured [Node Type][6] of a given [Node][5].
Type: function( [Node][5], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
If the second argument is missing, global config `xplr.config.node_types`
will be used.
Example:
```lua
xplr.util.node_type(app.focused_node)
-- { style = { fg = "Red", ... }, meta = { icon = "", ... } ... }
xplr.util.node_type(xplr.util.node("/foo/bar"), xplr.config.node_types)
-- { style = { fg = "Red", ... }, meta = { icon = "", ... } ... }
```
### xplr.util.dirname
Get the directory name of a given path.
Type: function( path:string ) -> path:string|nil
Example:
```lua
xplr.util.dirname("/foo/bar")
-- "/foo"
```
### xplr.util.basename
Get the base name of a given path.
Type: function( path:string ) -> path:string|nil
Example:
```lua
xplr.util.basename("/foo/bar")
-- "bar"
```
### xplr.util.absolute
Get the absolute path of the given path by prepending $PWD.
It doesn't check if the path exists.
Type: function( path:string ) -> path:string
Example:
```lua
xplr.util.absolute("foo/bar")
-- "/tmp/foo/bar"
```
### xplr.util.relative_to
Get the relative path based on the given base path or current working dir.
Will error if it fails to determine a relative path.
Type: function( path:string, options:table|nil ) -> path:string
Options type: { base:string|nil, with_prefix_dots:bookean|nil, without_suffix_dots:boolean|nil }
- If `base` path is given, the path will be relative to it.
- If `with_prefix_dots` is true, the path will always start with dots `..` / `.`
- If `without_suffix_dots` is true, the name will be visible instead of dots `..` / `.`
Example:
```lua
xplr.util.relative_to("/present/working/directory")
-- "."
xplr.util.relative_to("/present/working/directory/foo")
-- "foo"
xplr.util.relative_to("/present/working/directory/foo", { with_prefix_dots = true })
-- "./foo"
xplr.util.relative_to("/present/working/directory", { without_suffix_dots = true })
-- "../directory"
xplr.util.relative_to("/present/working")
-- ".."
xplr.util.relative_to("/present/working", { without_suffix_dots = true })
-- "../../working"
xplr.util.relative_to("/present/working/directory", { base = "/present/foo/bar" })
-- "../../working/directory"
```
### xplr.util.shorten
Shorten the given absolute path using the following rules:
- either relative to your home dir if it makes sense
- or relative to the current working directory
- or absolute path if it makes the most sense
Type: Similar to `xplr.util.relative_to`
Example:
```lua
xplr.util.shorten("/home/username/.config")
-- "~/.config"
xplr.util.shorten("/present/working/directory")
-- "."
xplr.util.shorten("/present/working/directory/foo")
-- "foo"
xplr.util.shorten("/present/working/directory/foo", { with_prefix_dots = true })
-- "./foo"
xplr.util.shorten("/present/working/directory", { without_suffix_dots = true })
-- "../directory"
xplr.util.shorten("/present/working/directory", { base = "/present/foo/bar" })
-- "../../working/directory"
xplr.util.shorten("/tmp")
-- "/tmp"
```
### xplr.util.explore
Explore directories with the given explorer config.
Type: function( path:string, [ExplorerConfig][1]|nil ) -> { [Node][2], ... }
Example:
```lua
xplr.util.explore("/tmp")
-- { { absolute_path = "/tmp/a", ... }, ... }
xplr.util.explore("/tmp", app.explorer_config)
-- { { absolute_path = "/tmp/a", ... }, ... }
```
### xplr.util.shell_execute
Execute shell commands safely.
Type: function( program:string, args:{ string, ... }|nil ) -> { stdout = string, stderr = string, returncode = number|nil }
Example:
```lua
xplr.util.shell_execute("pwd")
-- { stdout = "/present/working/directory", stderr = "", returncode = 0 }
xplr.util.shell_execute("bash", {"-c", "xplr --help"})
-- { stdout = "xplr...", stderr = "", returncode = 0 }
```
### xplr.util.shell_quote
Quote commands and paths safely.
Type: function( string ) -> string
Example:
```lua
xplr.util.shell_quote("a'b\"c")
-- 'a'"'"'b"c'
```
### xplr.util.shell_escape
Escape commands and paths safely.
Type: function( string ) -> string
Example:
```lua
xplr.util.shell_escape("a'b\"c")
-- "\"a'b\\\"c\""
```
### xplr.util.from_json
Load JSON string into Lua value.
Type: function( string ) -> any
Example:
```lua
xplr.util.from_json([[{"foo": "bar"}]])
-- { foo = "bar" }
```
### xplr.util.to_json
Dump Lua value into JSON (i.e. also YAML) string.
Type: function( value ) -> string
Example:
```lua
xplr.util.to_json({ foo = "bar" })
-- [[{ "foo": "bar" }]]
xplr.util.to_json({ foo = "bar" }, { pretty = true })
-- [[{
-- "foo": "bar"
-- }]]
```
### xplr.util.from_yaml
Load YAML (i.e. also JSON) string into Lua value.
Type: function( string ) -> value
Example:
```lua
xplr.util.from_yaml([[{foo: bar}]])
-- { foo = "bar" }
```
### xplr.util.to_yaml
Dump Lua value into YAML string.
Type: function( value ) -> string
Example:
```lua
xplr.util.to_yaml({ foo = "bar" })
-- "foo: bar"
```
### xplr.util.lscolor
Get a [Style][3] object for the given path based on the LS_COLORS
environment variable.
Type: function( path:string ) -> [Style][3]
Example:
```lua
xplr.util.lscolor("Desktop")
-- { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} }
```
### xplr.util.paint
Apply style (escape sequence) to string using a given [Style][3] object.
Type: function( string, [Style][3]|nil ) -> string
Example:
```lua
xplr.util.paint("Desktop", { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} })
-- "\u001b[31mDesktop\u001b[0m"
```
### xplr.util.style_mix
Mix multiple [Style][3] objects into one.
Type: function( { [Style][3], [Style][3], ... } ) -> [Style][3]
Example:
```lua
xplr.util.style_mix({{ fg = "Red" }, { bg = "Blue" }, { add_modifiers = {"Bold"} }})
-- { fg = "Red", bg = "Blue", add_modifiers = { "Bold" }, sub_modifiers = {} }
```
### xplr.util.textwrap
Wrap the given text to fit the specified width.
It will try to not split words when possible.
Type: function( string, options:number|table ) -> { string, ...}
Options type: { width = number, initial_indent = string|nil, subsequent_indent = string|nil, break_words = boolean|nil }
Example:
```lua
xplr.util.textwrap("this will be cut off", 11)
-- { "this will', 'be cut off" }
xplr.util.textwrap(
"this will be cut off",
{ width = 12, initial_indent = "", subsequent_indent = " ", break_words = false }
)
-- { "this will be", " cut off" }
```
### xplr.util.layout_replace
Find the target layout in the given layout and replace it with the replacement layout,
returning a new layout.
Type: function( layout:[Layout][4], target:[Layout][4], replacement:[Layout][4] ) -> layout:[Layout][4]
Example:
```lua
local layout = {
Horizontal = {
splits = {
"Table", -- Target
"HelpMenu",
},
config = ...,
}
}
xplr.util.layout_replace(layout, "Table", "Selection")
-- {
-- Horizontal = {
-- splits = {
-- "Selection", -- Replacement
-- "HelpMenu",
-- },
-- config = ...
-- }
-- }
```
### xplr.util.permissions_rwx
Convert [Permission][8] to rwxrwxrwx representation with special bits.
Type: function( [Permission][8] ) -> string
Example:
```lua
xplr.util.permissions_rwx({ user_read = true })
-- "r--------"
xplr.util.permissions_rwx(app.focused_node.permission)
-- "rwxrwsrwT"
```
### xplr.util.permissions_octal
Convert [Permission][8] to octal representation.
Type: function( [Permission][8] ) -> { number, number, number, number }
Example:
```lua
xplr.util.permissions_octal({ user_read = true })
-- { 0, 4, 0, 0 }
xplr.util.permissions_octal(app.focused_node.permission)
-- { 0, 7, 5, 4 }
```
[1]: https://xplr.dev/en/lua-function-calls#explorer-config
[2]: https://xplr.dev/en/lua-function-calls#node
[3]: https://xplr.dev/en/style
[4]: https://xplr.dev/en/layout
[5]: https://xplr.dev/en/lua-function-calls#node
[6]: https://xplr.dev/en/node-type
[7]: https://xplr.dev/en/node_types
[8]: https://xplr.dev/en/column-renderer#permission

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

@ -0,0 +1,357 @@
<!DOCTYPE HTML>
<html lang="{{ language }}" class="{{ default_theme }}" dir="{{ text_direction }}">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>{{ title }}</title>
{{#if is_print }}
<meta name="robots" content="noindex">
{{/if}}
{{#if base_url}}
<base href="{{ base_url }}">
{{/if}}
<!-- Custom HTML head -->
{{> head}}
<meta name="description" content="{{ description }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
{{#if favicon_svg}}
<link rel="icon" href="{{ path_to_root }}favicon.svg">
{{/if}}
{{#if favicon_png}}
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
{{/if}}
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
{{#if print_enable}}
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
{{/if}}
<!-- Fonts -->
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
{{#if copy_fonts}}
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
{{/if}}
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
<!-- Custom theme stylesheets -->
{{#each additional_css}}
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
{{/each}}
{{#if mathjax_support}}
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{/if}}
<!-- EthicalAds -->
<script async src="https://media.ethicalads.io/media/client/ethicalads.min.js"></script>
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "{{ path_to_root }}";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('{{ default_theme }}')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
{{#toc}}{{/toc}}
<!-- EthicalAds -->
<div
id="docs-sidebar-bottom"
class="dark flat"
data-ea-publisher="xplrdev"
data-ea-type="image"
></div>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
{{> header}}
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
{{/if}}
</div>
<h1 class="menu-title">{{ book_title }}</h1>
<div class="right-buttons">
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
{{/if}}
{{#if git_repository_url}}
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
</a>
{{/if}}
{{#if git_repository_edit_url}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
{{/if}}
</div>
</div>
{{#if search_enabled}}
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
{{/if}}
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
{{{ content }}}
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next prefetch" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
</nav>
</div>
{{#if live_reload_endpoint}}
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
{{/if}}
{{#if google_analytics}}
<!-- Google Analytics Tag -->
<script>
var localAddrs = ["localhost", "127.0.0.1", ""];
// make sure we don't activate google analytics if the developer is
// inspecting the book locally...
if (localAddrs.indexOf(document.location.hostname) === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{google_analytics}}', 'auto');
ga('send', 'pageview');
}
</script>
{{/if}}
{{#if playground_line_numbers}}
<script>
window.playground_line_numbers = true;
</script>
{{/if}}
{{#if playground_copyable}}
<script>
window.playground_copyable = true;
</script>
{{/if}}
{{#if playground_js}}
<script src="{{ path_to_root }}ace.js"></script>
<script src="{{ path_to_root }}editor.js"></script>
<script src="{{ path_to_root }}mode-rust.js"></script>
<script src="{{ path_to_root }}theme-dawn.js"></script>
<script src="{{ path_to_root }}theme-tomorrow_night.js"></script>
{{/if}}
{{#if search_js}}
<script src="{{ path_to_root }}elasticlunr.min.js"></script>
<script src="{{ path_to_root }}mark.min.js"></script>
<script src="{{ path_to_root }}searcher.js"></script>
{{/if}}
<script src="{{ path_to_root }}clipboard.min.js"></script>
<script src="{{ path_to_root }}highlight.js"></script>
<script src="{{ path_to_root }}book.js"></script>
<!-- Custom JS scripts -->
{{#each additional_js}}
<script src="{{ ../path_to_root }}{{this}}"></script>
{{/each}}
{{#if is_print}}
{{#if mathjax_support}}
<script>
window.addEventListener('load', function() {
MathJax.Hub.Register.StartupHook('End', function() {
window.setTimeout(window.print, 100);
});
});
</script>
{{else}}
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
{{/if}}
{{/if}}
</div>
</body>
</html>

@ -0,0 +1 @@
../../assets

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,235 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>xplr - A hackable, minimal, fast TUI file explorer</title>
<meta
name="description"
content="xplr is a hackable, minimal, fast TUI file explorer "
/>
<!--Inter UI font-->
<link href="https://rsms.me/inter/inter.css" rel="stylesheet" />
<!--vendors styles-->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.css"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick-theme.min.css"
/>
<!-- Icons -->
<link
rel="icon"
type="image/png"
sizes="126x128"
href="/assets/icon/xplr128.png"
/>
<link
rel="icon"
type="image/png"
sizes="64x64"
href="/assets/icon/xplr64.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/assets/icon/xplr32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/assets/icon/xplr16.png"
/>
<!-- Bootstrap CSS / Color Scheme -->
<link rel="stylesheet" href="css/default.css" id="theme-color" />
<!-- ------------- SEO TAGS --------------------->
<meta name="title" content="xplr" />
<meta
name="description"
content="A hackable, minimal, fast TUI file explorer"
/>
<meta name="twitter:title" content="xplr" />
<meta
name="twitter:description"
content="A hackable, minimal, fast TUI file explorer"
/>
<meta
name="twitter:image"
content="https://s8.gifyu.com/images/xplr-0.17.6.jpg"
/>
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://xplr.dev" />
<meta property="og:title" content="xplr" />
<meta property="og:type" content="object" />
<meta property="og:url" content="https://xplr.dev" />
<meta
property="og:image"
content="https://s8.gifyu.com/images/xplr-0.17.6.jpg"
/>
<meta itemprop="name" content="xplr" />
<meta
itemprop="description"
content="A hackable, minimal, fast TUI file explorer"
/>
<!-- Youtube Embed -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/paulirish/lite-youtube-embed@master/src/lite-yt-embed.min.css"
/>
<script
defer="defer"
src="https://cdn.jsdelivr.net/gh/paulirish/lite-youtube-embed@master/src/lite-yt-embed.min.js"
></script>
<!-- EthicalAds by "Read The Docs" folks -->
<script
async
src="https://media.ethicalads.io/media/client/ethicalads.min.js"
></script>
</head>
<body>
<!--navigation-->
<section>
<div class="container">
<nav class="navbar navbar-expand-md navbar-dark">
<a class="navbar-brand heading-black" href="/">
<img src="/assets/icon/xplr32.png" alt="xplr" />
</a>
<button
class="navbar-toggler navbar-toggler-right border-0"
type="button"
data-toggle="collapse"
data-target="#navbarCollapse"
aria-controls="navbarCollapse"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span data-feather="grid"></span>
</button>
<div class="collapse navbar-collapse" id="navbarCollapse">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a
class="nav-link page-scroll"
href="/en/introduction.html#features"
>
Features
</a>
</li>
<li class="nav-item">
<a class="nav-link page-scroll" href="/en">Documentation</a>
</li>
<li class="nav-item">
<a class="nav-link page-scroll" href="/en/community.html">
Community
</a>
</li>
<li class="nav-item">
<a
class="nav-link page-scroll"
href="https://blog.xplr.dev"
target="_blank"
rel="noreferrer noopener"
>
Blog
</a>
</li>
<li class="nav-item">
<a
class="nav-link page-scroll"
href="https://github.com/sayanarijit/xplr"
target="_blank"
rel="noreferrer noopener"
>
GitHub
</a>
</li>
</ul>
</div>
</nav>
<hr />
</div>
</section>
<section>
<div class="container">
<div
id="landing-under-navbar"
class="dark flat"
data-ea-publisher="xplrdev"
data-ea-type="text"
></div>
</div>
</section>
<!--hero header-->
<section id="home">
<div class="container">
<div class="row">
<div class="col-md-8 col-sm-10 col-12 mx-auto my-auto text-center">
<h1 class="heading-black">xplr</h1>
<p class="lead">A hackable, minimal, fast TUI file explorer</p>
<div id="QJaEMeVo9Uw" class="eleventy-plugin-youtube-embed pb-3">
<lite-youtube
videoid="QJaEMeVo9Uw"
style="
background-image: url('https://s8.gifyu.com/images/xplr-0.17.6.jpg');
"
>
<div class="lty-playbtn"></div>
</lite-youtube>
</div>
<a
class="btn btn-primary d-inline-flex flex-row align-items-center py-3"
href="/en/install.html"
class="mb-3"
>
Try or Install
<em class="ml-2" data-feather="arrow-right"></em>
</a>
</div>
</div>
</div>
</section>
<!--scroll to top-->
<div class="scroll-top">
<i class="fa fa-angle-up" aria-hidden="true"></i>
</div>
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/feather-icons/4.7.3/feather.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.8.1/slick.min.js"></script>
<script src="js/scripts.js"></script>
</body>
</html>

@ -0,0 +1,73 @@
$(function () {
// init feather icons
feather.replace();
// init tooltip & popovers
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();
//page scroll
$('a.page-scroll').bind('click', function (event) {
var $anchor = $(this);
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top - 20
}, 1000);
event.preventDefault();
});
// slick slider
$('.slick-about').slick({
slidesToShow: 1,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 3000,
dots: true,
arrows: false
});
//toggle scroll menu
var scrollTop = 0;
$(window).scroll(function () {
var scroll = $(window).scrollTop();
//adjust menu background
if (scroll > 80) {
if (scroll > scrollTop) {
$('.smart-scroll').addClass('scrolling').removeClass('up');
} else {
$('.smart-scroll').addClass('up');
}
} else {
// remove if scroll = scrollTop
$('.smart-scroll').removeClass('scrolling').removeClass('up');
}
scrollTop = scroll;
// adjust scroll to top
if (scroll >= 600) {
$('.scroll-top').addClass('active');
} else {
$('.scroll-top').removeClass('active');
}
return false;
});
// scroll top top
$('.scroll-top').click(function () {
$('html, body').stop().animate({
scrollTop: 0
}, 1000);
});
/**Theme switcher - DEMO PURPOSE ONLY */
$('.switcher-trigger').click(function () {
$('.switcher-wrap').toggleClass('active');
});
$('.color-switcher ul li').click(function () {
var color = $(this).attr('data-color');
$('#theme-color').attr("href", "css/" + color + ".css");
$('.color-switcher ul li').removeClass('active');
$(this).addClass('active');
});
});

@ -0,0 +1,561 @@
/*!
* Created by Kroplet (https://www.kroplet.com)
* The easiest way to create Bootstrap 4 themes.
*/
/*****************
Custom CSS
*****************/
body {
border-color: $black;
}
.small-xl {
font-size: 90%;
}
.img-faded {
opacity: 0.5;
}
.font-weight-medium {
font-weight: 600;
}
.heading-black {
font-weight: 800;
}
/* Box shadow */
.btn {
text-transform: uppercase;
font-size: 15px;
@each $color, $value in $theme-colors {
&.btn-#{$color} {
box-shadow: 0 8px 16px rgba($value, 0.3);
transition: all 0.2s ease-out;
&:hover {
box-shadow: 0 8px 20px rgba($value, 0.35);
}
&:active {
box-shadow: none !important;
}
}
}
}
/*Navbar*/
.navbar {
&.navbar-transparent {
opacity: 0.98;
}
@media (max-width: 992px) {
&.navbar-transparent {
background-color: rgba($black, 0.4);
}
}
.navbar-brand {
font-size: 1.5rem;
font-weight: 900;
color: $primary;
text-transform: uppercase;
}
.navbar-nav .nav-item {
margin: 0 0.7rem;
.nav-link {
font-weight: 600;
}
}
}
.section-angle {
position: relative;
border-color: inherit;
background: $black;
&:before,
&:after {
width: 0;
height: 0;
position: absolute;
content: '';
left: 0;
border: 0 solid transparent;
z-index: 3;
}
&.top-left:before,
&.top-right:before {
top: 0;
border-left-width: 100vw;
}
&.bottom-left:after,
&.bottom-right:after {
bottom: 0;
border-right-width: 100vw;
}
&.bottom-left:after {
border-right-color: inherit;
}
&.bottom-right:after {
border-bottom-color: inherit;
}
&.top-left:before {
border-top-color: inherit;
}
&.top-right:before {
border-left-color: inherit;
}
@include media-breakpoint-up('lg') {
&.bottom-right:after,
&.top-right:before {
border-bottom-width: 2rem;
}
&.bottom-left:after,
&.top-left:before {
border-top-width: 2rem;
}
}
@include media-breakpoint-down('sm') {
&.bottom-right:after,
&.top-right:before {
border-bottom-width: 1rem;
}
&.bottom-left:after,
&.top-left:before {
border-top-width: 1rem;
}
}
}
/*smart scrolling*/
.smart-scroll {
position: fixed;
top: 0;
z-index: 1020;
width: 100%;
transition: all 0.3s ease-out;
&.scrolling {
transform: translateY(-100%);
&.up {
background-color: rgba($black, 0.9);
transform: translateY(0);
transition: all 0.3s ease-out;
}
}
}
/**dividers */
.divider {
position: relative;
&.top-divider:before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
display: block;
height: 1px;
background: $gray-900;
background: linear-gradient(
to right,
rgba($primary, 0.1) 0,
$gray-900 50%,
rgba($primary, 0.1) 100%
);
}
&.bottom-divider:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
display: block;
height: 1px;
background: $gray-900;
background: linear-gradient(
to right,
rgba($primary, 0.24) 0,
$gray-900 50%,
rgba($primary, 0.24) 100%
);
}
}
/*vertical heights */
.vh-100 {
height: 100vh;
}
@media (min-width: 576px) {
.vh-sm-100 {
height: 100vh;
}
}
@media (min-width: 768px) {
.vh-md-100 {
height: 100vh;
}
}
.bg-hero {
background-color: $black;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 800 800'%3E%3Cg %3E%3Ccircle fill='#{$black}' cx='400' cy='400' r='600'/%3E%3Ccircle fill='#{darken($primary,40%)}' cx='400' cy='400' r='500'/%3E%3Ccircle fill='#{darken($primary,30%)}' cx='400' cy='400' r='400'/%3E%3Ccircle fill='#{darken($primary,20%)}' cx='400' cy='400' r='300'/%3E%3Ccircle fill='#{darken($primary,10%)}' cx='400' cy='400' r='200'/%3E%3Ccircle fill='#{$primary}' cx='400' cy='400' r='100'/%3E%3C/g%3E%3C/svg%3E");
/* background by SVGBackgrounds.com */
background-attachment: fixed;
background-size: cover;
position: relative;
&:before {
position: absolute;
z-index: 1;
width: 100%;
height: 100%;
display: block;
left: 0;
top: 0;
content: '';
background-color: rgba($black, 0.7);
}
.container {
z-index: 2;
position: relative;
}
}
/*features boxes*/
.feature-boxes {
text-align: center;
.box {
padding: 3rem;
}
}
/*features-list */
.features-list {
list-style: none;
li {
float: left;
width: 50%;
margin-top: 0;
margin-bottom: 1.75rem;
font-size: 1.05rem;
padding-left: 1.75rem;
font-weight: 500;
&:before {
position: relative;
font-family: FontAwesome;
font-size: 14px;
content: '\f10c';
color: $primary;
margin: 0 0.75rem 0 0;
}
}
}
/*Pricing tables*/
.pricing-table {
.pricing-list {
margin-bottom: 3rem;
margin-top: 2rem;
li {
margin-bottom: 1rem;
font-size: 1.05rem;
font-weight: 500;
}
}
.card {
transition: all 0.25s ease-out;
text-align: center;
.card-body {
padding: 2.25rem 2rem;
}
&.active {
color: white;
background-color: transparent;
h2,
h3 {
color: $white;
}
}
}
}
/**slick slider */
.slick-dots {
top: -4rem;
li {
button::before {
font-size: 0.75rem !important;
line-height: 3.5rem !important;
}
button::before,
&.slick-active button:before {
color: $primary !important;
}
}
}
.slick-next:before,
.slick-prev:before {
color: rgba($primary, 0.2);
}
.slick-next:hover:before,
.slick-prev:hover:before {
color: $primary;
}
/*footer*/
footer {
ul > li {
padding: 0.5rem 0;
}
a {
color: rgba($white, 0.7);
text-decoration: none;
font-weight: 500;
transition: all 0.25s ease-out;
&:hover {
color: rgba($white, 0.9);
text-decoration: underline;
}
}
h5 {
font-size: 1rem;
text-transform: uppercase;
}
}
/*social icons*/
.social {
a {
width: 45px;
height: 45px;
background: transparent;
display: block;
text-align: center;
color: gray-100;
border-radius: 4px;
font-size: 18px;
line-height: 45px;
&:hover {
background: $primary;
color: $black;
transition: all 0.4s ease-in-out;
}
}
&.social-sm a {
width: 35px;
height: 35px;
font-size: 16px;
line-height: 35px;
}
&.social-rounded a {
border-radius: 50%;
}
}
/*scroll to top */
.scroll-top {
bottom: 20px;
font-size: 20px;
height: 40px;
position: fixed;
text-align: center;
width: 40px;
z-index: 10;
cursor: pointer;
transition: 0.3s;
border-radius: 50%;
line-height: 40px;
right: -100px;
color: $white;
background-color: rgba($primary, 0.5);
&:hover {
background-color: rgba($primary, 1);
transition: all 0.4s ease-in-out;
}
&.active {
right: 20px;
}
}
/* Icon Boxes */
.icon-box {
position: relative;
border-radius: 50%;
display: inline-block;
vertical-align: middle;
background-color: $white;
margin: 1rem;
@each $color, $value in $theme-colors {
&.box-#{$color} {
color: $value;
background-color: rgba($value, 0.1);
}
}
.icon-box-inner {
display: flex;
flex-direction: row;
align-items: center;
padding: 1.5rem;
&.small {
padding: 1.25rem;
}
&.small-xs {
padding: 1rem;
}
}
}
/*all themes colors*/
.bg-black {
background-color: $black;
}
.bg-blue {
background-color: $blue;
}
.bg-indigo {
background-color: $indigo;
}
.bg-purple {
background-color: $purple;
}
.bg-pink {
background-color: $pink;
}
.bg-red {
background-color: $red;
}
.bg-orange {
background-color: $orange;
}
.bg-yellow {
background-color: $yellow;
}
.bg-green {
background-color: $green;
}
.bg-teal {
background-color: $teal;
}
.bg-cyan {
background-color: $cyan;
}
/*theme switcher*/
.switcher-wrap {
position: fixed;
top: 250px;
width: 250px;
background: $gray-900;
color: $body-color;
z-index: 100;
padding: 20px;
left: -250px;
transition: 0.3s;
&.active {
left: 0;
}
ul {
margin: 0;
padding: 0;
list-style: none;
li {
margin-bottom: 0.5rem;
a {
color: $body-color;
&:hover {
color: $primary;
}
}
}
}
.color-switcher ul li {
width: 28px;
height: 28px;
float: left;
margin: 3px;
margin-bottom: 10px;
cursor: pointer;
transition: 0.3s;
&.active {
border: 3px solid $gray-800;
}
}
.switcher-trigger {
position: absolute;
left: 100%;
width: 40px;
height: 40px;
background: $gray-900;
top: 0;
font-size: 20px;
text-align: center;
color: rgba($primary, 0.5);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0 2px 2px 0;
&:hover {
color: $primary;
}
}
}
@media screen and (max-width: 768px) {
.switcher-wrap {
display: none;
}
}
.ea-text {
text-align: center;
}

@ -0,0 +1,848 @@
/*!
* Created by Kroplet (https://www.kroplet.com)
* The easiest way to create Bootstrap 4 themes.
*/
//
//Colors
//
// Base Colors
$white: #ffffff;
$gray-100: #eceff1;
$gray-200: #cfd8dc;
$gray-300: #b0bec5;
$gray-400: #90a4ae;
$gray-500: #78909c;
$gray-600: #607d8b;
$gray-700: #546e7a;
$gray-800: #455a64;
$gray-900: #263238;
$black: #000000;
$blue: #2979ff;
$indigo: #3d5afe;
$purple: #d500f9;
$pink: #f50057;
$red: #ff1744;
$orange: #ff9100;
$yellow: #ffea00;
$green: #00e676;
$teal: #1de9b6;
$cyan: #00e5ff;
// Theme Colors
$primary: $teal;
$secondary: $white;
$success: $green;
$info: $cyan;
$warning: $yellow;
$danger: $red;
$light: $gray-100;
$dark: darken($gray-900, 10%);
$theme-colors: ();
$theme-colors: map-merge(
(
'primary': $primary,
'secondary': $secondary,
'success': $success,
'info': $info,
'warning': $warning,
'danger': $danger,
'light': $light,
'dark': $dark,
),
$theme-colors
);
$theme-color-interval: 8%;
$yiq-contrasted-threshold: 150;
$yiq-text-dark: darken($gray-900, 10%);
$yiq-text-light: $white;
//
//Global
//
$enable-caret: true;
$enable-rounded: true;
$enable-shadows: false;
$enable-gradients: false;
$enable-transitions: true;
$enable-hover-media-query: false;
$enable-grid-classes: true;
$enable-print-styles: true;
//
//Spacing
//
$spacer: 1rem;
$spacers: (
0: 0,
1: (
$spacer * 0.25,
),
2: (
$spacer * 0.5,
),
3: $spacer,
4: (
$spacer * 1.5,
),
5: (
$spacer * 3,
),
6: (
$spacer * 6,
),
7: (
$spacer * 9,
),
8: (
$spacer * 12,
),
9: (
$spacer * 15,
),
);
$sizes: (
25: 25%,
50: 50%,
75: 75%,
100: 100%,
);
//
//Body
//
$body-bg: $black;
$body-color: $gray-200;
//
//Links
//
$link-color: $primary;
$link-decoration: none;
$link-hover-color: darken($primary, 20%);
$link-hover-decoration: none;
//
//Paragraphs
//
$paragraph-margin-bottom: 1rem;
//
//GridBreakpoints
//
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
);
//
//GridContainers
//
$container-max-widths: (
sm: 540px,
md: 720px,
lg: 960px,
xl: 1140px,
);
//
//GridColumns
//
$grid-columns: 12;
$grid-gutter-width: 30px;
//
//Components
//
$line-height-lg: 1.5;
$line-height-sm: 1.5;
$border-width: 2px;
$border-color: $gray-200;
$border-radius: 0.3rem;
$border-radius-lg: 0.4rem;
$border-radius-sm: 0.25rem;
$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075);
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15);
$box-shadow-lg: 0 1rem 3rem rgba($black, 0.175);
$component-active-color: $white;
$component-active-bg: theme-color('primary');
$caret-width: 0.3em;
$transition-base: all 0.25s ease-in-out;
$transition-fade: opacity 0.15s linear;
$transition-collapse: height 0.35s ease;
//
//Fonts
//
$font-family-sans-serif: 'Inter UI', -apple-system, BlinkMacSystemFont,
'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', 'Segoe UI Symbol';
$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
$font-family-base: $font-family-sans-serif;
$font-size-base: 1rem;
$font-size-lg: ($font-size-base * 1.25);
$font-size-sm: ($font-size-base * 0.875);
$font-weight-light: 300;
$font-weight-normal: 400;
$font-weight-bold: 700;
$font-weight-base: $font-weight-normal;
$line-height-base: 1.5;
$h1-font-size: $font-size-base * 3;
$h2-font-size: $font-size-base * 2.5;
$h3-font-size: $font-size-base * 1.5;
$h4-font-size: $font-size-base * 1.375;
$h5-font-size: $font-size-base * 1.25;
$h6-font-size: $font-size-base;
$headings-margin-bottom: $spacer;
$headings-font-family: inherit;
$headings-font-weight: $font-weight-bold;
$headings-line-height: 1.5;
$headings-color: $white;
$display1-size: 5rem;
$display2-size: 4.5rem;
$display3-size: 3.5rem;
$display4-size: 2.5rem;
$display1-weight: 300;
$display2-weight: 300;
$display3-weight: 300;
$display4-weight: 300;
$display-line-height: $headings-line-height;
$lead-font-size: ($font-size-base * 1.2);
$lead-font-weight: 500;
$small-font-size: 80%;
$text-muted: $gray-500;
$blockquote-small-color: $gray-500;
$blockquote-font-size: ($font-size-base * 1.25);
$hr-border-color: $gray-800;
$hr-border-width: $border-width;
$mark-padding: 0.2em;
$dt-font-weight: $font-weight-bold;
$kbd-box-shadow: inset 0 -0.1rem 0 rgba($black, 0.25);
$nested-kbd-font-weight: $font-weight-bold;
$list-inline-padding: 0.5rem;
$mark-bg: #fcf8e3;
$hr-margin-y: $spacer;
//
//Tables
//
$table-cell-padding: 0.75rem;
$table-cell-padding-sm: 0.3rem;
$table-bg: transparent;
$table-accent-bg: rgba($black, 0.05);
$table-hover-bg: rgba($black, 0.075);
$table-active-bg: $table-hover-bg;
$table-border-width: $border-width;
$table-border-color: $gray-100;
$table-head-bg: $gray-100;
$table-head-color: $gray-700;
$table-dark-bg: $gray-900;
$table-dark-accent-bg: rgba($white, 0.05);
$table-dark-hover-bg: rgba($white, 0.075);
$table-dark-border-color: lighten($gray-900, 7.5%);
$table-dark-color: $body-bg;
$table-striped-order: odd;
$table-caption-color: $text-muted;
//
//Buttons
//
$input-btn-padding-y: 0.55rem;
$input-btn-padding-x: 1.5rem;
$input-btn-line-height: $line-height-base;
$input-btn-focus-width: 0.2rem;
$input-btn-focus-color: rgba(theme-color('primary'), 0.25);
$input-btn-focus-box-shadow: none;
$input-btn-padding-y-sm: 0.375rem;
$input-btn-padding-x-sm: 1rem;
$input-btn-line-height-sm: $line-height-sm;
$input-btn-padding-y-lg: 0.75rem;
$input-btn-padding-x-lg: 1.5rem;
$input-btn-line-height-lg: $line-height-lg;
$input-btn-border-width: $border-width;
$btn-padding-y: $input-btn-padding-y;
$btn-padding-x: $input-btn-padding-x;
$btn-line-height: $input-btn-line-height;
$btn-padding-y-sm: $input-btn-padding-y-sm;
$btn-padding-x-sm: $input-btn-padding-x-sm;
$btn-line-height-sm: $input-btn-line-height-sm;
$btn-padding-y-lg: $input-btn-padding-y-lg;
$btn-padding-x-lg: $input-btn-padding-x-lg;
$btn-line-height-lg: $input-btn-line-height-lg;
$btn-border-width: $input-btn-border-width;
$btn-font-weight: 500;
$btn-box-shadow: 0 2px 8px rgba($black, 0.1);
$btn-focus-width: $input-btn-focus-width;
$btn-focus-box-shadow: $input-btn-focus-box-shadow;
$btn-disabled-opacity: 0.65;
$btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125);
$btn-link-disabled-color: $gray-600;
$btn-block-spacing-y: 0.5rem;
$btn-border-radius: $border-radius;
$btn-border-radius-lg: $border-radius-lg;
$btn-border-radius-sm: $border-radius-sm;
$btn-transition: all 0.2s;
//
//Forms
//
$label-margin-bottom: 0.5rem;
$input-padding-y: $input-btn-padding-y;
$input-padding-x: $input-btn-padding-x;
$input-line-height: $input-btn-line-height;
$input-padding-y-sm: $input-btn-padding-y-sm;
$input-padding-x-sm: $input-btn-padding-x-sm;
$input-line-height-sm: $input-btn-line-height-sm;
$input-padding-y-lg: $input-btn-padding-y-lg;
$input-padding-x-lg: $input-btn-padding-x-lg;
$input-line-height-lg: $input-btn-line-height-lg;
$input-bg: $white;
$input-disabled-bg: $gray-100;
$input-color: $gray-700;
$input-border-color: $border-color;
$input-border-width: $border-width;
$input-box-shadow: none;
$input-border-radius: $border-radius;
$input-border-radius-lg: $border-radius-lg;
$input-border-radius-sm: $border-radius-sm;
$input-focus-bg: $input-bg;
$input-focus-border-color: $primary;
$input-focus-color: $input-color;
$input-focus-width: $input-btn-focus-width;
$input-focus-box-shadow: $input-btn-focus-box-shadow;
$input-placeholder-color: $gray-500;
$input-plaintext-color: $body-color;
$input-height-border: $input-btn-border-width * 2;
$input-height-inner: ($font-size-base * $input-btn-line-height) +
($input-btn-padding-y * 2);
$input-height: calc(#{$input-height-inner} + #{$input-height-border});
$input-height-inner-sm: ($font-size-sm * $input-btn-line-height-sm) +
($input-btn-padding-y-sm * 2);
$input-height-sm: calc(#{$input-height-inner-sm} + #{$input-height-border});
$input-height-inner-lg: ($font-size-lg * $input-btn-line-height-lg) +
($input-btn-padding-y-lg * 2);
$input-height-lg: calc(#{$input-height-inner-lg} + #{$input-height-border});
$input-transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
$form-text-margin-top: 0.25rem;
$form-check-input-gutter: 1.25rem;
$form-check-input-margin-y: 0.3rem;
$form-check-input-margin-x: 0.25rem;
$form-check-inline-margin-x: 0.75rem;
$form-check-inline-input-margin-x: 0.3125rem;
$form-group-margin-bottom: 1rem;
$input-group-addon-color: $white;
$input-group-addon-bg: $dark;
$input-group-addon-border-color: $dark;
$form-feedback-margin-top: $form-text-margin-top;
$form-feedback-font-size: $small-font-size;
$form-feedback-valid-color: theme-color('success');
$form-feedback-invalid-color: theme-color('danger');
//
//CustomForms
//
$custom-control-gutter: 1.75rem;
$custom-control-spacer-y: 0.25rem;
$custom-control-spacer-x: 1rem;
$custom-control-indicator-size: 1.125rem;
$custom-control-indicator-bg: $gray-100;
$custom-control-indicator-bg-size: 50% 50%;
$custom-control-indicator-box-shadow: inset 0 0.25rem 0.25rem rgba($black, 0.1);
$custom-control-indicator-disabled-bg: $input-disabled-bg;
$custom-control-label-disabled-color: $gray-200;
$custom-control-indicator-checked-color: $white;
$custom-control-indicator-checked-bg: theme-color('primary');
$custom-control-indicator-checked-box-shadow: none;
$custom-control-indicator-focus-box-shadow: 0 0 0 1px $body-bg,
$input-btn-focus-box-shadow;
$custom-control-indicator-active-color: $white;
$custom-control-indicator-active-bg: lighten(theme-color('primary'), 35%);
$custom-control-indicator-active-box-shadow: none;
$custom-checkbox-indicator-border-radius: $border-radius;
$custom-checkbox-indicator-icon-checked: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='#{$custom-control-indicator-checked-color}' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"),
'#',
'%23'
);
$custom-checkbox-indicator-indeterminate-bg: theme-color('primary');
$custom-checkbox-indicator-indeterminate-color: $custom-control-indicator-checked-color;
$custom-checkbox-indicator-icon-indeterminate: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='#{$custom-checkbox-indicator-indeterminate-color}' d='M0 2h4'/%3E%3C/svg%3E"),
'#',
'%23'
);
$custom-checkbox-indicator-indeterminate-box-shadow: none;
$custom-radio-indicator-border-radius: 50%;
$custom-radio-indicator-icon-checked: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='#{$custom-control-indicator-checked-color}'/%3E%3C/svg%3E"),
'#',
'%23'
);
$custom-select-padding-y: 0.375rem;
$custom-select-padding-x: 0.75rem;
$custom-select-height: $input-height;
$custom-select-indicator-padding: 1rem;
$custom-select-line-height: $input-btn-line-height;
$custom-select-color: $input-color;
$custom-select-disabled-color: $gray-600;
$custom-select-bg: $input-bg;
$custom-select-disabled-bg: $input-disabled-bg;
$custom-select-bg-size: 8px 10px;
$custom-select-indicator-color: $gray-800;
$custom-select-indicator: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='#{$custom-select-indicator-color}' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E"),
'#',
'%23'
);
$custom-select-border-width: $input-btn-border-width;
$custom-select-border-color: $input-border-color;
$custom-select-border-radius: $border-radius;
$custom-select-focus-border-color: $input-focus-border-color;
$custom-select-focus-box-shadow: inset 0 1px 2px rgba($black, 0.075),
$input-btn-focus-box-shadow;
$custom-select-font-size-sm: 75%;
$custom-select-height-sm: $input-height-sm;
$custom-select-font-size-lg: 125%;
$custom-select-height-lg: $input-height-lg;
$custom-range-track-width: 100%;
$custom-range-track-height: 0.5rem;
$custom-range-track-cursor: pointer;
$custom-range-track-bg: $gray-300;
$custom-range-track-border-radius: 1rem;
$custom-range-track-box-shadow: inset 0 0.25rem 0.25rem rgba($black, 0.1);
$custom-range-thumb-width: 1rem;
$custom-range-thumb-height: $custom-range-thumb-width;
$custom-range-thumb-bg: $component-active-bg;
$custom-range-thumb-border: 0;
$custom-range-thumb-border-radius: 1rem;
$custom-range-thumb-box-shadow: 0 0.1rem 0.25rem rgba($black, 0.1);
$custom-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg,
$input-btn-focus-box-shadow;
$custom-range-thumb-active-bg: lighten($component-active-bg, 35%);
$custom-file-height: $input-height;
$custom-file-focus-border-color: $input-focus-border-color;
$custom-file-focus-box-shadow: $input-btn-focus-box-shadow;
$custom-file-padding-y: $input-btn-padding-y;
$custom-file-padding-x: $input-btn-padding-x;
$custom-file-line-height: $input-btn-line-height;
$custom-file-color: $gray-300;
$custom-file-bg: $input-bg;
$custom-file-border-width: $input-btn-border-width;
$custom-file-border-color: $dark;
$custom-file-border-radius: $input-border-radius;
$custom-file-box-shadow: $input-box-shadow;
$custom-file-button-color: $white;
$custom-file-button-bg: $input-group-addon-bg;
$custom-file-text: (
en: 'Browse',
);
//
//Dropdowns
//
$dropdown-min-width: 10rem;
$dropdown-padding-y: 0.75rem;
$dropdown-spacer: 0.125rem;
$dropdown-bg: $white;
$dropdown-border-color: $gray-100;
$dropdown-border-radius: $border-radius;
$dropdown-border-width: $border-width;
$dropdown-divider-bg: $gray-100;
$dropdown-box-shadow: 0 0.5rem 1rem rgba($black, 0.175);
$dropdown-link-color: $gray-700;
$dropdown-link-hover-color: $gray-900;
$dropdown-link-hover-bg: $gray-100;
$dropdown-link-active-color: $component-active-color;
$dropdown-link-active-bg: $component-active-bg;
$dropdown-link-disabled-color: $gray-600;
$dropdown-item-padding-y: 0.25rem;
$dropdown-item-padding-x: 1.5rem;
$dropdown-header-color: $gray-400;
//
//ZindexMasterList
//
$zindex-dropdown: 1000;
$zindex-sticky: 1020;
$zindex-fixed: 1030;
$zindex-modal-backdrop: 1040;
$zindex-modal: 1050;
$zindex-popover: 1060;
$zindex-tooltip: 1070;
//
//Navs
//
$nav-link-padding-y: 0.25rem;
$nav-link-padding-x: 1rem;
$nav-link-disabled-color: $gray-400;
$nav-tabs-border-color: $gray-100;
$nav-tabs-border-width: $border-width;
$nav-tabs-border-radius: $border-radius;
$nav-tabs-link-hover-border-color: $gray-100 $gray-100 $nav-tabs-border-color;
$nav-tabs-link-active-color: $white;
$nav-tabs-link-active-bg: $dark;
$nav-tabs-link-active-border-color: $gray-200 $gray-200 $nav-tabs-link-active-bg;
$nav-pills-border-radius: $border-radius;
$nav-pills-link-active-color: $component-active-color;
$nav-pills-link-active-bg: $component-active-bg;
$nav-divider-color: $gray-200;
$nav-divider-margin-y: ($spacer / 2);
//
//Navbar
//
$navbar-padding-y: 1.5rem;
$navbar-padding-x: 1.25rem;
$navbar-nav-link-padding-x: 0.75rem;
$navbar-brand-font-size: $font-size-lg;
$nav-link-height: (
$font-size-base * $line-height-base + $nav-link-padding-y * 2
);
$navbar-brand-height: $navbar-brand-font-size * $line-height-base;
$navbar-brand-padding-y: ($nav-link-height - $navbar-brand-height) / 2;
$navbar-toggler-padding-y: 0.25rem;
$navbar-toggler-padding-x: 0.75rem;
$navbar-toggler-font-size: $font-size-lg;
$navbar-toggler-border-radius: $btn-border-radius;
$navbar-dark-color: rgba($white, 0.7);
$navbar-dark-hover-color: rgba($white, 0.9);
$navbar-dark-active-color: $white;
$navbar-dark-disabled-color: rgba($white, 0.3);
$navbar-dark-toggler-icon-bg: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-dark-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"),
'#',
'%23'
);
$navbar-dark-toggler-border-color: rgba($white, 0.1);
$navbar-light-color: rgba($black, 0.7);
$navbar-light-hover-color: rgba($primary, 0.9);
$navbar-light-active-color: $primary;
$navbar-light-disabled-color: rgba($black, 0.3);
$navbar-light-toggler-icon-bg: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='#{$navbar-light-color}' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"),
'#',
'%23'
);
$navbar-light-toggler-border-color: rgba($black, 0.1);
//
//Pagination
//
$pagination-padding-y: 0.5rem;
$pagination-padding-x: 0.75rem;
$pagination-padding-y-sm: 0.25rem;
$pagination-padding-x-sm: 0.5rem;
$pagination-padding-y-lg: 0.75rem;
$pagination-padding-x-lg: 1.5rem;
$pagination-line-height: 1;
$pagination-color: $white;
$pagination-bg: $dark;
$pagination-border-width: 0px;
$pagination-border-color: transparent;
$pagination-focus-box-shadow: $input-btn-focus-box-shadow;
$pagination-focus-outline: 0;
$pagination-hover-color: $gray-100;
$pagination-hover-bg: $gray-600;
$pagination-hover-border-color: $gray-700;
$pagination-active-color: $white;
$pagination-active-bg: $gray-600;
$pagination-active-border-color: $gray-700;
$pagination-disabled-color: $white;
$pagination-disabled-bg: $gray-400;
$pagination-disabled-border-color: transparent;
//
//Jumbotron
//
$jumbotron-padding: 2rem;
$jumbotron-bg: $gray-100;
//
//Cards
//
$card-spacer-y: 0.75rem;
$card-spacer-x: 1.25rem;
$card-border-width: 0px;
$card-border-radius: $border-radius;
$card-border-color: rgba($black, 0.125);
$card-inner-border-radius: calc(#{$card-border-radius} - #{$card-border-width});
$card-cap-bg: rgba($black, 0.03);
$card-bg: $gray-900;
$card-img-overlay-padding: 1.25rem;
$card-group-margin: ($grid-gutter-width / 2);
$card-deck-margin: $card-group-margin;
$card-columns-count: 3;
$card-columns-gap: 1.25rem;
$card-columns-margin: $card-spacer-y;
//
//Tooltips
//
$tooltip-font-size: $font-size-sm;
$tooltip-max-width: 200px;
$tooltip-color: $white;
$tooltip-bg: $black;
$tooltip-border-radius: $border-radius;
$tooltip-opacity: 0.9;
$tooltip-padding-y: 0.25rem;
$tooltip-padding-x: 0.5rem;
$tooltip-margin: 0;
$tooltip-arrow-width: 0.8rem;
$tooltip-arrow-height: 0.4rem;
$tooltip-arrow-color: $tooltip-bg;
//
//Popovers
//
$popover-font-size: $font-size-sm;
$popover-bg: $white;
$popover-max-width: 276px;
$popover-border-width: $border-width;
$popover-border-color: rgba($black, 0.2);
$popover-border-radius: $border-radius-lg;
$popover-box-shadow: 0 0.25rem 0.5rem rgba($black, 0.2);
$popover-header-bg: $dark;
$popover-header-color: $white;
$popover-header-padding-y: 0.65rem;
$popover-header-padding-x: 0.85rem;
$popover-body-color: $body-color;
$popover-body-padding-y: $popover-header-padding-y;
$popover-body-padding-x: $popover-header-padding-x;
$popover-arrow-width: 1rem;
$popover-arrow-height: 0.5rem;
$popover-arrow-color: $popover-bg;
$popover-arrow-outer-color: fade-in($popover-border-color, 0.05);
//
//Badges
//
$badge-font-size: 75%;
$badge-font-weight: $font-weight-bold;
$badge-padding-y: 0.35em;
$badge-padding-x: 0.5em;
$badge-border-radius: 4px;
$badge-pill-padding-x: 0.6em;
$badge-pill-border-radius: 10rem;
//
//Modals
//
$modal-inner-padding: 1.5rem;
$modal-dialog-margin: 0.5rem;
$modal-dialog-margin-y-sm-up: 1.75rem;
$modal-title-line-height: $line-height-base;
$modal-content-bg: $white;
$modal-content-border-color: $modal-content-bg;
$modal-content-border-width: $border-width;
$modal-content-border-radius: $border-radius-lg;
$modal-content-box-shadow-xs: 0 0.25rem 0.5rem rgba($black, 0.5);
$modal-content-box-shadow-sm-up: 0 0.5rem 1rem rgba($black, 0.5);
$modal-backdrop-bg: $black;
$modal-backdrop-opacity: 0.5;
$modal-header-border-color: $modal-content-bg;
$modal-footer-border-color: $modal-header-border-color;
$modal-header-border-width: $modal-content-border-width;
$modal-footer-border-width: $modal-header-border-width;
$modal-header-padding: 1.5rem;
$modal-lg: 800px;
$modal-md: 600px;
$modal-sm: 400px;
$modal-transition: transform 0.3s ease-out;
//
//Alerts
//
$alert-padding-y: 0.75rem;
$alert-padding-x: 1.5rem;
$alert-margin-bottom: 1rem;
$alert-border-radius: $border-radius;
$alert-link-font-weight: $font-weight-bold;
$alert-border-width: $border-width;
$alert-bg-level: -11;
$alert-border-level: -11;
$alert-color-level: 6;
//
//ProgressBars
//
$progress-height: 0.375rem;
$progress-font-size: ($font-size-base * 0.75);
$progress-bg: $gray-200;
$progress-border-radius: $border-radius;
$progress-box-shadow: inset 0 0.1rem 0.1rem rgba($black, 0.1);
$progress-bar-color: $white;
$progress-bar-bg: theme-color('primary');
$progress-bar-animation-timing: 1s linear infinite;
$progress-bar-transition: width 0.6s ease;
//
//ListGroup
//
$list-group-bg: $white;
$list-group-border-color: rgba($black, 0.125);
$list-group-border-width: $border-width;
$list-group-border-radius: $border-radius;
$list-group-item-padding-y: 0.75rem;
$list-group-item-padding-x: 1.25rem;
$list-group-hover-bg: $gray-100;
$list-group-active-color: $component-active-color;
$list-group-active-bg: $component-active-bg;
$list-group-active-border-color: $list-group-active-bg;
$list-group-disabled-color: $gray-400;
$list-group-disabled-bg: $list-group-bg;
$list-group-action-color: $gray-700;
$list-group-action-hover-color: $list-group-action-color;
$list-group-action-active-color: $body-color;
$list-group-action-active-bg: $gray-100;
//
//Images
//
$thumbnail-padding: 0px;
$thumbnail-bg: $white;
$thumbnail-border-width: 2px;
$thumbnail-border-color: $gray-100;
$thumbnail-border-radius: $border-radius;
$thumbnail-box-shadow: 0 1px 2px rgba($black, 0.075);
//
//Figures
//
$figure-caption-font-size: 90%;
$figure-caption-color: $gray-400;
//
//Breadcrumbs
//
$breadcrumb-padding-y: 0.75rem;
$breadcrumb-padding-x: 1rem;
$breadcrumb-item-padding: 0.5rem;
$breadcrumb-margin-bottom: 1rem;
$breadcrumb-bg: $gray-100;
$breadcrumb-divider-color: $gray-400;
$breadcrumb-active-color: $gray-400;
$breadcrumb-divider: '/';
$breadcrumb-border-radius: $border-radius;
//
//Carousel
//
$carousel-control-color: $white;
$carousel-control-width: 15%;
$carousel-control-opacity: 0.5;
$carousel-indicator-width: 30px;
$carousel-indicator-height: 3px;
$carousel-indicator-spacer: 3px;
$carousel-indicator-active-bg: $white;
$carousel-caption-width: 70%;
$carousel-caption-color: $white;
$carousel-control-icon-width: 20px;
$carousel-control-prev-icon-bg: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"),
'#',
'%23'
);
$carousel-control-next-icon-bg: str-replace(
url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='#{$carousel-control-color}' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"),
'#',
'%23'
);
$carousel-transition: transform 0.6s ease;
//
//Close
//
$close-font-size: $font-size-base * 2;
$close-font-weight: $font-weight-bold;
$close-color: $black;
$close-text-shadow: 0 1px 0 $white;
//
//Code
//
$code-font-size: 87.5%;
$code-color: $pink;
$kbd-padding-y: 0.2rem;
$kbd-padding-x: 0.4rem;
$kbd-font-size: $code-font-size;
$kbd-color: $white;
$kbd-bg: $gray-900;
$pre-color: $gray-900;
$pre-scrollable-max-height: 340px;
$print-page-size: a3;
$print-body-min-width: map-get($grid-breakpoints, 'lg');
//
//Extra SASS variables
//
$link-border-width: 0px;
$link-border-style: solid;
$link-border-color: transparent;
$link-hover-border-width: 0px;
$link-hover-border-style: solid;
$link-hover-border-color: transparent;
$link-font-size: inherit;
$link-font-weight: inherit;
$link-background-color: transparent;
$link-hover-background-color: transparent;
$link-footer-color: $link-color;
$link-footer-decoration: $link-decoration;
$link-footer-hover-color: $link-hover-color;
$link-footer-hover-decoration: $link-hover-decoration;
$paragraph-color: inherit;
$paragraph-bold-text-weight: bolder;
$paragraph-bold-text-color: inherit;
$btn-text-transform: none;
$btn-font-size: $font-size-base;
$btn-font-size-lg: $font-size-lg;
$btn-font-size-sm: $font-size-sm;
$btn-background-image: none;
$btn-hover-background-image: none;
$navbar-nav-link-padding-y: 0.5rem;
$navbar-nav-link-text-transform: none;
$navbar-nav-link-font-size: inherit;
$navbar-nav-link-font-weight: inherit;

@ -0,0 +1,11 @@
v="0.4.40"
curl -L https://github.com/rust-lang/mdBook/releases/download/v$v/mdbook-v$v-x86_64-unknown-linux-gnu.tar.gz -o mdbook.tgz \
&& tar xzvf mdbook.tgz \
&& ./mdbook build docs/en \
&& mkdir dist \
&& mv -v docs/en/book/html dist/en \
&& mv -v assets dist \
&& mv -v docs/landing/index.html docs/landing/css docs/landing/js dist \
&& rm -v mdbook \
&& rm -v mdbook.tgz

@ -0,0 +1,279 @@
"""Generate docs from comments."""
import os
from dataclasses import dataclass
from typing import List
# Messages --------------------------------------------------------------------
MESSAGES_DOC_TEMPLATE = """
# Full List of Messages
xplr [messages][1] categorized based on their purpose.
## Categories
{categories}
{msgs}
## Also See:
- [Message][1]
[1]: message.md
""".strip()
CONFIGURATION_DOC_TEMPLATE = """
# Configuration
{doc}
""".strip()
@dataclass
class MsgSection:
title: str
body: List[str]
@dataclass
class MsgCategory:
title: str
sections: List[MsgSection]
@dataclass
class MsgResult:
categories: List[MsgCategory]
msgs: List[str]
def gen_messages():
"""Generate messages.md"""
path = "./src/msg/in_/external.rs"
res = []
reading = False
with open(path) as f:
lines = iter(f.read().splitlines())
for line in lines:
line = line.strip()
if line.startswith("pub enum ExternalMsg {"):
reading = True
continue
if not reading:
continue
if line == "}":
break
if line.startswith("/// ### "):
line = line.lstrip("/// ### ").rstrip("-").strip()
sec = MsgSection(title=None, body=[])
cat = MsgCategory(title=line, sections=[sec])
res.append(cat)
continue
if line.startswith("/// "):
line = line.lstrip("/// ").strip()
res[-1].sections[-1].body.append(line)
continue
if not line or line == "///":
res[-1].sections[-1].body.append("")
continue
if line.endswith(","):
line = line.split(",")[0].split("(")[0]
res[-1].sections[-1].title = line
sec = MsgSection(title=None, body=[])
res[-1].sections.append(sec)
continue
result = MsgResult(categories=[], msgs=[])
for cat in res:
slug = cat.title.lower().replace(" ", "-")
result.categories.append(f"- [{cat.title}](#{slug})")
result.msgs.append(f"### {cat.title}")
result.msgs.append("")
for sec in cat.sections:
if not sec.title:
continue
result.msgs.append(f"#### {sec.title}")
result.msgs.append("")
for line in sec.body:
result.msgs.append(f"{line}")
result.msgs.append("")
messages = MESSAGES_DOC_TEMPLATE.format(
categories="\n".join(result.categories), msgs="\n".join(result.msgs)
)
print(messages)
with open("./docs/en/src/messages.md", "w") as f:
print(messages, file=f)
# Configuration ---------------------------------------------------------------
def gen_configuration():
"""Generate the following docs.
- configuration.md
- general-config.md
- node_types.md
- layouts.md
- modes.md
- modes.md
"""
path = "./src/init.lua"
configuration = [[]]
general = [[]]
node_types = [[]]
layouts = [[]]
modes = [[]]
with open(path) as f:
lines = iter(f.read().splitlines())
reading = None
for line in lines:
if line.startswith("---"):
continue
if (
line.startswith("-- # Configuration ")
or line.startswith("-- ## Config ")
or line.startswith("-- ## Function ")
or line.startswith("-- ## On Load ")
):
reading = configuration
if line.startswith("-- ### General Configuration "):
reading = general
if line.startswith("-- ### Node Types "):
reading = node_types
if line.startswith("-- ### Layouts "):
reading = layouts
if line.startswith("-- ### Modes "):
reading = modes
if not reading:
continue
if line.startswith("-- ") or line == "--":
if line.startswith("-- #") and line.endswith("--"):
line = "\n{0}\n".format(line.rstrip("-"))
reading[-1].append(line[3:])
continue
if line.startswith("xplr.") and reading[-1]:
reading[-1].insert(0, "\n#### {0}\n".format(line.split()[0]))
continue
if not line.strip() and reading[-1]:
reading.append([])
continue
with open("./docs/en/src/configuration.md", "w") as f:
doc = "\n".join(["\n".join(c) for c in configuration])
print(doc)
print(doc, file=f)
with open("./docs/en/src/general-config.md", "w") as f:
doc = "\n".join(["\n".join(c) for c in general])
print(doc)
print(doc, file=f)
with open("./docs/en/src/node_types.md", "w") as f:
doc = "\n".join(["\n".join(c) for c in node_types])
print(doc)
print(doc, file=f)
with open("./docs/en/src/layouts.md", "w") as f:
doc = "\n".join(["\n".join(c) for c in layouts])
print(doc)
print(doc, file=f)
with open("./docs/en/src/modes.md", "w") as f:
doc = "\n".join(["\n".join(c) for c in modes])
print(doc)
print(doc, file=f)
# xplr.util -------------------------------------------------------------------
@dataclass
class Function:
doc: List[str]
name: str
def gen_xplr_util():
path = "./src/lua/util.rs"
functions: List[Function] = []
with open(path) as f:
lines = iter(f.read().splitlines())
reading = None
for line in lines:
if line.startswith("///"):
if reading:
reading.doc.append(line[4:])
else:
reading = Function(doc=[line[4:]], name="")
if line.startswith("pub fn") and reading:
reading.name = "\n### xplr.util." + line.split("<")[0].split()[-1] + "\n"
functions.append(reading)
reading = None
continue
with open("./docs/en/src/xplr.util.md", "w") as f:
for function in functions:
print(function.name)
print(function.name, file=f)
print("\n".join(function.doc))
print("\n".join(function.doc), file=f)
if reading:
print("\n".join(reading.doc), file=f)
def format_docs():
os.system("prettier --write docs/en/src")
def main():
gen_messages()
gen_configuration()
gen_xplr_util()
format_docs()
if __name__ == "__main__":
main()

@ -0,0 +1,13 @@
fn main() {
match xplr::runner::runner().and_then(|a| a.run()) {
Ok(Some(out)) => print!("{}", out),
Ok(None) => {}
Err(err) => {
if !err.to_string().is_empty() {
eprintln!("error: {}", err);
};
std::process::exit(1);
}
}
}

@ -0,0 +1,26 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1704262187,
"narHash": "sha256-N4j9qghlp/Eb3Z11WF7Cb9U91AXwpascUbLH7YKMcLc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "65f0d241783c94a08e4c9a3870736fc8854dd520",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

@ -0,0 +1,70 @@
{
description = "xplr - A hackable, minimal, fast TUI file explorer";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
};
outputs = inputs@{ self, nixpkgs, ... }:
let
lib = nixpkgs.lib;
darwin = [ "x86_64-darwin" "aarch64-darwin" ];
linux = [ "x86_64-linux" "x86_64-linux-musl" "aarch64-linux" "aarch64-linux-android" "i86_64-linux" ];
allSystems = darwin ++ linux;
forEachSystem = systems: f: lib.genAttrs systems (system: f system);
forAllSystems = forEachSystem allSystems;
in
{
packages = forAllSystems (system:
let
pkgs = import nixpkgs { inherit system; };
in
rec {
# e.g. nix build .#xplr
xplr = pkgs.rustPlatform.buildRustPackage rec {
name = "xplr";
src = ./.;
cargoLock = {
lockFile = ./Cargo.lock;
};
};
# e.g. nix build .#cross.x86_64-linux-musl.xplr --impure
cross = forEachSystem (lib.filter (sys: sys != system) allSystems) (targetSystem:
let
crossPkgs = import nixpkgs { localSystem = system; crossSystem = targetSystem; };
in
{ inherit (crossPkgs) xplr; }
);
}
);
defaultPackage = forAllSystems (system: self.packages.${system}.xplr);
devShells = forAllSystems (system:
let
pkgs = import nixpkgs { inherit system; };
devRequirements = with pkgs; [
gcc
gnumake
clippy
rustc
cargo
rustfmt
rust-analyzer
];
in
{
default = pkgs.mkShell {
RUST_BACKTRACE = 1;
# For cross compilation
NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM = 1;
buildInputs = devRequirements;
packages = devRequirements;
};
}
);
};
}

@ -0,0 +1,4 @@
edition = "2021"
max_width = 89
tab_spaces = 4
use_field_init_shorthand = true

@ -0,0 +1,26 @@
name: xplr
version: git
summary: A hackable, minimal, fast TUI file explorer
description: |
xplr is a terminal UI based file explorer
that aims to increase our terminal productivity by being a flexible,
interactive orchestrator for the ever growing awesome command-line
utilities that work with the file-system.
source-code: https://github.com/sayanarijit/xplr
issues: https://github.com/sayanarijit/xplr/issues
website: https://xplr.dev/
base: core20
grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots
parts:
xplr:
plugin: rust
source: .
apps:
xplr:
command: bin/xplr

File diff suppressed because it is too large Load Diff

@ -1,12 +0,0 @@
use crate::app::{ExternalMsg, MsgIn, Task};
use std::sync::mpsc::Sender;
use std::thread;
use std::time::Duration;
pub fn start_auto_refreshing(tx: Sender<Task>) {
thread::spawn(move || loop {
tx.send(Task::new(3, MsgIn::External(ExternalMsg::Refresh), None))
.unwrap();
thread::sleep(Duration::from_secs(1));
});
}

@ -0,0 +1,215 @@
#![allow(clippy::too_many_arguments)]
use std::env;
use xplr::cli::{self, Cli};
use xplr::runner;
fn main() {
let cli = Cli::parse(env::args()).unwrap_or_else(|e| {
eprintln!("error: {e}");
std::process::exit(1);
});
if cli.help {
let usage = r###"
xplr [FLAG]... [OPTION]... [PATH] [SELECTION]..."###;
let flags = r###"
- Reads new-line (\n) separated paths from stdin
-- Denotes the end of command-line flags and options
--force-focus Focuses on the given <PATH>, even if it is a directory
-h, --help Prints help information
-m, --pipe-msg-in Helps safely passing messages to the active xplr
session, use %%, %s and %q as the placeholders
-M, --print-msg-in Like --pipe-msg-in, but prints the message instead of
passing to the active xplr session
--print-pwd-as-result Prints the present working directory when quitting
with `PrintResultAndQuit`
--read-only Enables read-only mode (config.general.read_only)
--read0 Reads paths separated using the null character (\0)
--write0 Prints paths separated using the null character (\0)
-0 --null Combines --read0 and --write0
-V, --version Prints version information"###;
let options = r###"
-c, --config <PATH> Specifies a custom config file (default is
"$HOME/.config/xplr/init.lua")
-C, --extra-config <PATH>... Specifies extra config files to load
--on-load <MESSAGE>... Sends messages when xplr loads
--vroot <PATH> Treats the specified path as the virtual root"###;
let args = r###"
<PATH> Path to focus on, or enter if directory, (default is `.`)
<SELECTION>... Paths to select, requires <PATH> to be set explicitly"###;
let help = format!(
"xplr {}\n{}\n{}\n\nUSAGE:{}\n\nFLAGS:{}\n\nOPTIONS:{}\n\nARGS:{}",
xplr::app::VERSION,
env!("CARGO_PKG_AUTHORS"),
env!("CARGO_PKG_DESCRIPTION"),
usage,
flags,
options,
args,
);
let help = help.trim();
println!("{help}");
} else if cli.version {
println!("xplr {}", xplr::app::VERSION);
} else if !cli.pipe_msg_in.is_empty() {
if let Err(err) = cli::pipe_msg_in(cli.pipe_msg_in) {
eprintln!("error: {err}");
std::process::exit(1);
}
} else if !cli.print_msg_in.is_empty() {
if let Err(err) = cli::print_msg_in(cli.print_msg_in) {
eprintln!("error: {err}");
std::process::exit(1);
}
} else {
match runner::from_cli(cli).and_then(|a| a.run()) {
Ok(Some(out)) => {
print!("{out}");
}
Ok(None) => {}
Err(err) => {
if !err.to_string().is_empty() {
eprintln!("error: {err}");
};
std::process::exit(1);
}
}
}
}
#[cfg(test)]
mod tests {
use assert_cmd::Command;
#[test]
fn test_no_debug_in_lib() {
// for pat in ["print!", "println!"].iter() {
// Command::new("grep")
// .args(&[
// "-R",
// pat,
// "src",
// "--exclude-dir",
// "bin/",
// "--exclude-dir",
// "rustc/",
// ])
// .assert()
// .failure();
// }
//
// TODO: fix github macos runner
}
#[test]
fn test_cli_version() {
Command::cargo_bin("xplr")
.unwrap()
.arg("--version")
.assert()
.success()
.code(0)
.stdout(format!("xplr {}\n", xplr::app::VERSION))
.stderr("");
Command::cargo_bin("xplr")
.unwrap()
.arg("-V")
.assert()
.success()
.code(0)
.stdout(format!("xplr {}\n", xplr::app::VERSION))
.stderr("");
}
#[test]
fn test_cli_help() {
Command::cargo_bin("xplr")
.unwrap()
.arg("-h")
.assert()
.success()
.code(0)
.stderr("");
Command::cargo_bin("xplr")
.unwrap()
.arg("--help")
.assert()
.success()
.code(0)
.stderr("");
}
// TODO fix GitHub CI failures
//
// #[test]
// fn test_cli_path_arg_valid() {
// Command::cargo_bin("xplr")
// .unwrap()
// .arg("src")
// .arg("--on-load")
// .arg("PrintResultAndQuit")
// .assert()
// .success()
// .code(0)
// .stderr("");
//
// Command::cargo_bin("xplr")
// .unwrap()
// .arg("src")
// .arg("--on-load")
// .arg("PrintResultAndQuit")
// .assert()
// .success()
// .code(0)
// .stderr("");
//
// Command::cargo_bin("xplr")
// .unwrap()
// .arg("--on-load")
// .arg("PrintResultAndQuit")
// .arg("--")
// .arg("src")
// .assert()
// .success()
// .code(0)
// .stderr("");
// }
//
// #[test]
// fn test_cli_path_stdin_valid() {
// Command::cargo_bin("xplr")
// .unwrap()
// .arg("-")
// .arg("--on-load")
// .arg("PrintResultAndQuit")
// .write_stdin("src\n")
// .assert()
// .success()
// .code(0)
// .stderr("");
// }
//
// #[test]
// fn test_on_load_yaml_parsing() {
// Command::cargo_bin("xplr")
// .unwrap()
// .arg("--on-load")
// .arg("Call: {command: touch, args: [foo]}")
// .arg("Quit")
// .assert()
// .success()
// .code(0)
// .stderr("");
//
// std::fs::remove_file("foo").unwrap();
// }
}

@ -0,0 +1,219 @@
use crate::{app, yaml};
use anyhow::{bail, Context, Result};
use app::ExternalMsg;
use path_absolutize::*;
use serde_json as json;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;
use std::{env, fs};
/// The arguments to pass
#[derive(Debug, Clone, Default)]
pub struct Cli {
pub bin: String,
pub version: bool,
pub help: bool,
pub read_only: bool,
pub force_focus: bool,
pub print_pwd_as_result: bool,
pub read0: bool,
pub write0: bool,
pub vroot: Option<PathBuf>,
pub config: Option<PathBuf>,
pub extra_config: Vec<PathBuf>,
pub on_load: Vec<app::ExternalMsg>,
pub pipe_msg_in: Vec<String>,
pub print_msg_in: Vec<String>,
pub paths: Vec<PathBuf>,
}
impl Cli {
fn read_path(arg: &str) -> Result<PathBuf> {
if arg.is_empty() {
bail!("empty string passed")
};
let path = PathBuf::from(arg).absolutize()?.to_path_buf();
if path.exists() {
Ok(path)
} else {
bail!("path doesn't exist: {}", path.to_string_lossy())
}
}
/// Parse arguments from the command-line
pub fn parse(args: env::Args) -> Result<Self> {
let mut cli = Self::default();
let mut args = args.peekable();
cli.bin = args
.next()
.map(which::which)
.context("failed to parse xplr binary path")?
.context("failed to find xplr binary path")?
.absolutize()?
.to_path_buf()
.to_string_lossy()
.to_string();
let mut flag_ends = false;
while let Some(arg) = args.next() {
if flag_ends {
cli.paths.push(Cli::read_path(&arg)?);
} else {
match arg.as_str() {
// Flags
"-" => {
let reader = BufReader::new(std::io::stdin());
if cli.read0 {
for path in reader.split(b'\0') {
cli.paths
.push(Cli::read_path(&String::from_utf8(path?)?)?);
}
} else {
for path in reader.lines() {
cli.paths.push(Cli::read_path(&path?)?);
}
};
}
"-h" | "--help" => {
cli.help = true;
}
"-V" | "--version" => {
cli.version = true;
}
"--read0" => {
cli.read0 = true;
}
"--write0" => {
cli.write0 = true;
}
"-0" | "--null" => {
cli.read0 = true;
cli.write0 = true;
}
"--" => {
flag_ends = true;
}
// Options
"-c" | "--config" => {
cli.config = Some(
args.next()
.map(|a| Cli::read_path(&a))
.with_context(|| format!("usage: xplr {arg} PATH"))??,
);
}
"--vroot" => {
cli.vroot = Some(
args.next()
.map(|a| Cli::read_path(&a))
.with_context(|| format!("usage: xplr {arg} PATH"))??,
);
}
"-C" | "--extra-config" => {
while let Some(path) =
args.next_if(|path| !path.starts_with('-'))
{
cli.extra_config.push(Cli::read_path(&path)?);
}
}
"--read-only" => cli.read_only = true,
"--on-load" => {
while let Some(msg) = args.next_if(|msg| !msg.starts_with('-')) {
cli.on_load.push(yaml::from_str(&msg)?);
}
}
"--force-focus" => {
cli.force_focus = true;
}
"--print-pwd-as-result" => {
cli.print_pwd_as_result = true;
}
"-m" | "--pipe-msg-in" => {
cli.pipe_msg_in.extend(args.by_ref());
if cli.pipe_msg_in.is_empty() {
bail!("usage: xplr {} FORMAT [ARGUMENT]...", arg)
}
}
"-M" | "--print-msg-in" => {
cli.print_msg_in.extend(args.by_ref());
if cli.print_msg_in.is_empty() {
bail!("usage: xplr {} FORMAT [ARGUMENT]...", arg)
}
}
// path
path => {
if path.starts_with('-') && !flag_ends {
bail!(
"invalid argument: {0:?}, try `-- {0:?}` or `--help`",
path
)
} else {
cli.paths.push(Cli::read_path(path)?);
}
}
}
}
}
Ok(cli)
}
}
pub fn pipe_msg_in(args: Vec<String>) -> Result<()> {
let mut msg = fmt_msg_in(args)?;
if let Ok(path) = std::env::var("XPLR_PIPE_MSG_IN") {
let delimiter = fs::read(&path)?
.first()
.cloned()
.context("failed to detect delimmiter")?;
msg.push(delimiter.into());
File::options()
.append(true)
.open(&path)?
.write_all(msg.as_bytes())?;
} else {
println!("{msg}");
};
Ok(())
}
pub fn print_msg_in(args: Vec<String>) -> Result<()> {
let msg = fmt_msg_in(args)?;
print!("{msg}");
Ok(())
}
fn fmt_msg_in(args: Vec<String>) -> Result<String> {
let msg = match jf::format(args.into_iter().map(Into::into)) {
Ok(msg) => msg,
Err(jf::Error::Jf(e)) => bail!("xplr -m: {e}"),
Err(jf::Error::Json(e)) => bail!("xplr -m: json: {e}"),
Err(jf::Error::Yaml(e)) => bail!("xplr -m: yaml: {e}"),
Err(jf::Error::Io(e)) => bail!("xplr -m: io: {e}"),
};
// validate
let _: ExternalMsg = json::from_str(&msg)?;
Ok(msg)
}

@ -0,0 +1,224 @@
// Things of the past, mostly bad decisions, which cannot erased, stays in this
// haunted module.
use crate::app;
use crate::lua;
use crate::ui::block;
use crate::ui::string_to_text;
use crate::ui::Constraint;
use crate::ui::ContentRendererArg;
use crate::ui::UI;
use serde::{Deserialize, Serialize};
use tui::layout::Constraint as TuiConstraint;
use tui::layout::Rect as TuiRect;
use tui::widgets::Cell;
use tui::widgets::List;
use tui::widgets::ListItem;
use tui::widgets::Paragraph;
use tui::widgets::Row;
use tui::widgets::Table;
use tui::Frame;
/// A cursed enum from crate::ui.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub enum ContentBody {
/// A paragraph to render
StaticParagraph { render: String },
/// A Lua function that returns a paragraph to render
DynamicParagraph { render: String },
/// List to render
StaticList { render: Vec<String> },
/// A Lua function that returns lines to render
DynamicList { render: String },
/// A table to render
StaticTable {
widths: Vec<Constraint>,
col_spacing: Option<u16>,
render: Vec<Vec<String>>,
},
/// A Lua function that returns a table to render
DynamicTable {
widths: Vec<Constraint>,
col_spacing: Option<u16>,
render: String,
},
}
/// A cursed struct from crate::ui.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct CustomContent {
pub title: Option<String>,
pub body: ContentBody,
}
/// A cursed function from crate::ui.
pub fn draw_custom_content(
ui: &mut UI,
f: &mut Frame,
layout_size: TuiRect,
app: &app::App,
content: CustomContent,
) {
let config = app.config.general.panel_ui.default.clone();
let title = content.title;
let body = content.body;
match body {
ContentBody::StaticParagraph { render } => {
let render = string_to_text(render);
let content = Paragraph::new(render).block(block(
config,
title.map(|t| format!(" {t} ")).unwrap_or_default(),
));
f.render_widget(content, layout_size);
}
ContentBody::DynamicParagraph { render } => {
let ctx = ContentRendererArg {
app: app.to_lua_ctx_light(),
layout_size: layout_size.into(),
screen_size: ui.screen_size.into(),
scrolltop: ui.scrolltop as u16,
};
let render = lua::serialize(ui.lua, &ctx)
.map(|arg| {
lua::call(ui.lua, &render, arg).unwrap_or_else(|e| format!("{e:?}"))
})
.unwrap_or_else(|e| e.to_string());
let render = string_to_text(render);
let content = Paragraph::new(render).block(block(
config,
title.map(|t| format!(" {t} ")).unwrap_or_default(),
));
f.render_widget(content, layout_size);
}
ContentBody::StaticList { render } => {
let items = render
.into_iter()
.map(string_to_text)
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let content = List::new(items).block(block(
config,
title.map(|t| format!(" {t} ")).unwrap_or_default(),
));
f.render_widget(content, layout_size);
}
ContentBody::DynamicList { render } => {
let ctx = ContentRendererArg {
app: app.to_lua_ctx_light(),
layout_size: layout_size.into(),
screen_size: ui.screen_size.into(),
scrolltop: ui.scrolltop as u16,
};
let items = lua::serialize(ui.lua, &ctx)
.map(|arg| {
lua::call(ui.lua, &render, arg)
.unwrap_or_else(|e| vec![format!("{e:?}")])
})
.unwrap_or_else(|e| vec![e.to_string()])
.into_iter()
.map(string_to_text)
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let content = List::new(items).block(block(
config,
title.map(|t| format!(" {t} ")).unwrap_or_default(),
));
f.render_widget(content, layout_size);
}
ContentBody::StaticTable {
widths,
col_spacing,
render,
} => {
let rows = render
.into_iter()
.map(|cols| {
Row::new(
cols.into_iter()
.map(string_to_text)
.map(Cell::from)
.collect::<Vec<Cell>>(),
)
})
.collect::<Vec<Row>>();
let widths = widths
.into_iter()
.map(|w| w.to_tui(ui.screen_size, layout_size))
.collect::<Vec<TuiConstraint>>();
let content = Table::new(rows, widths)
.column_spacing(col_spacing.unwrap_or(1))
.block(block(
config,
title.map(|t| format!(" {t} ")).unwrap_or_default(),
));
f.render_widget(content, layout_size);
}
ContentBody::DynamicTable {
widths,
col_spacing,
render,
} => {
let ctx = ContentRendererArg {
app: app.to_lua_ctx_light(),
layout_size: layout_size.into(),
screen_size: ui.screen_size.into(),
scrolltop: ui.scrolltop as u16,
};
let rows = lua::serialize(ui.lua, &ctx)
.map(|arg| {
lua::call(ui.lua, &render, arg)
.unwrap_or_else(|e| vec![vec![format!("{e:?}")]])
})
.unwrap_or_else(|e| vec![vec![e.to_string()]])
.into_iter()
.map(|cols| {
Row::new(
cols.into_iter()
.map(string_to_text)
.map(Cell::from)
.collect::<Vec<Cell>>(),
)
})
.collect::<Vec<Row>>();
let widths = widths
.into_iter()
.map(|w| w.to_tui(ui.screen_size, layout_size))
.collect::<Vec<TuiConstraint>>();
let mut content = Table::new(rows, &widths).block(block(
config,
title.map(|t| format!(" {t} ")).unwrap_or_default(),
));
if let Some(col_spacing) = col_spacing {
content = content.column_spacing(col_spacing);
};
f.render_widget(content, layout_size);
}
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save