rfd 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d3f9431d82b51b60a0202b8715d7c70258127130a1006f1467bfb024a729cf9
4
- data.tar.gz: 998e2b8409539ef0f2a0dcc9f50d39d8d200530a8a729020585ea627bc53d090
3
+ metadata.gz: 583342396eea5d2103dd2deb3bf55cbd46be8349fd339f8a60709231bdc66d81
4
+ data.tar.gz: e5e8d3be8efdbdd392587532ccc501c1f714ddac1ee639aa93e7b6c3a1e24ad0
5
5
  SHA512:
6
- metadata.gz: 619e350f6ab323070af91f516591659da3ebb5abe3a27780ce63644200cf171667bdba5ab4dfd1ccbfa3bcfa7c9a482c9f76d7eb154232124ac3bc2428cc4835
7
- data.tar.gz: b75b5c0ea0fea5daf59ec6dec75fbee332432cceae5ddd65f31d777ee4d485f1f47b1cd14239e644d532779eb6b52b6ff323cd110093f252d77cf49072a07617
6
+ metadata.gz: f60a2c8684d96b23caae0b68a67ed19e24f60817d87095cc7bd316f56416029f653d18269f9b28703b86fd00be29311bb8cd5e261cb8348a4b7cf5a78b6208d5
7
+ data.tar.gz: 7b9a8d13d1176548740265bf4313d29c8b2aaa03b28be7fb933afeb42ad2380c0357a4a97ec8bcf18aaf669025490bb23f7d235a0fe355a3a03b44d35bcec4bc
@@ -0,0 +1,37 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, macos-latest]
16
+ ruby: ['2.7', '3.0', '3.1', '3.2', '3.3', '3.4', '4.0']
17
+
18
+ steps:
19
+ - uses: actions/checkout@v6
20
+
21
+ - name: Set up Ruby
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: ${{ matrix.ruby }}
25
+ bundler-cache: true
26
+
27
+ - name: Install ncurses (Ubuntu)
28
+ if: runner.os == 'Linux'
29
+ run: sudo apt-get install -y libncursesw5-dev
30
+
31
+ - name: Set test environment
32
+ run: |
33
+ echo "TERM=xterm-256color" >> $GITHUB_ENV
34
+ echo "RFD_SKIP_PREVIEW_SERVER=1" >> $GITHUB_ENV
35
+
36
+ - name: Run tests
37
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ *.log
19
+ lib/rfd/help.txt
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  source 'https://rubygems.org'
3
3
 
4
+ gem 'simplecov'
5
+
4
6
  # Specify your gem's dependencies in rfd.gemspec
5
7
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # rfd (Ruby on Files & Directories)
2
2
 
3
- rfd is a terminal-based filesystem explorer, inspired by the legendary freesoft MS-DOS filer, "FD".
3
+ rfd is a terminal-based filesystem explorer, inspired by the legendary freesoft MS-DOS filer "FD", with Vim flavor.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,12 +8,20 @@ rfd is a terminal-based filesystem explorer, inspired by the legendary freesoft
8
8
 
9
9
  ## Requirements
10
10
 
11
- * Ruby 2.0, Ruby 2.1
12
- * NCurses
11
+ * Ruby 2.5 or newer
12
+ * NCurses with wide character support (ncursesw) recommended for best Unicode display
13
+
14
+ ### macOS: Setting up ncursesw for Unicode borders
15
+
16
+ macOS comes with an older system ncurses that doesn't fully support Unicode. For proper Unicode box-drawing characters, install and link Homebrew's ncurses before installing the curses gem:
17
+
18
+ % brew install ncurses
19
+ % brew link --force ncurses
20
+ % gem install curses
13
21
 
14
22
  ## Tested environments
15
23
 
16
- Mac OS X Mountain Lion, Mac OS X Lion, Ubuntu 13.04
24
+ Mac OS X Mountain Lion and newer, Ubuntu 13.04 and newer
17
25
 
18
26
  ## Screenshot
19
27
 
@@ -29,19 +37,23 @@ You can also pass in a starting directory name, which defaults to `.`.
29
37
 
30
38
  % rfd ~/src/rails
31
39
 
40
+
32
41
  ## Commands
33
42
 
34
43
  You can send commands to rfd by pressing some chars on your keyboard, just like Vim.
