rfd 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|