rfd 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +22 -0
- data/README.md +31 -0
- data/Rakefile +1 -0
- data/bin/rfd +5 -0
- data/lib/rfd.rb +456 -0
- data/lib/rfd/commands.rb +234 -0
- data/lib/rfd/item.rb +176 -0
- data/lib/rfd/windows.rb +227 -0
- data/rfd.gemspec +24 -0
- data/spec/controller_spec.rb +277 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/capture_helper.rb +21 -0
- data/spec/testdir/.file1 +0 -0
- data/spec/testdir/.file2 +0 -0
- data/spec/testdir/.file3 +0 -0
- data/spec/testdir/.link1 +0 -0
- data/spec/testdir/file1 +1 -0
- data/spec/testdir/file2 +0 -0
- data/spec/testdir/file3 +0 -0
- data/spec/testdir/link1 +1 -0
- data/spec/testdir/link2 +0 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c8531a85da40abf299418a5db10c44a8df36510c
|
4
|
+
data.tar.gz: 20dd4b8c0560d21f506a41ad64aeec7fbacc0c35
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6a045b387ff889e51b0e0af604f6248849f0e160bff2b41d5ee4da3b3db4de93d35422f84aa1dbdf0fc979db08cc2ce9c68bdd9f345c7caa90c3cf524e18b119
|
7
|
+
data.tar.gz: 4b4701049e945298a437cdf42616f71e3b7ad7b556c6c244c16e8d38cab2c8957d6e8c5538799853bf58f56fa48b28c2ee424565059264ba0c0b8be442fb6ad9
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Akira Matsuda
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# rfd (Ruby on Files and Directories)
|
2
|
+
|
3
|
+
rfd is a terminal based File explorer, inpsired by the legendary freesoft MS-DOS filer, "FD".
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
% gem install rfd
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
|
11
|
+
* Ruby 2.0, Ruby 2.1
|
12
|
+
* NCurses
|
13
|
+
* (FFI)
|
14
|
+
|
15
|
+
## Tested environment
|
16
|
+
|
17
|
+
Max OSX Snow Leopard
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Open up your terminal and type in:
|
22
|
+
|
23
|
+
% rfd
|
24
|
+
|
25
|
+
You can command rfd by pressing some chars on your keyboard, just like Vim.
|
26
|
+
|
27
|
+
All available commands are defined here. https://github.com/amatsuda/rfd/tree/master/lib/rfd/commands.rb
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Send me your pull requests here. https://github.com/amatsuda/rfd
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/rfd
ADDED
data/lib/rfd.rb
ADDED
@@ -0,0 +1,456 @@
|
|
1
|
+
require 'ffi-ncurses'
|
2
|
+
Curses = FFI::NCurses
|
3
|
+
require 'fileutils'
|
4
|
+
require_relative 'rfd/commands'
|
5
|
+
require_relative 'rfd/item'
|
6
|
+
require_relative 'rfd/windows'
|
7
|
+
|
8
|
+
module Rfd
|
9
|
+
VERSION = Gem.loaded_specs['rfd'].version.to_s
|
10
|
+
|
11
|
+
# :nodoc:
|
12
|
+
def self.init_curses
|
13
|
+
Curses.initscr
|
14
|
+
Curses.raw
|
15
|
+
Curses.noecho
|
16
|
+
Curses.curs_set 0
|
17
|
+
Curses.keypad Curses.stdscr, true
|
18
|
+
Curses.start_color
|
19
|
+
|
20
|
+
[Curses::COLOR_WHITE, Curses::COLOR_CYAN, Curses::COLOR_MAGENTA, Curses::COLOR_GREEN, Curses::COLOR_RED].each do |c|
|
21
|
+
Curses.init_pair c, c, Curses::COLOR_BLACK
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Start the app here!
|
26
|
+
#
|
27
|
+
# ==== Parameters
|
28
|
+
# * +dir+ - The initial directory.
|
29
|
+
def self.start(dir = '.')
|
30
|
+
init_curses
|
31
|
+
rfd = Rfd::Controller.new
|
32
|
+
rfd.cd dir
|
33
|
+
rfd.ls
|
34
|
+
rfd
|
35
|
+
end
|
36
|
+
|
37
|
+
class Controller
|
38
|
+
include Rfd::Commands
|
39
|
+
|
40
|
+
attr_reader :header_l, :header_r, :main, :command_line, :items, :displayed_items, :current_row, :current_page, :current_dir
|
41
|
+
|
42
|
+
# :nodoc:
|
43
|
+
def initialize
|
44
|
+
@main = MainWindow.new
|
45
|
+
@header_l = HeaderLeftWindow.new
|
46
|
+
@header_r = HeaderRightWindow.new
|
47
|
+
@command_line = CommandLineWindow.new
|
48
|
+
end
|
49
|
+
|
50
|
+
# The main loop.
|
51
|
+
def run
|
52
|
+
loop do
|
53
|
+
begin
|
54
|
+
case (c = Curses.getch)
|
55
|
+
when Curses::KEY_RETURN
|
56
|
+
enter
|
57
|
+
when Curses::KEY_ESCAPE
|
58
|
+
q
|
59
|
+
when 32 # space
|
60
|
+
space
|
61
|
+
when 127 # DEL
|
62
|
+
del
|
63
|
+
when Curses::KEY_DOWN
|
64
|
+
j
|
65
|
+
when Curses::KEY_UP
|
66
|
+
k
|
67
|
+
when Curses::KEY_LEFT
|
68
|
+
h
|
69
|
+
when Curses::KEY_RIGHT
|
70
|
+
l
|
71
|
+
when Curses::KEY_CTRL_A..Curses::KEY_CTRL_Z
|
72
|
+
chr = ((c - 1 + 65) ^ 0b0100000).chr
|
73
|
+
public_send "ctrl_#{chr}" if respond_to?("ctrl_#{chr}")
|
74
|
+
when 0..255
|
75
|
+
if respond_to? c.chr
|
76
|
+
public_send c.chr
|
77
|
+
else
|
78
|
+
debug "key: #{c}" if ENV['DEBUG']
|
79
|
+
end
|
80
|
+
else
|
81
|
+
debug "key: #{c}" if ENV['DEBUG']
|
82
|
+
end
|
83
|
+
rescue StopIteration
|
84
|
+
raise
|
85
|
+
rescue => e
|
86
|
+
command_line.show_error e.to_s
|
87
|
+
raise if ENV['DEBUG']
|
88
|
+
end
|
89
|
+
end
|
90
|
+
ensure
|
91
|
+
Curses.endwin
|
92
|
+
end
|
93
|
+
|
94
|
+
# Change the number of columns in the main window.
|
95
|
+
def spawn_panes(num)
|
96
|
+
main.spawn_panes num
|
97
|
+
@current_row = @current_page = 0
|
98
|
+
end
|
99
|
+
|
100
|
+
# The file or directory on which the cursor is on.
|
101
|
+
def current_item
|
102
|
+
items[current_row]
|
103
|
+
end
|
104
|
+
|
105
|
+
# * marked files and directories.
|
106
|
+
def marked_items
|
107
|
+
items.select(&:marked?)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Marked files and directories or Array(the current file or directory).
|
111
|
+
#
|
112
|
+
# . and .. will not be included.
|
113
|
+
def selected_items
|
114
|
+
((m = marked_items).any? ? m : Array(current_item)).reject {|i| %w(. ..).include? i.name}
|
115
|
+
end
|
116
|
+
|
117
|
+
# Move the cursor to specified row.
|
118
|
+
#
|
119
|
+
# The main window and the headers will be updated reflecting the displayed files and directories.
|
120
|
+
# The row number can be out of range of the current page.
|
121
|
+
def move_cursor(row = nil)
|
122
|
+
if row
|
123
|
+
page, item_index_in_page = row.divmod max_items
|
124
|
+
if page != current_page
|
125
|
+
switch_page page
|
126
|
+
else
|
127
|
+
if (prev_item = items[current_row])
|
128
|
+
main.draw_item prev_item
|
129
|
+
end
|
130
|
+
end
|
131
|
+
main.activate_pane item_index_in_page / maxy
|
132
|
+
@current_row = row
|
133
|
+
else
|
134
|
+
@current_row = 0
|
135
|
+
end
|
136
|
+
|
137
|
+
item = items[current_row]
|
138
|
+
main.draw_item item, current: true
|
139
|
+
|
140
|
+
header_l.draw_current_file_info item
|
141
|
+
header_l.wrefresh
|
142
|
+
end
|
143
|
+
|
144
|
+
# Change the current directory.
|
145
|
+
def cd(dir, pushd: true)
|
146
|
+
target = File.expand_path(dir.is_a?(Rfd::Item) ? dir.path : dir.start_with?('/') ? dir : current_dir ? File.join(current_dir, dir) : dir)
|
147
|
+
if File.readable? target
|
148
|
+
Dir.chdir target
|
149
|
+
(@dir_history ||= []) << current_dir if current_dir && pushd
|
150
|
+
@current_dir, @current_page, @current_row = target, 0, nil
|
151
|
+
main.activate_pane 0
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# cd to the previous directory.
|
156
|
+
def popd
|
157
|
+
if defined?(@dir_history) && @dir_history.any?
|
158
|
+
cd @dir_history.pop, pushd: false
|
159
|
+
ls
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Fetch files from current directory.
|
164
|
+
# Then update each windows reflecting the newest information.
|
165
|
+
def ls
|
166
|
+
fetch_items_from_filesystem
|
167
|
+
sort_items_according_to_current_direction
|
168
|
+
|
169
|
+
@current_page ||= 0
|
170
|
+
draw_items
|
171
|
+
move_cursor current_row
|
172
|
+
|
173
|
+
draw_marked_items
|
174
|
+
draw_total_items
|
175
|
+
end
|
176
|
+
|
177
|
+
# Sort the whole files and directories in the current directory, then refresh the screen.
|
178
|
+
#
|
179
|
+
# ==== Parameters
|
180
|
+
# * +direction+ - Sort order in a String.
|
181
|
+
# nil : order by name
|
182
|
+
# r : reverse order by name
|
183
|
+
# s, S : order by file size
|
184
|
+
# sr, Sr: reverse order by file size
|
185
|
+
# t : order by mtime
|
186
|
+
# tr : reverse order by mtime
|
187
|
+
# c : order by ctime
|
188
|
+
# cr : reverse order by ctime
|
189
|
+
# u : order by atime
|
190
|
+
# ur : reverse order by atime
|
191
|
+
# e : order by extname
|
192
|
+
# er : reverse order by extname
|
193
|
+
def sort(direction = nil)
|
194
|
+
@direction, @current_page = direction, 0
|
195
|
+
sort_items_according_to_current_direction
|
196
|
+
switch_page 0
|
197
|
+
move_cursor 0
|
198
|
+
end
|
199
|
+
|
200
|
+
# Change the file permission of the selected files and directories.
|
201
|
+
#
|
202
|
+
# ==== Parameters
|
203
|
+
# * +mode+ - Unix chmod string (e.g. +w, g-r)
|
204
|
+
#
|
205
|
+
#TODO: accept number forms such as 755, 0644
|
206
|
+
def chmod(mode = nil)
|
207
|
+
return unless mode
|
208
|
+
FileUtils.chmod mode, selected_items.map(&:path)
|
209
|
+
ls
|
210
|
+
end
|
211
|
+
|
212
|
+
# Fetch files from current directory.
|
213
|
+
def fetch_items_from_filesystem
|
214
|
+
@items = Dir.foreach(current_dir).map {|fn| Item.new dir: current_dir, name: fn, window_width: maxx}.to_a
|
215
|
+
end
|
216
|
+
|
217
|
+
# Focus at the first file or directory of which name starts with the given String.
|
218
|
+
def find(str)
|
219
|
+
index = items.index {|i| i.name.start_with? str}
|
220
|
+
move_cursor index if index
|
221
|
+
end
|
222
|
+
|
223
|
+
# Focus at the last file or directory of which name starts with the given String.
|
224
|
+
def find_reverse(str)
|
225
|
+
index = items.reverse.index {|i| i.name.start_with? str}
|
226
|
+
move_cursor items.length - index - 1 if index
|
227
|
+
end
|
228
|
+
|
229
|
+
# Width of the currently active pane.
|
230
|
+
def maxx
|
231
|
+
main.maxx
|
232
|
+
end
|
233
|
+
|
234
|
+
# Height of the currently active pane.
|
235
|
+
def maxy
|
236
|
+
main.maxy
|
237
|
+
end
|
238
|
+
|
239
|
+
# Number of files or directories that the current main window can show in a page.
|
240
|
+
def max_items
|
241
|
+
main.max_items
|
242
|
+
end
|
243
|
+
|
244
|
+
# Update the main window with the loaded files and directories. Also update the header.
|
245
|
+
def draw_items
|
246
|
+
main.draw_items_to_each_pane (@displayed_items = items[current_page * max_items, max_items])
|
247
|
+
header_l.draw_path_and_page_number path: current_dir, current: current_page + 1, total: total_pages
|
248
|
+
end
|
249
|
+
|
250
|
+
# Sort the loaded files and directories in already given sort order.
|
251
|
+
def sort_items_according_to_current_direction
|
252
|
+
case @direction
|
253
|
+
when nil
|
254
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map(&:sort)
|
255
|
+
when 'r'
|
256
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort.reverse}
|
257
|
+
when 'S', 's'
|
258
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by {|i| -i.size}}
|
259
|
+
when 'Sr', 'sr'
|
260
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:size)}
|
261
|
+
when 't'
|
262
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.mtime <=> x.mtime}}
|
263
|
+
when 'tr'
|
264
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:mtime)}
|
265
|
+
when 'c'
|
266
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.ctime <=> x.ctime}}
|
267
|
+
when 'cr'
|
268
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:ctime)}
|
269
|
+
when 'u'
|
270
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.atime <=> x.atime}}
|
271
|
+
when 'ur'
|
272
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:atime)}
|
273
|
+
when 'e'
|
274
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.extname <=> x.extname}}
|
275
|
+
when 'er'
|
276
|
+
@items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:extname)}
|
277
|
+
end
|
278
|
+
items.each.with_index {|item, index| item.index = index}
|
279
|
+
end
|
280
|
+
|
281
|
+
# Search files and directories from the current directory, and update the screen.
|
282
|
+
#
|
283
|
+
# * +pattern+ - Search pattern against file names in Ruby Regexp string.
|
284
|
+
#
|
285
|
+
# === Example
|
286
|
+
#
|
287
|
+
# a : Search files that contains the letter "a" in their file name
|
288
|
+
# .*\.pdf$ : Search PDF files
|
289
|
+
def grep(pattern = '.*')
|
290
|
+
regexp = Regexp.new(pattern)
|
291
|
+
fetch_items_from_filesystem
|
292
|
+
@items = items.shift(2) + items.select {|i| i.name =~ regexp}
|
293
|
+
sort_items_according_to_current_direction
|
294
|
+
switch_page 0
|
295
|
+
move_cursor 0
|
296
|
+
|
297
|
+
draw_total_items
|
298
|
+
end
|
299
|
+
|
300
|
+
# Copy selected files and directories to the destination.
|
301
|
+
def cp(dest)
|
302
|
+
src = (m = marked_items).any? ? m.map(&:path) : current_item.path
|
303
|
+
FileUtils.cp_r src, File.join(current_dir, dest)
|
304
|
+
ls
|
305
|
+
end
|
306
|
+
|
307
|
+
# Move selected files and directories to the destination.
|
308
|
+
def mv(dest)
|
309
|
+
src = (m = marked_items).any? ? m.map(&:path) : current_item.path
|
310
|
+
FileUtils.mv src, File.join(current_dir, dest)
|
311
|
+
ls
|
312
|
+
end
|
313
|
+
|
314
|
+
# Rename selected files and directories.
|
315
|
+
#
|
316
|
+
# ==== Parameters
|
317
|
+
# * +pattern+ - / separated Regexp like string
|
318
|
+
def rename(pattern)
|
319
|
+
from, to = pattern.split '/'
|
320
|
+
from = Regexp.new from
|
321
|
+
selected_items.each do |item|
|
322
|
+
name = item.name.gsub from, to
|
323
|
+
FileUtils.mv item.path, File.join(current_dir, name)
|
324
|
+
end
|
325
|
+
ls
|
326
|
+
end
|
327
|
+
|
328
|
+
# Create a new directory.
|
329
|
+
def mkdir(dir)
|
330
|
+
FileUtils.mkdir_p File.join(current_dir, dir)
|
331
|
+
ls
|
332
|
+
end
|
333
|
+
|
334
|
+
# Create a new empty file.
|
335
|
+
def touch(filename)
|
336
|
+
FileUtils.touch File.join(current_dir, filename)
|
337
|
+
ls
|
338
|
+
end
|
339
|
+
|
340
|
+
# Current page is the first page?
|
341
|
+
def first_page?
|
342
|
+
current_page == 0
|
343
|
+
end
|
344
|
+
|
345
|
+
# Do we have more pages?
|
346
|
+
def last_page?
|
347
|
+
current_page == total_pages - 1
|
348
|
+
end
|
349
|
+
|
350
|
+
# Number of pages in the current directory.
|
351
|
+
def total_pages
|
352
|
+
items.length / max_items + 1
|
353
|
+
end
|
354
|
+
|
355
|
+
# Move to the given page number.
|
356
|
+
#
|
357
|
+
# ==== Parameters
|
358
|
+
# * +page+ - Target page number
|
359
|
+
def switch_page(page)
|
360
|
+
@current_page = page
|
361
|
+
draw_items
|
362
|
+
end
|
363
|
+
|
364
|
+
# Update the header information concerning currently marked files or directories.
|
365
|
+
def draw_marked_items
|
366
|
+
items = marked_items
|
367
|
+
header_r.draw_marked_items count: items.size, size: items.inject(0) {|sum, i| sum += i.size}
|
368
|
+
end
|
369
|
+
|
370
|
+
# Update the header information concerning total files and directories in the current directory.
|
371
|
+
def draw_total_items
|
372
|
+
header_r.draw_total_items count: items.size, size: items.inject(0) {|sum, i| sum += i.size}
|
373
|
+
end
|
374
|
+
|
375
|
+
def toggle_mark
|
376
|
+
main.toggle_mark current_item
|
377
|
+
end
|
378
|
+
|
379
|
+
# Accept user input, and directly execute it as a Ruby method call to the controller.
|
380
|
+
#
|
381
|
+
# ==== Parameters
|
382
|
+
# * +preset_command+ - A command that would be displayed at the command line before user input.
|
383
|
+
def process_command_line(preset_command: nil)
|
384
|
+
prompt = preset_command ? ":#{preset_command} " : ':'
|
385
|
+
command_line.set_prompt prompt
|
386
|
+
cmd, *args = command_line.get_command(prompt: prompt).split(' ')
|
387
|
+
if cmd && !cmd.empty? && respond_to?(cmd)
|
388
|
+
self.public_send cmd, *args
|
389
|
+
command_line.wclear
|
390
|
+
command_line.wrefresh
|
391
|
+
end
|
392
|
+
rescue Interrupt
|
393
|
+
command_line.wclear
|
394
|
+
command_line.wrefresh
|
395
|
+
end
|
396
|
+
|
397
|
+
# Accept user input, and directly execute it in an external shell.
|
398
|
+
def process_shell_command
|
399
|
+
command_line.set_prompt ':!'
|
400
|
+
cmd = command_line.get_command(prompt: ':!')[1..-1]
|
401
|
+
execute_external_command pause: true do
|
402
|
+
system cmd
|
403
|
+
end
|
404
|
+
rescue Interrupt
|
405
|
+
ensure
|
406
|
+
command_line.wclear
|
407
|
+
command_line.wrefresh
|
408
|
+
end
|
409
|
+
|
410
|
+
# Let the user answer y or n.
|
411
|
+
#
|
412
|
+
# ==== Parameters
|
413
|
+
# * +prompt+ - Prompt message
|
414
|
+
def ask(prompt = '(y/n)')
|
415
|
+
command_line.set_prompt prompt
|
416
|
+
command_line.wrefresh
|
417
|
+
while (c = Curses.getch)
|
418
|
+
next unless [78, 89, 110, 121, 3, 27] .include? c # N, Y, n, y, ^c, esc
|
419
|
+
command_line.wclear
|
420
|
+
command_line.wrefresh
|
421
|
+
break [89, 121].include? c # Y, y
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Open current file or directory with the editor.
|
426
|
+
def edit
|
427
|
+
execute_external_command do
|
428
|
+
editor = ENV['EDITOR'] || 'vim'
|
429
|
+
system %Q[#{editor} "#{current_item.path}"]
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# Open current file or directory with the viewer.
|
434
|
+
def view
|
435
|
+
execute_external_command do
|
436
|
+
pager = ENV['PAGER'] || 'less'
|
437
|
+
system %Q[#{pager} "#{current_item.path}"]
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
private
|
442
|
+
def execute_external_command(pause: false)
|
443
|
+
Curses.def_prog_mode
|
444
|
+
Curses.endwin
|
445
|
+
yield
|
446
|
+
ensure
|
447
|
+
Curses.reset_prog_mode
|
448
|
+
Curses.getch if pause
|
449
|
+
Curses.refresh
|
450
|
+
end
|
451
|
+
|
452
|
+
def debug(str)
|
453
|
+
header_r.debug str
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|