35
44
  If you're unfamiliar with this sort of command system, I recommend you to play with `vimtutor` before you go any further.
36
45
 
46
+ Press `?` to see the built-in help screen with all available commands.
47
+
37
48
  All available commands in rfd are defined as Ruby methods here. https://github.com/amatsuda/rfd/tree/master/lib/rfd/commands.rb
38
49
 
39
50
  ### Changing the current directory
40
51
 
41
- * `<Enter>`: cd into the directory where the cursor is on.
52
+ * `<Enter>`: cd into the directory where the cursor is on. For files, opens with viewer.
42
53
  * `<Delete>` (or \<Backspace\> on your keyboard, probably?): Go up to the upper directory (cd ..).
43
54
  * `-`: Get back to where you once belonged (popd).
44
- * `@`: cd to a directory given via the command-line window.
55
+ * `@`: Open directory tree browser for navigation.
56
+ * `~`: Go to home directory.
45
57
 
46
58
  ### Moving the cursor
47
59
 
@@ -75,6 +87,7 @@ You can find a file by typing the first letter of it immediately after the find
75
87
  * `f{char}`: Move to the next file / directory of which name starts with the given char.
76
88
  * `F{char}`: Move to the previous file / directory of which name starts with the given char.
77
89
  * `n`: Repeat the last `f` or `F`.
90
+ * `N`: Repeat the last `f` or `F` in reverse direction.
78
91
 
79
92
  ### Searching, sorting
80
93
 
@@ -107,8 +120,8 @@ The mark is drawn as a `*` char on the left of each file / directory name.
107
120
 
108
121
  As stated above, you can send a command to one or more files / directories. In this document, the term "selected items" means "(the marked files / directories) || (the file / directory on which the cursor is on)".
109
122
 
110
- * `c`: Copy selected items (cp).
111
- * `m`: Move selected items (mv).
123
+ * `c`: Copy selected items (cp). Opens tree browser to select destination.
124
+ * `m`: Move selected items (mv). Opens tree browser to select destination.
112
125
  * `d`: Move selected items into the Trash.
113
126
  * `D`: Delete selected items.
114
127
  * `r`: Rename selected items. This command takes a sed-like argument separated by a `/`. For example, changing all .html files' extension to .html.erb could be done by `\.html$/.html.erb`.
@@ -133,10 +146,11 @@ As stated above, you can send a command to one or more files / directories. In t
133
146
 
134
147
  ### Viewing, Editing, Opening
135
148
 
136
- * `<Enter>`: View current file with the system $VIEWER such as `less`.
149
+ * `<Enter>`: Open directory, or view file. For images, shows inline preview. For audio, plays with system player.
137
150
  * `v`: View current file with the system $VIEWER such as `less`.
138
151
  * `e`: Edit current file with the system $EDITOR such as `vim`.
139
152
  * `o`: Send the `open` command.
153
+ * `P`: Toggle preview window (shows file contents with syntax highlighting).
140
154
 
141
155
  ### Manipulating archives
142
156
 
@@ -161,6 +175,7 @@ Mouse is available if your terminal supports it. You can move the cursor by clic
161
175
  * `C`: Copy selected items' paths to the clipboard.
162
176
  * `O`: Open a new terminal window at the current directory.
163
177
  * `!`: Execute a shell command.
178
+ * `?`: Show help screen.
164
179
  * `q`: Quit the app.
165
180
 
166
181
  ## How to manually execute a command, or how the commands are executed
@@ -168,6 +183,53 @@ Mouse is available if your terminal supports it. You can move the cursor by clic
168
183
  By pressing `:`, you can enter the command-line mode. Any string given in the command line after `:` will be executed as Ruby method call in the `Controller` instance.
169
184
  For instance, `:j` brings your cursor down, `:mkdir foo` makes a directory named "foo". And `:q!` of course works as you might expect, since `q!` method is implemented so.
170
185
 
