terminal-file-picker 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/lib/terminal-file-picker/file_browser_model.rb +70 -0
- data/lib/terminal-file-picker/file_picker.rb +33 -74
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f07dbb5a7aa9a96128b360a8c326301aac7cad3783a9962bf7a99b9921f769bb
|
4
|
+
data.tar.gz: 49164b98d2645238d09777eda25d28272089a7e2d7f6cc12c6d2b2352e1ccfba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca6b198dada64c7612e21130f9a01b6f960008c91eacdd5a4b6315d845bc7f6d2da64b28fe4cec4ca72e5be3be097d6788ff2a2b4214c5f0736e7d071cedf814
|
7
|
+
data.tar.gz: 032e125883ceac5aeb4ab844602640aff05c02026b30a1b1f04d12cfaaaccfdd303592bc7aa93e132d18945af00a37142a3e81763aa660f09f4d59621038c490
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/terminal-file-picker.svg)](https://badge.fury.io/rb/terminal-file-picker)
|
4
4
|
|
5
|
-
<img alt="Gif showing usage" src="https://raw.githubusercontent.com/ilkutkutlar/terminal-file-picker/
|
5
|
+
<img alt="Gif showing usage" src="https://raw.githubusercontent.com/ilkutkutlar/terminal-file-picker/main/usage.gif" width=70%>
|
6
6
|
|
7
7
|
This gem shows an interactive terminal file picker to user, allowing them to browse their files with arrow keys and file. The picked file path is then returned to the calling program. The file picker is completely text-based and can be used with a terminal or terminal emulator. It is essentially a terminal version of a GUI file choosing dialogue.
|
8
8
|
|
@@ -25,15 +25,15 @@ gem 'terminal-file-picker'
|
|
25
25
|
# Simple Usage
|
26
26
|
|
27
27
|
```rb
|
28
|
-
# Only required argument is the "root path". The user
|
29
|
-
# navigating from the root path
|
30
|
-
#
|
28
|
+
# Only required argument is the "root path". The user will
|
29
|
+
# start navigating from the root path once file picker is invoked.
|
30
|
+
# Root path can be either absolute or relative.
|
31
31
|
picker = FilePicker.new('.')
|
32
32
|
|
33
|
-
# This brings up the interactive file picker. Once the user
|
34
|
-
# has picked a file, the picker is cleared from screen and
|
35
|
-
# the chosen file path returned.
|
36
33
|
|
34
|
+
# `pick_file` brings up the interactive file picker. Once the user
|
35
|
+
# has picked a file, the picker is cleared from screen and
|
36
|
+
# the chosen file path (as an absolute path) is returned.
|
37
37
|
# If user picks a directory, instead of returning its path,
|
38
38
|
# the files inside the chosen directory is shown instead.
|
39
39
|
puts(picker.pick_file)
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'pry'
|
2
|
+
|
3
|
+
# Functions related to retrieving and formatting
|
4
|
+
# information related to directories and their contents.
|
5
|
+
class FileBrowserModel
|
6
|
+
attr_accessor :selected, :page, :current_path, :files
|
7
|
+
|
8
|
+
def initialize(starting_path, options = {})
|
9
|
+
@options = options
|
10
|
+
@current_path = starting_path
|
11
|
+
@page = 0
|
12
|
+
@selected = 0
|
13
|
+
@files = order_files(files_in_dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
def files_in_dir
|
17
|
+
date_format = @options.fetch(:date_format, '%d/%m/%Y')
|
18
|
+
time_format = @options.fetch(:time_format, '%H:%M')
|
19
|
+
|
20
|
+
Dir.entries(@current_path).map do |f|
|
21
|
+
file_path = File.join(@current_path, f)
|
22
|
+
|
23
|
+
name = add_indicator(f)
|
24
|
+
|
25
|
+
size_bytes = File.size(file_path)
|
26
|
+
|
27
|
+
mtime = File.mtime(file_path)
|
28
|
+
date_mod = mtime.strftime(date_format)
|
29
|
+
time_mod = mtime.strftime(time_format)
|
30
|
+
|
31
|
+
[name, size_bytes, date_mod, time_mod]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Order files such that '.' and '..' come before
|
36
|
+
# all the other files.
|
37
|
+
def order_files(files)
|
38
|
+
# Put "." and ".." at the start
|
39
|
+
groups = files.group_by do |f|
|
40
|
+
if f.first == './' || f.first == '../'
|
41
|
+
:dots
|
42
|
+
else
|
43
|
+
:files
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sort so that "." comes before ".."
|
48
|
+
(groups[:dots] || []).sort.reverse + (groups[:files] || [])
|
49
|
+
end
|
50
|
+
|
51
|
+
# Absolute path of the currently selected file
|
52
|
+
def selected_absolute_path
|
53
|
+
selected_file_name = @files[@selected].first
|
54
|
+
# This may not be the absolute path (e.g. file_name may be '.')
|
55
|
+
selected_full_path = File.join(@current_path, selected_file_name)
|
56
|
+
File.absolute_path(selected_full_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def path_rel_to_start(file_name)
|
60
|
+
File.join(@current_path, file_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def add_indicator(file_name)
|
66
|
+
return "#{file_name}/" if File.directory?(path_rel_to_start(file_name))
|
67
|
+
|
68
|
+
file_name
|
69
|
+
end
|
70
|
+
end
|
@@ -1,24 +1,24 @@
|
|
1
|
+
require 'pry'
|
1
2
|
require 'tty-cursor'
|
2
3
|
require 'tty-reader'
|
3
4
|
require_relative 'file_browser_view'
|
4
5
|
require_relative 'helper'
|
6
|
+
require_relative 'file_browser_model'
|
5
7
|
|
6
8
|
# Responsible for keeping the state of the interactive file picker.
|
7
9
|
# Also responds to user input to modify the state and redraw
|
8
10
|
# file picker to reflect new state.
|
9
11
|
class FilePicker
|
10
|
-
def initialize(
|
11
|
-
@
|
12
|
-
@
|
12
|
+
def initialize(start_dir_path, options = {})
|
13
|
+
@model = FileBrowserModel.new(start_dir_path, options)
|
14
|
+
@view = FileBrowserView.new(options)
|
13
15
|
@reader = TTY::Reader.new(interrupt: :exit)
|
14
16
|
@reader.subscribe(self)
|
15
|
-
@user_have_picked = false
|
16
|
-
@page = 0
|
17
17
|
@cursor = TTY::Cursor
|
18
|
+
@user_have_picked = false
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
change_directory(dir_path)
|
20
|
+
absolute_start_path = File.absolute_path(start_dir_path)
|
21
|
+
change_directory(absolute_start_path)
|
22
22
|
end
|
23
23
|
|
24
24
|
def pick_file
|
@@ -30,22 +30,22 @@ class FilePicker
|
|
30
30
|
end
|
31
31
|
|
32
32
|
print(@cursor.clear_screen_down)
|
33
|
-
|
33
|
+
@model.selected_absolute_path
|
34
34
|
end
|
35
35
|
|
36
36
|
def keydown(_event)
|
37
|
-
@selected += 1 unless selected_at_bottom?
|
37
|
+
@model.selected += 1 unless selected_at_bottom?
|
38
38
|
if selected_below_page?
|
39
|
-
@page += 1
|
39
|
+
@model.page += 1
|
40
40
|
print(@cursor.clear_screen_down)
|
41
41
|
end
|
42
42
|
redraw(true)
|
43
43
|
end
|
44
44
|
|
45
45
|
def keyup(_event)
|
46
|
-
@selected -= 1 unless selected_at_top?
|
46
|
+
@model.selected -= 1 unless selected_at_top?
|
47
47
|
if selected_above_page?
|
48
|
-
@page -= 1
|
48
|
+
@model.page -= 1
|
49
49
|
print(@cursor.clear_screen_down)
|
50
50
|
end
|
51
51
|
redraw(true)
|
@@ -54,8 +54,10 @@ class FilePicker
|
|
54
54
|
def keypress(event)
|
55
55
|
case event.value
|
56
56
|
when "\r"
|
57
|
-
|
58
|
-
|
57
|
+
selected = @model.selected_absolute_path
|
58
|
+
|
59
|
+
if File.directory?(selected)
|
60
|
+
change_directory(selected)
|
59
61
|
print(@cursor.clear_screen_down)
|
60
62
|
# Cache keeps a rendering of current directory.
|
61
63
|
# Going to a new directory, so needs to refresh
|
@@ -69,78 +71,35 @@ class FilePicker
|
|
69
71
|
|
70
72
|
private
|
71
73
|
|
74
|
+
def change_directory(absolute_file_path)
|
75
|
+
@model.current_path = absolute_file_path
|
76
|
+
@model.page = 0
|
77
|
+
@model.selected = 0
|
78
|
+
@model.files = @model.order_files(@model.files_in_dir)
|
79
|
+
end
|
80
|
+
|
72
81
|
def redraw(use_cache)
|
73
|
-
rendered = @
|
82
|
+
rendered = @view.render(@model.current_path,
|
83
|
+
@model.files,
|
84
|
+
@model.selected,
|
85
|
+
@model.page,
|
86
|
+
use_cache)
|
74
87
|
Helper.print_in_place(rendered)
|
75
88
|
end
|
76
89
|
|
77
90
|
def selected_at_top?
|
78
|
-
@selected.zero?
|
91
|
+
@model.selected.zero?
|
79
92
|
end
|
80
93
|
|
81
94
|
def selected_at_bottom?
|
82
|
-
@selected == @files.length - 1
|
95
|
+
@model.selected == @model.files.length - 1
|
83
96
|
end
|
84
97
|
|
85
98
|
def selected_above_page?
|
86
|
-
@selected < (@page * @
|
99
|
+
@model.selected < (@model.page * @view.files_per_page)
|
87
100
|
end
|
88
101
|
|
89
102
|
def selected_below_page?
|
90
|
-
@selected > (@page * @
|
91
|
-
end
|
92
|
-
|
93
|
-
def files_in_dir(dir_path)
|
94
|
-
Dir.entries(dir_path).map do |f|
|
95
|
-
size_bytes = File.size(full_path(f))
|
96
|
-
|
97
|
-
mtime = File.mtime(full_path(f))
|
98
|
-
date_mod = mtime.strftime(@date_format)
|
99
|
-
time_mod = mtime.strftime(@time_format)
|
100
|
-
|
101
|
-
name = file_display_name(f)
|
102
|
-
|
103
|
-
[name, size_bytes, date_mod, time_mod]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def change_directory(full_path)
|
108
|
-
@page = 0
|
109
|
-
@selected = 0
|
110
|
-
@current_path = full_path
|
111
|
-
@files = order_files(files_in_dir(@current_path))
|
112
|
-
end
|
113
|
-
|
114
|
-
def order_files(files)
|
115
|
-
# Put "." and ".." at the start
|
116
|
-
groups = files.group_by do |f|
|
117
|
-
if f.first == '.' || f.first == '..'
|
118
|
-
:dots
|
119
|
-
else
|
120
|
-
:files
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Sort so that "." comes before ".."
|
125
|
-
(groups[:dots] || []).sort + (groups[:files] || [])
|
126
|
-
end
|
127
|
-
|
128
|
-
def full_path(file_name)
|
129
|
-
return @current_path if file_name == '.'
|
130
|
-
return "#{@current_path}#{file_name}" if @current_path[-1] == '/'
|
131
|
-
|
132
|
-
"#{@current_path}/#{file_name}"
|
133
|
-
end
|
134
|
-
|
135
|
-
def full_path_of_selected
|
136
|
-
full_path(@files[@selected].first)
|
137
|
-
end
|
138
|
-
|
139
|
-
def file_display_name(file_name)
|
140
|
-
if File.directory?(full_path(file_name))
|
141
|
-
return "#{file_name}/" unless ['.', '..'].include?(file_name)
|
142
|
-
end
|
143
|
-
|
144
|
-
file_name
|
103
|
+
@model.selected > (@model.page * @view.files_per_page) + @view.files_per_page - 1
|
145
104
|
end
|
146
105
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terminal-file-picker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilkut Kutlar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pastel
|
@@ -105,6 +105,7 @@ extra_rdoc_files: []
|
|
105
105
|
files:
|
106
106
|
- README.md
|
107
107
|
- lib/terminal-file-picker.rb
|
108
|
+
- lib/terminal-file-picker/file_browser_model.rb
|
108
109
|
- lib/terminal-file-picker/file_browser_view.rb
|
109
110
|
- lib/terminal-file-picker/file_picker.rb
|
110
111
|
- lib/terminal-file-picker/helper.rb
|