ncurses-lyra 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +60 -0
- data/Rakefile +2 -0
- data/exe/lyra.rb +768 -0
- data/lib/ncurses/lyra/version.rb +5 -0
- data/ncurses-lyra.gemspec +28 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: acdc28dedffd82821ba897ad9e89fa1a819f92f3281dd3d7277c87d62e28d14f
|
4
|
+
data.tar.gz: a6780129be3192e7adde4071395a23a7e47c74a195b78033d74b64d5c4ca8e0c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1a3e2de948cf59f3fef75c4625b0df6f2e0616672b8ff886bde71ea139d9c5fb4c8e90dd2d34f6b9c1ee4eba69089f7637a75a637eba21be4281d9a4d91d6196
|
7
|
+
data.tar.gz: 74f2403bfa38554936f8693a7762971da6985ce092c2d3e197a0549b74c75c38ab7a97f61ed70bd351669cd32a95a2f2871a3e6faa4314d66fe5a91bd80558c8
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 kepler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 kepler
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# ncurses-lyra
|
2
|
+
|
3
|
+
A simple small file lister and directory explorer.
|
4
|
+
One can use left and right arrow keys to open directories and file, or go back up to higher directory.
|
5
|
+
By default it opens files using PAGER, but files can be opened in EDITOR too using RETURN or e.
|
6
|
+
|
7
|
+
The code has been kept in one file so that the file can just be copied into one's bin folder or anywhere in the path.
|
8
|
+
This is simple ruby code, so you can edit it, or change colors or even expand this simple program.
|
9
|
+
|
10
|
+
Feel free to modify the code to your needs. You can submit patches if you feel it will help others.
|
11
|
+
|
12
|
+
|
13
|
+
I will add very little functionality to this as I use it, perhaps deleting a file, but I don't intend to let this become much larger.
|
14
|
+
|
15
|
+
File listing and sorting is done through `zsh` itself. So the `zsh` executable should be somewhere in path.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
1.
|
20
|
+
gem install ncurses-lyra
|
21
|
+
|
22
|
+
You might want to alias the lyra file to a single letter.
|
23
|
+
|
24
|
+
alias n='~/PATH/lyra.rb'
|
25
|
+
|
26
|
+
You may also copy lyra.rb to your PATH.
|
27
|
+
|
28
|
+
2. Copying the executable file
|
29
|
+
|
30
|
+
This does depend on the `ffi-ncurses` gem by Sean Halpin.
|
31
|
+
That gem should have got installed when you installed the gem. If you did not install the gem, and just copied the lyra.rb file from the exe dir, then you will have to install that gem.
|
32
|
+
|
33
|
+
gem install ffi-ncurses
|
34
|
+
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Call the program using the program name.
|
39
|
+
|
40
|
+
Use ARROW keys to view a file, or enter a directory, or go up.
|
41
|
+
Use '/' (slash) and enter a few characters and <RETURN> to see a filtered list.
|
42
|
+
|
43
|
+
There are a couple of menus with minimal options.
|
44
|
+
Tilde gives the main menu which contains a sort menu.
|
45
|
+
The toggle menu is on the equal key (=) and one can toggle hidden files, or long listing.
|
46
|
+
A few more options will be added with time.
|
47
|
+
|
48
|
+
Navigation can be accelerated with C-n, C-p, C-d, C-b, <space>, g (top), G (bottom).
|
49
|
+
|
50
|
+
Help on keys needs to come on pressing `?`
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mare-imbrium/ncurses-lyra.
|
57
|
+
|
58
|
+
## License
|
59
|
+
|
60
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/exe/lyra.rb
ADDED
@@ -0,0 +1,768 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'ffi-ncurses'
|
3
|
+
require 'ffi-ncurses/widechars'
|
4
|
+
# ----------------------------------------------------------------------------- #
|
5
|
+
# File: lyra.rb
|
6
|
+
# Description: a quick small directory lister aimed at being simple fast with minimal
|
7
|
+
# features, and mostly for viewing files quickly through PAGER
|
8
|
+
# Author: j kepler http://github.com/mare-imbrium/
|
9
|
+
# Date: 2018-03-09
|
10
|
+
# License: MIT
|
11
|
+
# Last update: 2018-05-05 12:40
|
12
|
+
# ----------------------------------------------------------------------------- #
|
13
|
+
# lyra.rb Copyright (C) 2012-2018 j kepler
|
14
|
+
# == TODO
|
15
|
+
# [ ] make a help screen on ?
|
16
|
+
# [ ] move to lyra or some gem and publish
|
17
|
+
# [ ] pop directories
|
18
|
+
# [ ] go back to start directory
|
19
|
+
# [ ] go to given directory
|
20
|
+
# [x] open files on RIGHT arrow in view (?)
|
21
|
+
# [ ] in a long listing, how to get to a file name. first char or pattern TODO
|
22
|
+
# [ ] pressing p should open PAGER, e EDITOR, m MOST, v - view
|
23
|
+
# [x] on zip file show contents in pager. x to extract.
|
24
|
+
# [x] when going up a directory keep cursor on the directory we came from
|
25
|
+
# [x] space bar to page down. also page up on c-n c-p top bottom
|
26
|
+
# [x] hide dot files
|
27
|
+
# [x] reveal dot files on toggle
|
28
|
+
# [x] long listing files on toggle
|
29
|
+
# [x] long file names not getting cleared
|
30
|
+
# [ ] allow entry of command and page output or show in PAGER
|
31
|
+
# [x] pressing ENTER should invoke EDITOR
|
32
|
+
# [x] scrolling up behavior not correct. we should scroll up from first row not last.
|
33
|
+
# see vifm for correct way. mc has different behavior
|
34
|
+
# ----------
|
35
|
+
# == CHANGELOG
|
36
|
+
#
|
37
|
+
#
|
38
|
+
# --------
|
39
|
+
#
|
40
|
+
|
41
|
+
|
42
|
+
BOLD = FFI::NCurses::A_BOLD
|
43
|
+
REVERSE = FFI::NCurses::A_REVERSE
|
44
|
+
UNDERLINE = FFI::NCurses::A_UNDERLINE
|
45
|
+
NORMAL = FFI::NCurses::A_NORMAL
|
46
|
+
COLOR_BLACK = FFI::NCurses::BLACK
|
47
|
+
COLOR_WHITE = FFI::NCurses::WHITE
|
48
|
+
COLOR_BLUE = FFI::NCurses::BLUE
|
49
|
+
COLOR_RED = FFI::NCurses::RED
|
50
|
+
COLOR_GREEN = FFI::NCurses::GREEN
|
51
|
+
|
52
|
+
def init_curses
|
53
|
+
FFI::NCurses.initscr
|
54
|
+
FFI::NCurses.curs_set 1
|
55
|
+
FFI::NCurses.raw
|
56
|
+
FFI::NCurses.noecho
|
57
|
+
FFI::NCurses.keypad FFI::NCurses.stdscr, true
|
58
|
+
FFI::NCurses.scrollok FFI::NCurses.stdscr, true
|
59
|
+
if FFI::NCurses.has_colors
|
60
|
+
FFI::NCurses.start_color
|
61
|
+
std_colors
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# COLOR_BLACK 0
|
67
|
+
# COLOR_RED 1
|
68
|
+
# COLOR_GREEN 2
|
69
|
+
# COLOR_YELLOW 3
|
70
|
+
# COLOR_BLUE 4
|
71
|
+
# COLOR_MAGENTA 5
|
72
|
+
# COLOR_CYAN 6
|
73
|
+
# COLOR_WHITE 7
|
74
|
+
|
75
|
+
# In case, the init_pairs are changed, then update these as well, so that the programs use the correct pairs.
|
76
|
+
CP_BLACK = 0
|
77
|
+
CP_RED = 1
|
78
|
+
CP_GREEN = 2
|
79
|
+
CP_YELLOW = 3
|
80
|
+
CP_BLUE = 4
|
81
|
+
CP_MAGENTA = 5
|
82
|
+
CP_CYAN = 6
|
83
|
+
CP_WHITE = 7
|
84
|
+
# defining various colors
|
85
|
+
# NOTE this should be done by application or else we will be changing this all the time.
|
86
|
+
def std_colors
|
87
|
+
FFI::NCurses.use_default_colors
|
88
|
+
# 2018-03-17 - changing it to ncurses defaults
|
89
|
+
FFI::NCurses.init_pair(0, FFI::NCurses::BLACK, -1)
|
90
|
+
FFI::NCurses.init_pair(1, FFI::NCurses::RED, -1)
|
91
|
+
FFI::NCurses.init_pair(2, FFI::NCurses::GREEN, -1)
|
92
|
+
FFI::NCurses.init_pair(3, FFI::NCurses::YELLOW, -1)
|
93
|
+
FFI::NCurses.init_pair(4, FFI::NCurses::BLUE, -1)
|
94
|
+
FFI::NCurses.init_pair(5, FFI::NCurses::MAGENTA, -1)
|
95
|
+
FFI::NCurses.init_pair(6, FFI::NCurses::CYAN, -1)
|
96
|
+
FFI::NCurses.init_pair(7, FFI::NCurses::WHITE, -1)
|
97
|
+
# ideally the rest should be done by application
|
98
|
+
#FFI::NCurses.init_pair(8, FFI::NCurses::WHITE, -1)
|
99
|
+
#FFI::NCurses.init_pair(9, FFI::NCurses::BLUE, -1)
|
100
|
+
#FFI::NCurses.init_pair(10, FFI::NCurses::BLACK, FFI::NCurses::CYAN)
|
101
|
+
# alert
|
102
|
+
FFI::NCurses.init_pair(12, FFI::NCurses::BLACK, FFI::NCurses::BLUE)
|
103
|
+
#FFI::NCurses.init_pair(13, FFI::NCurses::BLACK, FFI::NCurses::MAGENTA)
|
104
|
+
#
|
105
|
+
# needed by menu
|
106
|
+
FFI::NCurses.init_pair(14, FFI::NCurses::WHITE, FFI::NCurses::CYAN)
|
107
|
+
end
|
108
|
+
|
109
|
+
# create and return a color_pair for a combination of bg and fg.
|
110
|
+
# This will always return the same color_pair so a duplicate one will not be created.
|
111
|
+
# @param bgcolor [Integer] color of background e.g., COLOR_BLACK
|
112
|
+
# @param fgcolor [Integer] color of foreground e.g., COLOR_WHITE
|
113
|
+
# @return [Integer] - color_pair which can be passed to #printstring, or used directly as #COLOR_PAIR(int)
|
114
|
+
def create_color_pair(bgcolor, fgcolor)
|
115
|
+
code = (bgcolor*10) + fgcolor
|
116
|
+
FFI::NCurses.init_pair(code, fgcolor, bgcolor)
|
117
|
+
return code
|
118
|
+
end
|
119
|
+
#
|
120
|
+
## Window class
|
121
|
+
# Creates and manages the underlying window in which we write or place a form and fields.
|
122
|
+
# The two important methods here are the constructor, and +destroy()+.
|
123
|
+
# +pointer+ is important for making further direct calls to FFI::NCurses.
|
124
|
+
class Window
|
125
|
+
# pointer to FFI routines, use when calling FFI directly.
|
126
|
+
attr_reader :pointer # window pointer
|
127
|
+
attr_reader :panel # panel associated with window
|
128
|
+
attr_reader :width, :height, :top, :left
|
129
|
+
|
130
|
+
# creates a window with given height, width, top and left.
|
131
|
+
# If no args given, creates a root window (i.e. full size).
|
132
|
+
# @param height [Integer]
|
133
|
+
# @param width [Integer]
|
134
|
+
# @param top [Integer]
|
135
|
+
# @param left [Integer]
|
136
|
+
def initialize h=0, w=0, top=0, left=0
|
137
|
+
@height, @width, @top, @left = h, w, top, left
|
138
|
+
|
139
|
+
@height = FFI::NCurses.LINES if @height == 0 # 2011-11-14 added since tired of checking for zero
|
140
|
+
@width = FFI::NCurses.COLS if @width == 0
|
141
|
+
@pointer = FFI::NCurses.newwin(@height, @width, @top, @left) # added FFI 2011-09-6
|
142
|
+
|
143
|
+
@panel = FFI::NCurses.new_panel(@pointer)
|
144
|
+
FFI::NCurses.keypad(@pointer, true)
|
145
|
+
return @pointer
|
146
|
+
end
|
147
|
+
|
148
|
+
# print string at x, y coordinates. replace this with the original one below
|
149
|
+
# @deprecated
|
150
|
+
def printstr(str, x=0,y=0)
|
151
|
+
win = @pointer
|
152
|
+
FFI::NCurses.wmove(win, x, y)
|
153
|
+
FFI::NCurses.waddstr win, str
|
154
|
+
end
|
155
|
+
|
156
|
+
# 2018-03-08 - taken from canis reduced
|
157
|
+
# print given string at row, col with given color and attributes
|
158
|
+
# @param row [Integer] row to print on
|
159
|
+
# @param col [Integer] column to print on
|
160
|
+
# @param color [Integer] color_pair created earlier
|
161
|
+
# @param attr [Integer] any of the four FFI attributes, e.g. A_BOLD, A_REVERSE
|
162
|
+
def printstring(r,c,string, color=0, att = FFI::NCurses::A_NORMAL)
|
163
|
+
|
164
|
+
#$log.debug "printstring recvd nil row #{r} or col #{c}, color:#{color},att:#{att}." if $log
|
165
|
+
raise "printstring recvd nil row #{r} or col #{c}, color:#{color},att:#{att} " if r.nil? || c.nil?
|
166
|
+
att ||= FFI::NCurses::A_NORMAL
|
167
|
+
color ||= 0
|
168
|
+
raise "color is nil " unless color
|
169
|
+
raise "att is nil " unless att
|
170
|
+
|
171
|
+
FFI::NCurses.wattron(@pointer, FFI::NCurses.COLOR_PAIR(color) | att)
|
172
|
+
FFI::NCurses.mvwprintw(@pointer, r, c, "%s", :string, string);
|
173
|
+
FFI::NCurses.wattroff(@pointer, FFI::NCurses.COLOR_PAIR(color) | att)
|
174
|
+
end
|
175
|
+
##
|
176
|
+
# Get a key from the standard input.
|
177
|
+
#
|
178
|
+
# This will get control keys and function keys but not Alt keys.
|
179
|
+
# This is usually called in a loop by the main program.
|
180
|
+
# It returns the ascii code (integer).
|
181
|
+
# 1 is Ctrl-a .... 27 is Esc
|
182
|
+
# FFI already has constants declared for function keys and control keys for checkin against.
|
183
|
+
# Can return a 3 or -1 if user pressed Control-C.
|
184
|
+
#
|
185
|
+
# NOTE: For ALT keys we need to check for 27/Esc and if so, then do another read
|
186
|
+
# with a timeout. If we get a key, then resolve. Otherwise, it is just ESC
|
187
|
+
# @return [Integer] ascii code of key
|
188
|
+
def getch
|
189
|
+
ch = FFI::NCurses.wgetch(@pointer)
|
190
|
+
rescue SystemExit, Interrupt
|
191
|
+
3 # is C-c
|
192
|
+
rescue StandardError
|
193
|
+
-1 # is C-c
|
194
|
+
end
|
195
|
+
alias :getkey :getch
|
196
|
+
|
197
|
+
# refresh the window (wrapper)
|
198
|
+
# To be called after printing on a window.
|
199
|
+
def wrefresh
|
200
|
+
FFI::NCurses.wrefresh(@pointer)
|
201
|
+
end
|
202
|
+
# destroy the window and the panel.
|
203
|
+
# This is important. It should be placed in the ensure block of caller application, so it happens.
|
204
|
+
def destroy
|
205
|
+
FFI::NCurses.del_panel(@panel) if @panel
|
206
|
+
FFI::NCurses.delwin(@pointer) if @pointer
|
207
|
+
@panel = @pointer = nil # prevent call twice
|
208
|
+
end
|
209
|
+
# route other methods to ffi. {{{
|
210
|
+
# This should preferable NOT be used. Better to use the direct call itself.
|
211
|
+
# It attempts to route other calls to FFI::NCurses by trying to add w to the name and passing the pointer.
|
212
|
+
# I would like to remove this at some time.
|
213
|
+
def method_missing(name, *args)
|
214
|
+
name = name.to_s
|
215
|
+
raise "method missing !!! #{name}"
|
216
|
+
if (name[0,2] == "mv")
|
217
|
+
test_name = name.dup
|
218
|
+
test_name[2,0] = "w" # insert "w" after"mv"
|
219
|
+
if (FFI::NCurses.respond_to?(test_name))
|
220
|
+
return FFI::NCurses.send(test_name, @pointer, *args)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
test_name = "w" + name
|
224
|
+
if (FFI::NCurses.respond_to?(test_name))
|
225
|
+
return FFI::NCurses.send(test_name, @pointer, *args)
|
226
|
+
end
|
227
|
+
FFI::NCurses.send(name, @pointer, *args)
|
228
|
+
end # }}}
|
229
|
+
|
230
|
+
# make a box around the window. Just a wrapper
|
231
|
+
def box
|
232
|
+
FFI::NCurses.box(@pointer, 0, 0)
|
233
|
+
end
|
234
|
+
# print a centered title on top of window
|
235
|
+
# This should be called after box, or else box will erase the title
|
236
|
+
# @param str [String] title to print
|
237
|
+
# @param color [Integer] color_pair
|
238
|
+
# @param att [Integer] attribute constant
|
239
|
+
def title str, color=0, att=BOLD
|
240
|
+
strl = str.length
|
241
|
+
col = (@width - strl)/2
|
242
|
+
printstring(0,col, str, color, att)
|
243
|
+
end
|
244
|
+
end # window
|
245
|
+
|
246
|
+
# a midnight commander like mc_menu
|
247
|
+
# Pass a hash of key and label.
|
248
|
+
# menu will only accept keys or arrow keys or C-c Esc to cancel
|
249
|
+
# returns nil if C-c or Esc pressed.
|
250
|
+
# Otherwise returns character pressed.
|
251
|
+
# == TODO
|
252
|
+
# depends on our window class which is minimal.
|
253
|
+
# [ ] cursor should show on the row that is highlighted
|
254
|
+
# [ ] Can we remove that dependency so this is independent
|
255
|
+
# Currently, we paint window each time user pressed up or down, but we can just repaint the attribute
|
256
|
+
# [ ] width of array items not checked. We could do that or have user pass it in.
|
257
|
+
# [ ] we are not scrolling if user sends in a large number of items. we should cap it to 10 or 20
|
258
|
+
# == CHANGELOG
|
259
|
+
#
|
260
|
+
class Menu
|
261
|
+
|
262
|
+
def initialize title, hash, config={}
|
263
|
+
|
264
|
+
@list = hash.values
|
265
|
+
@keys = hash.keys.collect { |x| x.to_s }
|
266
|
+
@hash = hash
|
267
|
+
bkgd = config[:bkgd] || FFI::NCurses.COLOR_PAIR(14) | BOLD
|
268
|
+
@attr = BOLD
|
269
|
+
@color_pair = config[:color_pair] || 14
|
270
|
+
ht = @list.size+2
|
271
|
+
wid = config[:width] || 40
|
272
|
+
top = (FFI::NCurses.LINES - ht)/2
|
273
|
+
left = (FFI::NCurses.COLS - wid)/2
|
274
|
+
@window = Window.new(ht, wid, top, left)
|
275
|
+
FFI::NCurses.wbkgd(@window.pointer, bkgd)
|
276
|
+
@window.box
|
277
|
+
@window.title(title)
|
278
|
+
@current = 0
|
279
|
+
print_items @hash
|
280
|
+
end
|
281
|
+
def print_items hash
|
282
|
+
ix = 0
|
283
|
+
hash.each_pair {|k, val|
|
284
|
+
attr = @attr
|
285
|
+
attr = REVERSE if ix == @current
|
286
|
+
@window.printstring(ix+1 , 2, "#{k} #{val}", @color_pair, attr )
|
287
|
+
ix += 1
|
288
|
+
}
|
289
|
+
@window.wrefresh
|
290
|
+
end
|
291
|
+
def getkey # in menu
|
292
|
+
ch = 0
|
293
|
+
char = nil
|
294
|
+
begin
|
295
|
+
while (ch = @window.getkey) != FFI::NCurses::KEY_CTRL_C
|
296
|
+
break if ch == 27 # ESC
|
297
|
+
tmpchar = FFI::NCurses.keyname(ch) rescue '?'
|
298
|
+
if @keys.include? tmpchar
|
299
|
+
char = tmpchar
|
300
|
+
break
|
301
|
+
end
|
302
|
+
case ch
|
303
|
+
when FFI::NCurses::KEY_DOWN
|
304
|
+
@current += 1
|
305
|
+
when FFI::NCurses::KEY_UP
|
306
|
+
@current -= 1
|
307
|
+
when ?g.getbyte(0)
|
308
|
+
@current_index = 0
|
309
|
+
when ?G.getbyte(0)
|
310
|
+
@current_index = @list.size-1
|
311
|
+
when FFI::NCurses::KEY_RETURN
|
312
|
+
char = @keys[@current]
|
313
|
+
break
|
314
|
+
end
|
315
|
+
@current = 0 if @current < 0
|
316
|
+
@current = @list.size-1 if @current >= @list.size
|
317
|
+
print_items @hash
|
318
|
+
|
319
|
+
# trap arrow keys here
|
320
|
+
end
|
321
|
+
ensure
|
322
|
+
@window.destroy
|
323
|
+
end
|
324
|
+
return char
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# main program
|
329
|
+
|
330
|
+
TOPLINE="| ` Menu | = Toggle | q Quit | lyra 0.1"
|
331
|
+
$sorto = "on"
|
332
|
+
$hidden = nil
|
333
|
+
$long_listing = false
|
334
|
+
$patt = nil
|
335
|
+
_LINES = FFI::NCurses.LINES-1
|
336
|
+
def create_footer_window h = 2 , w = FFI::NCurses.COLS, t = FFI::NCurses.LINES-2, l = 0
|
337
|
+
ewin = Window.new(h, w , t, l)
|
338
|
+
end
|
339
|
+
def create_input_window h = 1 , w = FFI::NCurses.COLS, t = FFI::NCurses.LINES-1, l = 0
|
340
|
+
ewin = Window.new(h, w , t, l)
|
341
|
+
end
|
342
|
+
# accepts user input in current window
|
343
|
+
# and returns characters after RETURN pressed
|
344
|
+
def getchars win, max=20
|
345
|
+
str = ""
|
346
|
+
pos = 0
|
347
|
+
filler = " "*max
|
348
|
+
pointer = win.pointer
|
349
|
+
y, x = FFI::NCurses.getyx(pointer)
|
350
|
+
while (ch = win.getkey) != FFI::NCurses::KEY_RETURN
|
351
|
+
#str << ch.chr
|
352
|
+
if ch > 27 and ch < 127
|
353
|
+
str.insert(pos, ch.chr)
|
354
|
+
pos += 1
|
355
|
+
#FFI::NCurses.waddstr(win.pointer, ch.chr)
|
356
|
+
end
|
357
|
+
case ch
|
358
|
+
when FFI::NCurses::KEY_LEFT
|
359
|
+
pos -= 1
|
360
|
+
pos = 0 if pos < 0
|
361
|
+
when FFI::NCurses::KEY_RIGHT
|
362
|
+
pos += 1
|
363
|
+
pos = str.size if pos >= str.size
|
364
|
+
when 127
|
365
|
+
pos -= 1 if pos > 0
|
366
|
+
str.slice!(pos,1) if pos >= 0 # no backspace if on first pos
|
367
|
+
when 27, FFI::NCurses::KEY_CTRL_C
|
368
|
+
return nil
|
369
|
+
end
|
370
|
+
FFI::NCurses.wmove(pointer, y,x)
|
371
|
+
FFI::NCurses.waddstr(pointer, filler)
|
372
|
+
FFI::NCurses.wmove(pointer, y,x)
|
373
|
+
FFI::NCurses.waddstr(pointer, str)
|
374
|
+
FFI::NCurses.wmove(pointer, y,pos+1) # set cursor to correct position
|
375
|
+
break if str.size >= max
|
376
|
+
end
|
377
|
+
str
|
378
|
+
end
|
379
|
+
# runs given command and returns.
|
380
|
+
# Does not wait, so command should be like an editor or be paged to less.
|
381
|
+
def shell_out command
|
382
|
+
FFI::NCurses.endwin
|
383
|
+
ret = system command
|
384
|
+
FFI::NCurses.refresh
|
385
|
+
end
|
386
|
+
|
387
|
+
## code related to long listing of files
|
388
|
+
GIGA_SIZE = 1073741824.0
|
389
|
+
MEGA_SIZE = 1048576.0
|
390
|
+
KILO_SIZE = 1024.0
|
391
|
+
|
392
|
+
# Return the file size with a readable style.
|
393
|
+
def readable_file_size(size, precision)
|
394
|
+
case
|
395
|
+
#when size == 1 : "1 B"
|
396
|
+
when size < KILO_SIZE then "%d B" % size
|
397
|
+
when size < MEGA_SIZE then "%.#{precision}f K" % (size / KILO_SIZE)
|
398
|
+
when size < GIGA_SIZE then "%.#{precision}f M" % (size / MEGA_SIZE)
|
399
|
+
else "%.#{precision}f G" % (size / GIGA_SIZE)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
## format date for file given stat
|
403
|
+
def date_format t
|
404
|
+
t.strftime "%Y/%m/%d"
|
405
|
+
end
|
406
|
+
# clears window but leaves top line
|
407
|
+
def clearwin(pointer)
|
408
|
+
FFI::NCurses.wmove(pointer, 1,0)
|
409
|
+
FFI::NCurses.wclrtobot(pointer)
|
410
|
+
end
|
411
|
+
##
|
412
|
+
def file_edit win, fp
|
413
|
+
#$log.debug " edit #{fp}"
|
414
|
+
editor = ENV['EDITOR'] || 'vi'
|
415
|
+
vimp = %x[which #{editor}].chomp
|
416
|
+
shell_out "#{vimp} #{fp}"
|
417
|
+
end
|
418
|
+
def file_open win, fp
|
419
|
+
unless File.exists? fp
|
420
|
+
pwd = %x[pwd]
|
421
|
+
#alert "No such file. My pwd is #{pwd} "
|
422
|
+
alert win, "No such file. My pwd is #{pwd} "
|
423
|
+
return
|
424
|
+
end
|
425
|
+
ft=%x[file #{fp}]
|
426
|
+
if ft.index("text")
|
427
|
+
file_edit win, fp
|
428
|
+
elsif ft.index(/zip/i)
|
429
|
+
shell_out "tar tvf #{fp} | less"
|
430
|
+
elsif ft.index(/directory/i)
|
431
|
+
shell_out "ls -lh #{fp} | less"
|
432
|
+
else
|
433
|
+
alert "#{fp} is not text, not opening (#{ft}) "
|
434
|
+
end
|
435
|
+
end
|
436
|
+
def file_page win, fp
|
437
|
+
unless File.exists? fp
|
438
|
+
pwd = %x[pwd]
|
439
|
+
alert "No such file. My pwd is #{pwd} "
|
440
|
+
return
|
441
|
+
end
|
442
|
+
ft=%x[file #{fp}]
|
443
|
+
if ft.index("text")
|
444
|
+
pager = ENV['PAGER'] || 'less'
|
445
|
+
vimp = %x[which #{pager}].chomp
|
446
|
+
shell_out "#{vimp} #{fp}"
|
447
|
+
elsif ft.index(/zip/i)
|
448
|
+
shell_out "tar tvf #{fp} | less"
|
449
|
+
elsif ft.index(/directory/i)
|
450
|
+
shell_out "ls -lh #{fp} | less"
|
451
|
+
else
|
452
|
+
alert "#{fp} is not text, not paging "
|
453
|
+
#use_on_file "als", fp # only zip or archive
|
454
|
+
end
|
455
|
+
end
|
456
|
+
def get_files
|
457
|
+
#files = Dir.glob("*")
|
458
|
+
files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}M)'`.split("\n")
|
459
|
+
if $patt
|
460
|
+
files = files.grep(/#{$patt}/)
|
461
|
+
end
|
462
|
+
return files
|
463
|
+
end
|
464
|
+
# a quick simple list with highlight row, and scrolling
|
465
|
+
#
|
466
|
+
# mark directories in color
|
467
|
+
# @return start row
|
468
|
+
def listing win, path, files, cur=0, pstart
|
469
|
+
curpos = 1
|
470
|
+
width = win.width-1
|
471
|
+
y = x = 1
|
472
|
+
ht = win.height-2
|
473
|
+
#st = 0
|
474
|
+
st = pstart # previous start
|
475
|
+
pend = pstart + ht -1 # previous end
|
476
|
+
if cur > pend
|
477
|
+
st = (cur -ht) +1
|
478
|
+
elsif cur < pstart
|
479
|
+
st = cur
|
480
|
+
end
|
481
|
+
hl = cur
|
482
|
+
#if cur >= ht
|
483
|
+
#st = (cur - ht ) +1
|
484
|
+
#hl = cur
|
485
|
+
## we need to scroll the rows
|
486
|
+
#end
|
487
|
+
y = 0
|
488
|
+
ctr = 0
|
489
|
+
filler = " "*width
|
490
|
+
files.each_with_index {|f, y|
|
491
|
+
next if y < st
|
492
|
+
colr = CP_WHITE # white on bg -1
|
493
|
+
ctr += 1
|
494
|
+
mark = " "
|
495
|
+
if y == hl
|
496
|
+
attr = FFI::NCurses::A_REVERSE
|
497
|
+
mark = ">"
|
498
|
+
curpos = ctr
|
499
|
+
else
|
500
|
+
attr = FFI::NCurses::A_NORMAL
|
501
|
+
end
|
502
|
+
fullp = path + "/" + f
|
503
|
+
|
504
|
+
if $long_listing
|
505
|
+
begin
|
506
|
+
unless File.exist? f
|
507
|
+
last = f[-1]
|
508
|
+
if last == " " || last == "@" || last == '*'
|
509
|
+
stat = File.stat(f.chop)
|
510
|
+
end
|
511
|
+
else
|
512
|
+
stat = File.stat(f)
|
513
|
+
end
|
514
|
+
f = "%10s %s %s" % [readable_file_size(stat.size,1), date_format(stat.mtime), f]
|
515
|
+
rescue Exception => e
|
516
|
+
f = "%10s %s %s" % ["?", "??????????", f]
|
517
|
+
end
|
518
|
+
end
|
519
|
+
if File.directory? fullp
|
520
|
+
#ff = "#{mark} #{f}/"
|
521
|
+
# 2018-03-12 - removed slash at end since zsh puts it there
|
522
|
+
ff = "#{mark} #{f}"
|
523
|
+
colr = CP_BLUE # blue on background color_pair COLOR_PAIR
|
524
|
+
attr = attr | FFI::NCurses::A_BOLD
|
525
|
+
elsif File.executable? fullp
|
526
|
+
ff = "#{mark} #{f}*"
|
527
|
+
colr = CP_WHITE # yellow on background color_pair COLOR_PAIR
|
528
|
+
attr = attr | FFI::NCurses::A_BOLD
|
529
|
+
else
|
530
|
+
ff = "#{mark} #{f}"
|
531
|
+
end
|
532
|
+
win.printstring(ctr, x, filler, colr )
|
533
|
+
win.printstring(ctr, x, ff, colr, attr)
|
534
|
+
break if ctr >= ht
|
535
|
+
}
|
536
|
+
#curpos = cur + 1
|
537
|
+
#if curpos > ht
|
538
|
+
#curpos = ht
|
539
|
+
#end
|
540
|
+
#statusline(win, "#{cur+1}/#{files.size} #{files[cur]}. cur = #{cur}, pos:#{curpos},ht = #{ht} , hl #{hl}")
|
541
|
+
statusline(win, "#{cur+1}/#{files.size} #{files[cur]}. (#{$sorto}) ")
|
542
|
+
FFI::NCurses.wmove(win.pointer, curpos , 0) # +1 depends on offset of ctr
|
543
|
+
win.wrefresh
|
544
|
+
return st
|
545
|
+
end
|
546
|
+
def statusline win, str
|
547
|
+
win.printstring(win.height-1, 2, str, 1) # white on default
|
548
|
+
end
|
549
|
+
def topline win, str, column = 0
|
550
|
+
# LINES-2 prints on second last line so that box can be seen
|
551
|
+
win.printstring( 0, 0, " "*(win.width), 6, REVERSE)
|
552
|
+
str.split("|").each_with_index {|s,ix|
|
553
|
+
_color = 5
|
554
|
+
_color = 6 if ix%2==0
|
555
|
+
win.printstring( 0,column, s, _color, REVERSE)
|
556
|
+
column += s.length+1
|
557
|
+
}
|
558
|
+
end
|
559
|
+
|
560
|
+
def alert str
|
561
|
+
win = create_footer_window
|
562
|
+
# 10 is too much BLACK on CYAN
|
563
|
+
FFI::NCurses.wbkgd(win.pointer, FFI::NCurses.COLOR_PAIR(12))
|
564
|
+
win.printstring(0,1, str)
|
565
|
+
win.wrefresh
|
566
|
+
win.getkey
|
567
|
+
win.destroy
|
568
|
+
end
|
569
|
+
def main_menu
|
570
|
+
h = { :s => :sort_menu, :M => :newdir, "%" => :newfile }
|
571
|
+
m = Menu.new "Main Menu", h
|
572
|
+
ch = m.getkey
|
573
|
+
return nil if !ch
|
574
|
+
|
575
|
+
binding = h[ch]
|
576
|
+
binding = h[ch.to_sym] unless binding
|
577
|
+
if binding
|
578
|
+
if respond_to?(binding, true)
|
579
|
+
send(binding)
|
580
|
+
end
|
581
|
+
end
|
582
|
+
return ch, binding
|
583
|
+
end
|
584
|
+
|
585
|
+
def sort_menu
|
586
|
+
lo = nil
|
587
|
+
h = { :n => :newest, :a => :accessed, :o => :oldest,
|
588
|
+
:l => :largest, :s => :smallest , :m => :name , :r => :rname, :d => :dirs, :c => :clear }
|
589
|
+
m = Menu.new "Sort Menu", h
|
590
|
+
ch = m.getkey
|
591
|
+
return nil if !ch
|
592
|
+
menu_text = h[ch.to_sym]
|
593
|
+
case menu_text
|
594
|
+
when :newest
|
595
|
+
lo="om"
|
596
|
+
when :accessed
|
597
|
+
lo="oa"
|
598
|
+
when :oldest
|
599
|
+
lo="Om"
|
600
|
+
when :largest
|
601
|
+
lo="OL"
|
602
|
+
when :smallest
|
603
|
+
lo="oL"
|
604
|
+
when :name
|
605
|
+
lo="on"
|
606
|
+
when :rname
|
607
|
+
lo="On"
|
608
|
+
when :dirs
|
609
|
+
lo="/"
|
610
|
+
when :clear
|
611
|
+
lo=""
|
612
|
+
end
|
613
|
+
## This needs to persist and be a part of all listings, put in change_dir.
|
614
|
+
$sorto = lo
|
615
|
+
#$files = `zsh -c 'print -rl -- *(#{lo}#{$hidden}M)'`.split("\n") if lo
|
616
|
+
end
|
617
|
+
|
618
|
+
begin
|
619
|
+
init_curses
|
620
|
+
win = Window.new
|
621
|
+
pointer = win.pointer
|
622
|
+
$ht = win.height
|
623
|
+
$wid = win.width
|
624
|
+
$pagecols = $ht / 2
|
625
|
+
$spacecols = $ht
|
626
|
+
win.printstr("Press Ctrl-Q to quit #{win.height}:#{win.width}", win.height-1, 20)
|
627
|
+
|
628
|
+
path = File.expand_path("./")
|
629
|
+
#win.printstring(0,0, "PATH: #{path} #{TOPLINE}",0)
|
630
|
+
topline(win, "PATH: #{path} #{TOPLINE}",0)
|
631
|
+
files = get_files
|
632
|
+
current = 0
|
633
|
+
prevstart = listing(win, path, files, current, 0)
|
634
|
+
|
635
|
+
ch = 0
|
636
|
+
xx = 1
|
637
|
+
yy = 1
|
638
|
+
y = x = 1
|
639
|
+
while (ch = win.getkey) != 113
|
640
|
+
#y, x = win.getbegyx(pointer)
|
641
|
+
old_y, old_x = y, x
|
642
|
+
case ch
|
643
|
+
when FFI::NCurses::KEY_RIGHT
|
644
|
+
# if directory then open it
|
645
|
+
fullp = path + "/" + files[current]
|
646
|
+
if File.directory? fullp
|
647
|
+
Dir.chdir(files[current])
|
648
|
+
$patt = nil
|
649
|
+
path = Dir.pwd
|
650
|
+
#win.printstring(0,0, "PATH: #{path} ",0)
|
651
|
+
#win.printstring(0,0, "PATH: #{path} #{TOPLINE}",0)
|
652
|
+
topline(win, "PATH: #{path} #{TOPLINE}",0)
|
653
|
+
files = get_files
|
654
|
+
current = 0
|
655
|
+
FFI::NCurses.wclrtobot(pointer)
|
656
|
+
#win.wclrtobot
|
657
|
+
elsif File.readable? fullp
|
658
|
+
file_page win, fullp
|
659
|
+
win.wrefresh
|
660
|
+
# open file
|
661
|
+
end
|
662
|
+
x += 1
|
663
|
+
when FFI::NCurses::KEY_LEFT
|
664
|
+
# go back higher level
|
665
|
+
oldpath = path
|
666
|
+
Dir.chdir("..")
|
667
|
+
path = Dir.pwd
|
668
|
+
$patt = nil
|
669
|
+
#win.printstring(0,0, "PATH: #{path} #{TOPLINE}",0)
|
670
|
+
topline(win, "PATH: #{path} #{TOPLINE}",0)
|
671
|
+
files = get_files
|
672
|
+
# when going up, keep focus on the dir we came from
|
673
|
+
current = files.index(File.basename(oldpath) + "/")
|
674
|
+
current = 0 if current.nil? or current == -1
|
675
|
+
#win.wclrtobot
|
676
|
+
FFI::NCurses.wclrtobot(pointer)
|
677
|
+
x -= 1
|
678
|
+
when FFI::NCurses::KEY_RETURN
|
679
|
+
# if directory then open it
|
680
|
+
fullp = path + "/" + files[current]
|
681
|
+
if File.directory? fullp
|
682
|
+
Dir.chdir(files[current])
|
683
|
+
$patt = nil
|
684
|
+
path = Dir.pwd
|
685
|
+
#win.printstring(0,0, "PATH: #{path} ",0)
|
686
|
+
#win.printstring(0,0, "PATH: #{path} #{TOPLINE}",0)
|
687
|
+
topline(win, "PATH: #{path} #{TOPLINE}",0)
|
688
|
+
files = get_files
|
689
|
+
#files = Dir.entries("./")
|
690
|
+
#files.delete(".")
|
691
|
+
#files.delete("..")
|
692
|
+
current = 0
|
693
|
+
FFI::NCurses.wclrtobot(pointer)
|
694
|
+
#win.wclrtobot
|
695
|
+
elsif File.readable? fullp
|
696
|
+
# open file
|
697
|
+
file_open win, fullp
|
698
|
+
win.wrefresh
|
699
|
+
end
|
700
|
+
when FFI::NCurses::KEY_UP, ?k.getbyte(0)
|
701
|
+
current -=1
|
702
|
+
when FFI::NCurses::KEY_DOWN, ?j.getbyte(0)
|
703
|
+
current +=1
|
704
|
+
when FFI::NCurses::KEY_CTRL_N
|
705
|
+
current += $pagecols
|
706
|
+
when FFI::NCurses::KEY_CTRL_P
|
707
|
+
current -= $pagecols
|
708
|
+
when 32, FFI::NCurses::KEY_CTRL_D
|
709
|
+
current += $spacecols
|
710
|
+
when ?g.getbyte(0)
|
711
|
+
current = 0
|
712
|
+
when ?G.getbyte(0)
|
713
|
+
current = files.size-1
|
714
|
+
when FFI::NCurses::KEY_BACKSPACE, FFI::NCurses::KEY_CTRL_B, 127
|
715
|
+
current -= $spacecols
|
716
|
+
when FFI::NCurses::KEY_CTRL_X
|
717
|
+
when ?=.getbyte(0)
|
718
|
+
#list = ["x this", "y that","z other","a foo", "b bar"]
|
719
|
+
list = { "h" => "hidden files toggle", "l" => "long listing toggle", "z" => "the other", "a" => "another one", "b" => "yet another" }
|
720
|
+
m = Menu.new "Toggle Options", list
|
721
|
+
key = m.getkey
|
722
|
+
win.wrefresh # otherwise menu popup remains till next key press.
|
723
|
+
case key
|
724
|
+
when 'h'
|
725
|
+
$hidden = $hidden ? nil : "D"
|
726
|
+
files = get_files
|
727
|
+
clearwin(pointer)
|
728
|
+
when 'l'
|
729
|
+
$long_listing = !$long_listing
|
730
|
+
clearwin(pointer)
|
731
|
+
end
|
732
|
+
when ?/.getbyte(0)
|
733
|
+
# search grep
|
734
|
+
# this is writing over the last line of the listing
|
735
|
+
ewin = create_input_window
|
736
|
+
ewin.printstr("/", 0, 0)
|
737
|
+
#win.wmove(1, _LINES-1)
|
738
|
+
str = getchars(ewin, 10)
|
739
|
+
ewin.destroy
|
740
|
+
#alert "Got #{str}"
|
741
|
+
$patt = str #if str
|
742
|
+
files = get_files
|
743
|
+
clearwin(pointer)
|
744
|
+
when ?`.getbyte(0)
|
745
|
+
main_menu
|
746
|
+
files = get_files
|
747
|
+
clearwin(pointer)
|
748
|
+
else
|
749
|
+
alert("key #{ch} not known")
|
750
|
+
end
|
751
|
+
#win.printstr("Pressed #{ch} on #{files[current]} ", 0, 70)
|
752
|
+
current = 0 if current < 0
|
753
|
+
current = files.size-1 if current >= files.size
|
754
|
+
# listing does not refresh files, so if files has changed, you need to refresh
|
755
|
+
prevstart = listing(win, path, files, current, prevstart)
|
756
|
+
win.wrefresh
|
757
|
+
end
|
758
|
+
|
759
|
+
rescue Object => e
|
760
|
+
@window.destroy if @window
|
761
|
+
FFI::NCurses.endwin
|
762
|
+
puts e
|
763
|
+
puts e.backtrace.join("\n")
|
764
|
+
ensure
|
765
|
+
@window.destroy if @window
|
766
|
+
FFI::NCurses.endwin
|
767
|
+
puts
|
768
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ncurses/lyra/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ncurses-lyra"
|
8
|
+
spec.version = Ncurses::Lyra::VERSION
|
9
|
+
spec.authors = ["kepler"]
|
10
|
+
spec.email = ["githubkepler.50s@gishpuppy.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{a small fast minimal file lister and explorer}
|
13
|
+
spec.description = %q{a light fast directory explorer in ncurses}
|
14
|
+
spec.homepage = "https://github.com/mare-imbrium/ncurses-lyra"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_runtime_dependency "ffi-ncurses", ">= 0.4.0", ">= 0.4.0"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ncurses-lyra
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- kepler
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ffi-ncurses
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.4.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.4.0
|
55
|
+
description: a light fast directory explorer in ncurses
|
56
|
+
email:
|
57
|
+
- githubkepler.50s@gishpuppy.com
|
58
|
+
executables:
|
59
|
+
- lyra.rb
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- exe/lyra.rb
|
70
|
+
- lib/ncurses/lyra/version.rb
|
71
|
+
- ncurses-lyra.gemspec
|
72
|
+
homepage: https://github.com/mare-imbrium/ncurses-lyra
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.7.6
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: a small fast minimal file lister and explorer
|
96
|
+
test_files: []
|