186
+
187
+ ## Features
188
+
189
+ ### File Icons
190
+
191
+ rfd displays file type icons using Nerd Fonts. If you have a Nerd Font installed, you'll see icons for directories, symlinks, and various file types (Ruby, JavaScript, Markdown, etc.).
192
+
193
+ To disable icons:
194
+
195
+ % RFD_NO_ICONS=1 rfd
196
+
197
+ ### Preview Window
198
+
199
+ When the file list is multi-paned (default), rfd shows the preview window in an inactive pane. The preview window shows a preview of the file or directory where the cursor is on.
200
+
201
+ * **Code files**: Syntax-highlighted preview (via Rouge)
202
+ * **Directories**: List of contents
203
+ * **Archives**: Tree view of zip/tar.gz contents
204
+ * **Markdown**: Formatted with headers and lists highlighted
205
+ * **Images**: Inline preview (in supported terminals)
206
+ * **Binary files**: Indicated as `[Binary file]`
207
+
208
+ However, with this feature enabled, the cursor would not move smoothly. In that case, you can disable (toggle) the preview window by pressing `P`.
209
+
210
+ ### Directory Tree Browser
211
+
212
+ Press `@` to open an interactive directory tree browser with:
213
+ * **Fuzzy filtering**: Type to filter directories
214
+ * **Tree navigation**: `j/k` or arrows to move, `Enter` to select
215
+ * **Expand/collapse**: `h/l` to collapse/expand directories
216
+ * **Quick access**: `~` for home, `/` for root
217
+ * **Bookmarks**: `@` to switch to bookmark view
218
+
219
+ ### Bookmarks
220
+
221
+ Save frequently used directories for quick access:
222
+ * In tree or bookmark view, press `^B` to toggle bookmark on current directory
223
+ * Press `@` twice (or `@` then `@` in tree view) to see bookmarks
224
+ * Bookmarks are saved to `~/.config/rfd/bookmarks`
225
+
226
+
227
+ ## Environment Variables
228
+
229
+ * `RFD_NO_ICONS`: Set to `1` to disable file icons (useful if you don't have a Nerd Font installed)
230
+ * `EDITOR`: Editor used for the `e` command (default: vim)
231
+ * `VIEWER`: Viewer used for the `v` command (default: less)
232
+
171
233
  ## Contributing
172
234
 
173
235
  Send me your pull requests here. https://github.com/amatsuda/rfd
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'bundler'
3
3
  Bundler::GemHelper.install_tasks
4
- require "bundler/gem_tasks"
4
+ require 'bundler/gem_tasks'
5
5
 
6
6
  require 'rspec/core'
7
7
  require 'rspec/core/rake_task'
@@ -11,3 +11,15 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
11
11
  end
12
12
 
13
13
  task :default => :spec
14
+
15
+ namespace :build do
16
+ desc 'Generate help text cache'
17
+ task :help do
18
+ require_relative 'lib/rfd/help_generator'
19
+ Rfd::HelpGenerator.write_cache
20
+ puts "Generated #{Rfd::HelpGenerator::CACHE_FILE}"
21
+ end
22
+ end
23
+
24
+ # Generate help before building the gem
25
+ Rake::Task[:build].enhance(['build:help'])
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rfd
4
+ module Bookmark
5
+ CONFIG_DIR = File.join(ENV.fetch('XDG_CONFIG_HOME') { File.expand_path('~/.config') }, 'rfd')
6
+ BOOKMARK_FILE = File.join(CONFIG_DIR, 'bookmarks')
7
+
8
+ @bookmarks = []
9
+
10
+ class << self
11
+ attr_accessor :bookmarks
12
+
13
+ def add(path)
14
+ path = File.expand_path(path)
15
+ return if @bookmarks.include?(path)
16
+
17
+ @bookmarks << path
18
+ save
19
+ end
20
+
21
+ def remove(path)
22
+ path = File.expand_path(path)
23
+ @bookmarks.delete(path)
24
+ save
25
+ end
26
+
27
+ def include?(path)
28
+ @bookmarks.include?(File.expand_path(path))
29
+ end
30
+
31
+ def toggle(path)
32
+ if include?(path)
33
+ remove(path)
34
+ else
35
+ add(path)
36
+ end
37
+ end
38
+
39
+ def load
40
+ return unless File.exist?(BOOKMARK_FILE)
41
+
42
+ @bookmarks = File.readlines(BOOKMARK_FILE, chomp: true)
43
+ .map { |line| File.expand_path(line) }
44
+ .select { |path| File.directory?(path) }
45
+ rescue Errno::EACCES, Errno::ENOENT
46
+ @bookmarks = []
47
+ end
48
+
49
+ def save
50
+ dir = File.dirname(BOOKMARK_FILE)
51
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
52
+ File.write(BOOKMARK_FILE, @bookmarks.join("\n") + "\n")
53
+ rescue Errno::EACCES, Errno::ENOENT
54
+ # Silently fail if we can't write
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rfd
4
+ class BookmarkWindow < SubWindow
5
+ def initialize(controller)
6
+ super(controller)
7
+ @cursor = 0
8
+ @scroll = 0
9
+ @filter = FilterInput.new { apply_filter }
10
+ @filtered_items = nil
11
+ end
12
+
13
+ def all_items
14
+ Bookmark.bookmarks
15
+ end
16
+
17
+ def display_items
18
+ @filtered_items || all_items
19
+ end
20
+
21
+ def current_bookmarked?
22
+ Bookmark.include?(controller.current_dir.path)
23
+ end
24
+
25
+ def visible_items
26
+ display_items[@scroll, max_height - 1] || []
27
+ end
28
+
29
+ def current_item
30
+ display_items[@cursor]
31
+ end
32
+
33
+ def render
34
+ reposition_if_needed
35
+ @window.clear
36
+
37
+ draw_border('Bookmarks (@:tree ^B:add/remove ESC:close)')
38
+
39
+ # Filter input line (row 1)
40
+ @filter.render(@window, 1, max_width)
41
+
42
+ # Bookmarks list starts at row 2
43
+ visible_items.each_with_index do |path, i|
44
+ actual_index = @scroll + i
45
+ @window.setpos(2 + i, 1)
46
+
47
+ display_path = path.sub(File.expand_path('~'), '~')
48
+ if actual_index == @cursor
49
+ @window.attron(Curses::A_REVERSE) { @window.addstr(display_path[0, max_width].ljust(max_width)) }
50
+ else
51
+ @window.addstr(display_path[0, max_width].ljust(max_width))
52
+ end
53
+ end
54
+
55
+ @window.refresh
56
+ end
57
+
58
+ def handle_input(c)
59
+ case c
60
+ when 27 # ESC - close window
61
+ controller.close_sub_window
62
+ true
63
+ when 64, ?@ # @ - switch to tree view
64
+ controller.close_sub_window
65
+ controller.instance_variable_set(:@sub_window, NavigationWindow.new(controller))
66
+ controller.instance_variable_get(:@sub_window).render
67
+ true
68
+ when 2 # Ctrl-B - toggle bookmark for current directory
69
+ toggle_bookmark
70
+ true
71
+ when 10, 13 # Enter - cd to selected bookmark
72
+ select_item
73
+ true
74
+ when 14 # Ctrl-N
75
+ move_cursor_down
76
+ true
77
+ when 16 # Ctrl-P
78
+ move_cursor_up
79
+ true
80
+ else
81
+ if @filter.handle_input(c)
82
+ render
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def apply_filter
93
+ if @filter.empty?
94
+ @filtered_items = nil
95
+ else
96
+ @filtered_items = all_items.select { |path| @filter.fuzzy_match?(path) }
97
+ end
98
+ @cursor = 0
99
+ @scroll = 0
100
+ end
101
+
102
+ def toggle_bookmark
103
+ Bookmark.toggle(controller.current_dir.path)
104
+ apply_filter # Re-apply filter in case list changed
105
+ render
106
+ end
107
+
108
+ def select_item
109
+ item = current_item
110
+ return unless item
111
+
112
+ controller.close_sub_window
113
+ controller.cd(item)
114
+ end
115
+
116
+ def move_cursor_down
117
+ return if @cursor >= display_items.size - 1
118
+
119
+ @cursor += 1
120
+ adjust_scroll
121
+ render
122
+ end
123
+
124
+ def move_cursor_up
125
+ return if @cursor <= 0
126
+
127
+ @cursor -= 1
128
+ adjust_scroll
129
+ render
130
+ end
131
+
132
+ def adjust_scroll
133
+ available_height = max_height - 1 # Reserve one line for filter
134
+ if @cursor < @scroll
135
+ @scroll = @cursor
136
+ elsif @cursor >= @scroll + available_height
137
+ @scroll = @cursor - available_height + 1
138
+ end
139
+ end
140
+ end
141
+ end