rigel 0.0.2
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 +23 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +65 -0
- data/Rakefile +2 -0
- data/bin/rigel +493 -0
- data/lib/rigel.rb +587 -0
- data/lib/rigel/version.rb +3 -0
- data/rigel.gemspec +24 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a51ebc53ff6ebba1121a65701886f410b87af1d8
|
4
|
+
data.tar.gz: 6ed01de3dfffdecb5b569fdf9d1a6408603b4162
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: abf87334becd9461edc9e17df9d2f34db8659cf4949e9d06cf4fe2ced96135a6989d9598a986f7f33bea9b5e9f7b37d717784c0319bf82883ec2ddfc33423913
|
7
|
+
data.tar.gz: 76b39374517db0247a1f805b305e95e62e17e1438f5be858e506f46858f01bc48c9cb92c69a8705f14cc50a3a92b1a71091f5bf1eea15e43ee4090fcafeef9eb
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
*.log
|
23
|
+
*.old
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 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/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Rigel
|
2
|
+
|
3
|
+
ri browser using ncurses
|
4
|
+
(based on `ribhu` which uses rbcurse gem)
|
5
|
+
|
6
|
+
Hopes to make your ruby documentation experience faster. This is a two pane application, with
|
7
|
+
Classes on the left list, and details on the right.
|
8
|
+
You may press Enter on a class to see its documention. Pressing ENTER over a method in the right will
|
9
|
+
fetch its details.
|
10
|
+
|
11
|
+
M-x gives a Ctrl-P like list displaying classes, and one can filter from them and select just as one does
|
12
|
+
with Ctrl-P. Pressing "/" in the classes list, also lets you type in a string to search from.
|
13
|
+
|
14
|
+
You may mark classes with an upper case alphabet (vim style by pressing 'm' in the left list) and access them directly using single-quote.
|
15
|
+
Several classes have been bookmarked such as Array, String, Hash, File. You may place more in a file named "~/.rigel.conf" in the form:
|
16
|
+
|
17
|
+
$bookmarks[:Z] = "Zlib"
|
18
|
+
|
19
|
+
A list of visited classes is also maintained and can be accessed and selected from. You may prepopulate it
|
20
|
+
from the conf file as:
|
21
|
+
|
22
|
+
$visited.concat %w{Abbrev GC ARGF}
|
23
|
+
|
24
|
+
Pressing Alt-c ('ask-class') and type in any class or method or portion. If `ri` does not return data or returns
|
25
|
+
choices, a popup will allows selection of choices. If you have used the Alt-c already, pressing Alt-h inside the ask-class popup will show history of previous searches.
|
26
|
+
|
27
|
+
In the right window, pressing Alt-m will allow selection of methods from a popup. Space and ENTER select, use j/k/gg/G for navigation, or 'f' following by a char to go to the first or next method starting with that char. e.g. pressing 'fm' jumps to 'match' for String.
|
28
|
+
|
29
|
+
Search through the current window using "/". You may use "Alt-h" to access history (previous searches).
|
30
|
+
|
31
|
+
Browser style, one may backspace through earlier results, or use Alt-n and Alt-p to go back and forth
|
32
|
+
between previous and next pages viewed.
|
33
|
+
|
34
|
+
Please get back to me if there are cases where it's unhelpful in finding the ridocs.
|
35
|
+
|
36
|
+
|
37
|
+
## Installation
|
38
|
+
|
39
|
+
gem install rigel
|
40
|
+
|
41
|
+
## Usage
|
42
|
+
|
43
|
+
Ensure you have ri documentation working. On the command line you may do, "ri String". You should get documentation for the String class. If not proceed as follows:
|
44
|
+
|
45
|
+
To get ri documentation, you would do
|
46
|
+
|
47
|
+
rvm docs generate-ri
|
48
|
+
|
49
|
+
If you use any gems for development, e.g. highline or rbcurse-core, use the `--ri` flag while installing the gem (this assumes you've switched off ri and rdocs in your .gemrc).
|
50
|
+
|
51
|
+
This gem depends on rbcurse-core
|
52
|
+
|
53
|
+
## Gem name
|
54
|
+
|
55
|
+
The first two letters are for "ri", the name is that of a star.
|
56
|
+
|
57
|
+
https://rubygems.org/gems/rigel
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
1. Fork it
|
62
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
63
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
64
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
65
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/rigel
ADDED
@@ -0,0 +1,493 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# NOTE: If the listbox is empty, that could mean that you have not generated
|
4
|
+
# ri documentation for this version of ruby. You can do so by doing:
|
5
|
+
# rvm docs generate-ri
|
6
|
+
# or
|
7
|
+
# rvm docs generate
|
8
|
+
# (This assumes you are using rvm)
|
9
|
+
#
|
10
|
+
# WARNING : IF THIS PROGRAM HANGS check the ri command
|
11
|
+
# Maybe your version of ri has different options and is going interactive.
|
12
|
+
# ruby 1.9.3's ri requires a -l option or else if becomes interactive.
|
13
|
+
# this program tests out a listbox
|
14
|
+
# This is written in the old style where we start and end ncurses and initiate a
|
15
|
+
# getch loop. It gives more control.
|
16
|
+
# The new style is to use App which does the ncurses setup and teardown, as well
|
17
|
+
# as manages keys. It also takes care of logger and includes major stuff.
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# TODO:
|
21
|
+
# when pressing enter on left list move focus to right
|
22
|
+
require 'logger'
|
23
|
+
require 'canis'
|
24
|
+
require 'canis/core/widgets/listbox'
|
25
|
+
#require 'canis/core/widgets/rtextview'
|
26
|
+
#require 'canis/core/include/vieditable'
|
27
|
+
require 'shellwords'
|
28
|
+
require 'canis/core/widgets/textpad'
|
29
|
+
require 'canis/core/util/widgetshortcuts'
|
30
|
+
require 'canis/core/util/rcommandwindow'
|
31
|
+
require 'rigel'
|
32
|
+
|
33
|
+
|
34
|
+
def my_help_text
|
35
|
+
<<-eos
|
36
|
+
|
37
|
+
=========================================================================
|
38
|
+
Basic Usage
|
39
|
+
|
40
|
+
Press <ENTER> on a class name on the first list, to view ri information
|
41
|
+
for it on the right.
|
42
|
+
|
43
|
+
Tab to right area, and press <ENTER> on a method name, to see its details
|
44
|
+
Press / <slash> in any box to search. e.g /String will take you to the
|
45
|
+
first occurrence of String. <n> will take you to next.
|
46
|
+
|
47
|
+
To go quickly to first class starting with 'S', type <f> followed by <S>.
|
48
|
+
Then press <n> to go to next match.
|
49
|
+
|
50
|
+
On the right window, you may press BACKSPACE to go back to earlier pages.
|
51
|
+
You may also press Alt-n and Alt-p to cycle through next and previously
|
52
|
+
viewed pages.
|
53
|
+
|
54
|
+
Press Alt-c to enter class or method name and see details. If there is
|
55
|
+
no data returned, or options returned you may have to select from a list.
|
56
|
+
e.g. entering 'flatten' may return a list of choices to select from.
|
57
|
+
Entering 'Str' returns nothing from ri, so you have to select from classes
|
58
|
+
starting with 'Str'.
|
59
|
+
|
60
|
+
Press Alt-d to get a popup of visited classes, from which you may select.
|
61
|
+
|
62
|
+
Press Alt-b to get a popup of bookmarks, from which you may select.
|
63
|
+
|
64
|
+
Press Alt-m to get a popup of methods for current class, from which you may select.
|
65
|
+
|
66
|
+
'?' displays all key binding for current object. 'Q' and 'C-q' quit.
|
67
|
+
In any popups, try 'C-q' to quit.
|
68
|
+
|
69
|
+
=========================================================================
|
70
|
+
Bookmarks
|
71
|
+
|
72
|
+
Access bookmarks using single-quote as in vim followed by a single character.
|
73
|
+
Some have been set such as Array, String, Hash, File. To create a bookmark,
|
74
|
+
position cursor over a class in the left list, and press "m". You will be
|
75
|
+
prompted for the character to use as the mark.
|
76
|
+
|
77
|
+
=========================================================================
|
78
|
+
Buffers
|
79
|
+
|
80
|
+
Ordinary a textview contains only one buffer. However, the one on the right
|
81
|
+
is extended for multiple buffers. Pressing ENTER on the left on several
|
82
|
+
rows opens multiple buffers on the right. Use M-n (Alt-N) and M-p to navigate.
|
83
|
+
ALternatively, : maps to a menu, so :n and :p may also be used.
|
84
|
+
<BACKSPACE> will also go to previous buffer, like a browser.
|
85
|
+
|
86
|
+
=========================================================================
|
87
|
+
Press <M-n> for next help screen, or try :n
|
88
|
+
|
89
|
+
eos
|
90
|
+
end
|
91
|
+
|
92
|
+
# seems unused now, but may be useful elsewhere
|
93
|
+
def convert_man_to_ansi file
|
94
|
+
lines = file.split "\n"
|
95
|
+
l = nil
|
96
|
+
lines.each_with_index do |line, ix|
|
97
|
+
# convert underlined words to yellow or one color, these are usually params
|
98
|
+
line.gsub!(/((_[^ ])+)/,'[4;33m\1[0m')
|
99
|
+
line.gsub!(/_/,'')
|
100
|
+
# convert bold words to red or one color, these are usually headers and other words
|
101
|
+
l= line.gsub(/(([^ ][^ ])+)/,'[1;31m\1[0m').gsub(/[^ ]/,'').gsub(//,'')
|
102
|
+
lines[ix] = l
|
103
|
+
end
|
104
|
+
lines
|
105
|
+
end
|
106
|
+
##
|
107
|
+
# display the ridoc for given word (class or method or part)
|
108
|
+
def display_text word
|
109
|
+
raise "word should be string" unless word.is_a? String
|
110
|
+
w = @form.by_name["tv"];
|
111
|
+
lines = get_data word
|
112
|
+
#file = `ri -f bs #{word}`
|
113
|
+
#lines = convert_man_to_ansi file
|
114
|
+
return if lines.nil? || lines.size == 0
|
115
|
+
# ansi can have overflow
|
116
|
+
w.add_content(lines, :content_type => :ansi, :title => word)
|
117
|
+
|
118
|
+
# Since i am trying without border, so need to put class on top right
|
119
|
+
header = @form.by_name["header"];
|
120
|
+
header.text_right(word)
|
121
|
+
|
122
|
+
$visited << word unless $visited.index(word)
|
123
|
+
#w.add_content(lines, :title => word)
|
124
|
+
w.buffer_last
|
125
|
+
end
|
126
|
+
## retrieve data in ansi format.
|
127
|
+
# NOTE that ri returns <t>CLEAR</tt> without the 0. So canis-core does not catch that
|
128
|
+
# it expects a zero there. So i've replaced [[m with [[0m.
|
129
|
+
# @param String class or method name to fetch ri info for
|
130
|
+
# @ returns Array of lines, containing ansi format data
|
131
|
+
def get_data str
|
132
|
+
lines = `ri -f ansi #{str} 2>&1`.gsub('[m','[0m').split("\n")
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# prompt user for a class name and show ri doc for same, can be method too
|
137
|
+
#
|
138
|
+
def ask_classes
|
139
|
+
format="rdoc"
|
140
|
+
str = get_string_with_history("Enter a class name: ")
|
141
|
+
if str && str != ""
|
142
|
+
#lines = `ri -f #{format} #{str}`.split("\n")
|
143
|
+
lines = get_data str
|
144
|
+
if lines.size == 0
|
145
|
+
alert "Nothing came through for #{str}"
|
146
|
+
## Nothing returned, lets see if we can match something from the class list
|
147
|
+
li = @form.by_name["mylist"];
|
148
|
+
values = li.list
|
149
|
+
values = values.grep(/^#{str}/i)
|
150
|
+
if values.size > 0
|
151
|
+
ix = popuplist(values)
|
152
|
+
if ix
|
153
|
+
str = values[ix]
|
154
|
+
#lines = `ri -f #{format} #{str}`.split("\n")
|
155
|
+
lines = get_data str
|
156
|
+
end
|
157
|
+
else
|
158
|
+
alert "Nothing came through for #{str}"
|
159
|
+
$message.value = "Nothing came through for #{str}"
|
160
|
+
end
|
161
|
+
elsif lines.first.index(".#{str} not found")
|
162
|
+
## we are returned something with some choices, lets prompt user with choices
|
163
|
+
lines.shift
|
164
|
+
lines.shift
|
165
|
+
ix = popuplist(lines)
|
166
|
+
if ix
|
167
|
+
str = lines[ix]
|
168
|
+
#lines = `ri -f #{format} #{str}`.split("\n")
|
169
|
+
lines = get_data str
|
170
|
+
end
|
171
|
+
end
|
172
|
+
return if lines.size == 0
|
173
|
+
w = @form.by_name["tv"];
|
174
|
+
w.add_content(lines, :content_type => :ansi, :title => str)
|
175
|
+
#w.add_content(lines, :title => str)
|
176
|
+
w.buffer_last
|
177
|
+
set_focus_on str
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# set focus in list on given string
|
182
|
+
def set_focus_on str
|
183
|
+
listb = @form.by_name["mylist"];
|
184
|
+
ix = listb.list.index(str)
|
185
|
+
if ix
|
186
|
+
#listb.set_focus_on ix
|
187
|
+
listb.goto_line ix
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
## user has pressed bokmark char, now get the mark A-Z
|
192
|
+
def ask_bookmark ch=nil
|
193
|
+
unless ch
|
194
|
+
ch = @window.getchar
|
195
|
+
ch = ch.chr.upcase
|
196
|
+
end
|
197
|
+
str = $bookmarks[ch.to_sym]
|
198
|
+
if str
|
199
|
+
display_text str
|
200
|
+
# set focus to that in the left list
|
201
|
+
set_focus_on str
|
202
|
+
else
|
203
|
+
$message.value = "No bookmark for #{ch}. Use m to create."
|
204
|
+
## No bookmark, jumping to first char
|
205
|
+
listb = @form.by_name["mylist"];
|
206
|
+
listb.set_selection_for_char ch
|
207
|
+
end
|
208
|
+
end
|
209
|
+
def add_bookmark ch, word
|
210
|
+
$bookmarks[ch.upcase.to_sym] = word
|
211
|
+
alert "set mark for #{ch.upcase.to_sym} for #{word}"
|
212
|
+
end
|
213
|
+
|
214
|
+
## try various options till you get something.
|
215
|
+
#
|
216
|
+
def try_ri arr
|
217
|
+
_text = nil
|
218
|
+
arr.each do |w|
|
219
|
+
#_text = `ri -f rdoc #{w} 2>&1`.split("\n")
|
220
|
+
_text = get_data w
|
221
|
+
if _text.first.index("Nothing known about")
|
222
|
+
else
|
223
|
+
break
|
224
|
+
end
|
225
|
+
end
|
226
|
+
_text
|
227
|
+
end
|
228
|
+
def popup_history
|
229
|
+
return if $visited.size == 0
|
230
|
+
ix = popuplist($visited, :title => " History ", :bgcolor => :black, :color => :white, :row => 1, :col => 1)
|
231
|
+
if ix
|
232
|
+
display_text $visited[ix]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
#
|
236
|
+
# History is accessible using Alt-h while in field, press enter on any row in the popup
|
237
|
+
def get_string_with_history prompt
|
238
|
+
$shell_history ||= []
|
239
|
+
cmd = get_string(prompt, :maxlen => 50) do |f|
|
240
|
+
require 'canis/core/include/rhistory'
|
241
|
+
f.extend(FieldHistory)
|
242
|
+
f.history($shell_history)
|
243
|
+
end
|
244
|
+
if cmd && !cmd.empty?
|
245
|
+
$shell_history.push(cmd) unless $shell_history.include? cmd
|
246
|
+
end
|
247
|
+
return cmd
|
248
|
+
end
|
249
|
+
# switches location of 2 windows
|
250
|
+
#
|
251
|
+
def switch_windows
|
252
|
+
tv = @form.by_name["tv"]
|
253
|
+
list = @form.by_name["mylist"]
|
254
|
+
if tv.col > 0
|
255
|
+
tv.col = 0
|
256
|
+
tmp = tv.width
|
257
|
+
list.col = tmp + 1
|
258
|
+
else
|
259
|
+
list.col = 0
|
260
|
+
tv.col = list.width + 1
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
include Canis::WidgetShortcuts
|
266
|
+
|
267
|
+
#if $0 == __FILE__
|
268
|
+
if true
|
269
|
+
include Canis
|
270
|
+
|
271
|
+
begin
|
272
|
+
# Initialize curses
|
273
|
+
Canis::start_ncurses # this is initializing colors via ColorMap.setup
|
274
|
+
|
275
|
+
$log = create_logger "rigel.log"
|
276
|
+
$visited = []
|
277
|
+
$bookmarks = {
|
278
|
+
:A => "Array",
|
279
|
+
:C => "Class",
|
280
|
+
:D => "Dir",
|
281
|
+
:F => "File",
|
282
|
+
:H => "Hash",
|
283
|
+
:I => "IO",
|
284
|
+
:K => "Kernel",
|
285
|
+
:M => "Module",
|
286
|
+
:O => "OptionParser",
|
287
|
+
:P => "Proc",
|
288
|
+
:S => "String"
|
289
|
+
}
|
290
|
+
config = File.expand_path("~/.rigel.conf")
|
291
|
+
# read up any bookmarks or visited classes, they get added to the existing data
|
292
|
+
if File.exists? config
|
293
|
+
load config
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
@window = Canis::Window.root_window
|
298
|
+
colors = Ncurses.COLORS
|
299
|
+
back = :black
|
300
|
+
lineback = :blue
|
301
|
+
back = 234 if colors >= 256
|
302
|
+
lineback = 236 if colors >= 256
|
303
|
+
$window = @window # i need this for get_char, YUCK
|
304
|
+
$catch_alt_digits = true; # emacs like alt-1..9 numeric arguments
|
305
|
+
#install_help_text my_help_text
|
306
|
+
# Initialize few color pairs
|
307
|
+
# Create the window to be associated with the form
|
308
|
+
# Un post form and free the memory
|
309
|
+
|
310
|
+
catch(:close) do
|
311
|
+
@form = Form.new @window
|
312
|
+
@form.help_manager.help_text = my_help_text
|
313
|
+
#@form.bind_key(KEY_F1, 'help'){ display_app_help }
|
314
|
+
@form.bind_key(?\M-c, 'select class') do
|
315
|
+
ask_classes
|
316
|
+
end
|
317
|
+
@form.bind_key(FFI::NCurses::KEY_F5, 'switch_windows'){ switch_windows }
|
318
|
+
@form.bind_key(?', 'select bookmark') do
|
319
|
+
ask_bookmark
|
320
|
+
end
|
321
|
+
# Alt-h is taken by scrolling in list and textareas
|
322
|
+
@form.bind_key(?\M-d, 'View history') do
|
323
|
+
popup_history
|
324
|
+
end
|
325
|
+
@form.bind_key(?\M-b, 'View bookmarks') do
|
326
|
+
# Display bookmarks and allow user to select one
|
327
|
+
list = []
|
328
|
+
$bookmarks.each_pair { |k, v| list << " #[fg=yellow, bold] #{k} #[/end] #[fg=cyan] #{v} #[/end]" }
|
329
|
+
ch = padpopup list, :title => "View Bookmarks", :row => 1, :col => 1
|
330
|
+
ask_bookmark ch.upcase
|
331
|
+
end
|
332
|
+
|
333
|
+
widget_shortcuts_init
|
334
|
+
header = app_header "0.0.1", :text_center => "ri Documentation Browser", :text_right =>"rigel" , :name => "header" , :color => :white, :bgcolor => lineback , :attr => :bold
|
335
|
+
|
336
|
+
|
337
|
+
|
338
|
+
#"#[bg=236, fg=black]#[fg=yellow, bold]F1#[/end] Help | #[fg=yellow, bold]?#[/end] Keys | #[fg=yellow, bold]M-c#[/end] Ask | #[fg=yellow, bold]M-d#[/end] History | #[fg=yellow, bold]M-m#[/end] Methods | #[fg=yellow, bold]M-b#[/end] Bookmarks | Q Quit | %20s" % [$message.value]
|
339
|
+
_col = "#[fg=yellow]"
|
340
|
+
$message = Variable.new
|
341
|
+
$message.value = ""
|
342
|
+
@status_line = status_line :row => Ncurses.LINES-1 #, :bgcolor => :red, :color => :yellow
|
343
|
+
@status_line.command {
|
344
|
+
"#[bg=236, fg=black]#{_col}F1#[/end] Help | #{_col}?#[/end] Keys | #{_col}M-x#[/end] Classes | #{_col}M-d#[/end] History | #{_col}M-m#[/end] Methods | #{_col}M-b#[/end] Bookmarks | #{_col}M-c#[/end] Ask | Q Quit | %20s" % [$message.value]
|
345
|
+
}
|
346
|
+
|
347
|
+
h = FFI::NCurses.LINES-2
|
348
|
+
mylist = `ri -l `.split("\n")
|
349
|
+
$classnames = mylist
|
350
|
+
w = 25
|
351
|
+
r = 1
|
352
|
+
|
353
|
+
listb = Listbox.new @form, :name => "mylist" ,
|
354
|
+
:row => r ,
|
355
|
+
:col => 0 ,
|
356
|
+
:width => w,
|
357
|
+
:height => h,
|
358
|
+
:list => mylist,
|
359
|
+
:selection_mode => :SINGLE,
|
360
|
+
:show_selector => true,
|
361
|
+
:suppress_borders => true,
|
362
|
+
:bgcolor => back,
|
363
|
+
:color => :white,
|
364
|
+
:title => " Ruby Classes "
|
365
|
+
#title_attrib 'reverse'
|
366
|
+
#listb.vieditable_init_listbox
|
367
|
+
# what for is Io here
|
368
|
+
#include Io
|
369
|
+
#listb.bind_key(32) {|l| l.scroll_forward };
|
370
|
+
listb.bind(:PRESS) {
|
371
|
+
## select class and display riinfo for class
|
372
|
+
#display_text listb.text
|
373
|
+
display_text listb.current_value
|
374
|
+
}
|
375
|
+
listb.bind_key(?m) {
|
376
|
+
#str = listb.text
|
377
|
+
str = listb.current_value
|
378
|
+
ch = get_string("Enter character as shortcut for #{str}")
|
379
|
+
if ch && ch != ""
|
380
|
+
add_bookmark ch[0], str
|
381
|
+
end
|
382
|
+
## select class and display riinfo for class
|
383
|
+
}
|
384
|
+
# since keys are added at handle_key, really late, therefore unbind is of no use.
|
385
|
+
listb.bind_key(?', 'select bookmark') do
|
386
|
+
ask_bookmark
|
387
|
+
end
|
388
|
+
|
389
|
+
tv = Canis::TextPad.new @form, :row => r, :col => w+0, :height => h, :width => FFI::NCurses.COLS-w-0,
|
390
|
+
:name => "tv", :title => "Press Enter on Class", :suppress_borders => true
|
391
|
+
tv.set_content ["Press Enter on list to view ri information in this area.",
|
392
|
+
"Press ENTER on method name to see details"]
|
393
|
+
require 'canis/core/include/multibuffer'
|
394
|
+
tv.extend(Canis::MultiBuffers)
|
395
|
+
#tv.unbind_key([?', ?'])
|
396
|
+
#tv.unbind_key(?')
|
397
|
+
tv.bind_key(?', 'select bookmark') do
|
398
|
+
ask_bookmark
|
399
|
+
end
|
400
|
+
|
401
|
+
# pressing ENTER on a method name will popup details for that method
|
402
|
+
tv.bind(:PRESS) { |ev|
|
403
|
+
w = ev.word_under_cursor.strip
|
404
|
+
# check that user did not hit enter on empty area
|
405
|
+
if w != ""
|
406
|
+
str = "#{tv.title}.#{w}"
|
407
|
+
_text = try_ri(["#{str}", w])
|
408
|
+
tt = tv.title
|
409
|
+
if _text.first.index("Nothing known about")
|
410
|
+
|
411
|
+
if tt.index("::")
|
412
|
+
ix = tt.index("::")
|
413
|
+
tt = tt[0,ix]
|
414
|
+
|
415
|
+
_text = get_data "#{tt}::#{w}"
|
416
|
+
end
|
417
|
+
end
|
418
|
+
if _text && _text.size != 0
|
419
|
+
view(_text, :content_type => :ansi)
|
420
|
+
#view(_text)
|
421
|
+
end
|
422
|
+
end
|
423
|
+
}
|
424
|
+
@form.bind_key(?\M-m, "Select methods") {
|
425
|
+
tv = @form.by_name["tv"];
|
426
|
+
kl = tv.title.strip
|
427
|
+
if kl.index "."
|
428
|
+
kl = kl.split(".")[0]
|
429
|
+
end
|
430
|
+
|
431
|
+
# 2013-03-21 - 19:50 since the display may be of a selected method
|
432
|
+
# so we requery the methods each time. Can we go to old position somehow ?
|
433
|
+
lines = `ri -f rdoc #{kl} 2>&1`.split("\n")
|
434
|
+
# 2013-03-21 - 19:09 Fixed, had stopped working after change to ansi format
|
435
|
+
#ix = lines.grep(/Instance methods:/)
|
436
|
+
ix = lines.index("= Instance methods:")
|
437
|
+
unless ix
|
438
|
+
alert "No instance methods found for #{kl}"
|
439
|
+
end
|
440
|
+
if ix
|
441
|
+
values = lines[ix+2..-1]
|
442
|
+
values = values.collect { |x| x.strip }
|
443
|
+
#ix = popuplist(values)
|
444
|
+
#ix = popuplist(values, :title => " Methods ", :bgcolor => :blue, :color => :white)
|
445
|
+
# the problem is when the list is longer than indexing values
|
446
|
+
# since we are not doing pagin in this small popup.
|
447
|
+
#
|
448
|
+
# maybe whenever we scroll or page, redo the numbering from a, and keep sta
|
449
|
+
#meth = Rigel::indexed_list("Methods", values)
|
450
|
+
meth = Rigel::ri_full_indexed_list(values, :title => "Methods")
|
451
|
+
if meth
|
452
|
+
meth = meth.strip
|
453
|
+
# 2013-03-23 - 00:33 shell escape required for methods like << & [] etc else error
|
454
|
+
# from shell which messes screen.
|
455
|
+
meth = Shellwords.escape(meth)
|
456
|
+
display_text "#{kl}.#{meth}"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
}
|
460
|
+
@form.bind_key(?\M-x, "Select classes"){
|
461
|
+
# TODO : this is case sensitive and uses contiguous, we need fuzzy
|
462
|
+
command = Proc.new {|str| $classnames.select do |line| line =~ /#{str}/i ; end }
|
463
|
+
# fuzzy but can be quite a pain since i am not prioritizing in any way
|
464
|
+
#command = Proc.new {|str| patt = str.split("").join(".*"); $classnames.select do |line| line =~ /#{patt}/i ; end }
|
465
|
+
str = display_list $classnames, :title => "Select a class", :command => command
|
466
|
+
if str
|
467
|
+
display_text str
|
468
|
+
set_focus_on str
|
469
|
+
end
|
470
|
+
}
|
471
|
+
|
472
|
+
@form.repaint
|
473
|
+
@window.wrefresh
|
474
|
+
Ncurses::Panel.update_panels
|
475
|
+
while((ch = @window.getchar()) != KEY_F10 )
|
476
|
+
break if ch == ?Q.ord || ch == ?\C-q.getbyte(0)
|
477
|
+
@form.handle_key(ch)
|
478
|
+
@window.wrefresh
|
479
|
+
end
|
480
|
+
end
|
481
|
+
rescue => ex
|
482
|
+
textdialog ["Error in rigel: #{ex} ", *ex.backtrace], :title => "Exception"
|
483
|
+
$log.debug( ex) if ex
|
484
|
+
$log.debug(ex.backtrace.join("\n")) if ex
|
485
|
+
ensure
|
486
|
+
@window.destroy if !@window.nil?
|
487
|
+
Canis::stop_ncurses
|
488
|
+
p ex if ex
|
489
|
+
p(ex.backtrace.join("\n")) if ex
|
490
|
+
end
|
491
|
+
else
|
492
|
+
puts "Error #{$0}, #{__FILE__}"
|
493
|
+
end
|
data/lib/rigel.rb
ADDED
@@ -0,0 +1,587 @@
|
|
1
|
+
#require "rigel/version"
|
2
|
+
|
3
|
+
module Rigel
|
4
|
+
extend self
|
5
|
+
# i had put this inside module but was unable to access it from bin/rigel
|
6
|
+
## COPIED FROM CYGNUS
|
7
|
+
#
|
8
|
+
# pad version of original popuplist
|
9
|
+
# Not used in cygnus, so will require testing if used from elsewhere
|
10
|
+
# Is meant to replace the original popuplist soon since the original list
|
11
|
+
# and other classes have too much work giong on in repaint.
|
12
|
+
# Single selection, selection is with ENTER key, SPACE scrolls
|
13
|
+
# @param Array values to be displayed
|
14
|
+
# @param Hash configuration settings such as row, col, width, height etc
|
15
|
+
# @return int - index in list if selected, nil if C-q pressed
|
16
|
+
def padpopuplist list, config={}, &block
|
17
|
+
raise ArgumentError, "Nil list received by popuplist" unless list
|
18
|
+
|
19
|
+
max_visible_items = config[:max_visible_items]
|
20
|
+
row = config[:row] || 5
|
21
|
+
col = config[:col] || 5
|
22
|
+
relative_to = config[:relative_to]
|
23
|
+
if relative_to
|
24
|
+
layout = relative_to.form.window.layout
|
25
|
+
row += layout[:top]
|
26
|
+
col += layout[:left]
|
27
|
+
end
|
28
|
+
config.delete :relative_to
|
29
|
+
longest = list.max_by(&:length)
|
30
|
+
width = config[:width] || longest.size()+2 # borders take 2
|
31
|
+
if config[:title]
|
32
|
+
width = config[:title].size + 2 if width < config[:title].size
|
33
|
+
end
|
34
|
+
height = config[:height]
|
35
|
+
height ||= [max_visible_items || 10+2, list.length+2].min
|
36
|
+
#layout(1+height, width+4, row, col)
|
37
|
+
layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
|
38
|
+
window = Canis::Window.new(layout)
|
39
|
+
form = Canis::Form.new window
|
40
|
+
|
41
|
+
listconfig = config[:listconfig] || {}
|
42
|
+
listconfig[:list] = list
|
43
|
+
listconfig[:width] = width
|
44
|
+
listconfig[:height] = height
|
45
|
+
#listconfig[:selection_mode] ||= :single
|
46
|
+
listconfig.merge!(config)
|
47
|
+
listconfig.delete(:row);
|
48
|
+
listconfig.delete(:col);
|
49
|
+
# trying to pass populists block to listbox
|
50
|
+
#lb = Canis::List.new form, listconfig, &block
|
51
|
+
lb = Canis::TextPad.new form, listconfig, &block
|
52
|
+
#lb = Cygnus::TextPad.new form, :height => height, :width => width, :row => 0, :col => 0 , :title => "A title", :name => "popup"
|
53
|
+
lb.text(list)
|
54
|
+
#
|
55
|
+
#window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
|
56
|
+
form.repaint
|
57
|
+
Ncurses::Panel.update_panels
|
58
|
+
begin
|
59
|
+
while((ch = window.getchar()) != 999 )
|
60
|
+
case ch
|
61
|
+
when ?\C-q.getbyte(0)
|
62
|
+
break
|
63
|
+
else
|
64
|
+
lb.handle_key ch
|
65
|
+
form.repaint
|
66
|
+
if ch == 13 || ch == 10
|
67
|
+
return lb.current_index #if lb.selection_mode != :multiple
|
68
|
+
end
|
69
|
+
#yield ch if block_given?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
window.destroy
|
74
|
+
end
|
75
|
+
return nil
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
##
|
80
|
+
# Menu creator which displays a menu and executes methods based on keys.
|
81
|
+
# In some cases, we call this and then do a case statement on either key or binding.
|
82
|
+
# Call this with care if you do not intend values to be executed. Maybe that was a bad
|
83
|
+
# idea to club execution with display.
|
84
|
+
# @param String title
|
85
|
+
# @param hash of keys and methods to call
|
86
|
+
# @return key pressed, and binding (if found, and responded)
|
87
|
+
#
|
88
|
+
def menu title, hash, config={}, &block
|
89
|
+
raise ArgumentError, "Nil hash received by menu" unless hash
|
90
|
+
list = []
|
91
|
+
hash.each_pair { |k, v| list << " #[fg=yellow, bold] #{k} #[/end] #[fg=green] #{v} #[/end]" }
|
92
|
+
# s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
|
93
|
+
config[:title] = title
|
94
|
+
ch = padpopup list, config, &block
|
95
|
+
return unless ch
|
96
|
+
if ch.size > 1
|
97
|
+
# could be a string due to pressing enter
|
98
|
+
# but what if we format into multiple columns
|
99
|
+
ch = ch.strip[0]
|
100
|
+
end
|
101
|
+
|
102
|
+
binding = hash[ch]
|
103
|
+
binding = hash[ch.to_sym] unless binding
|
104
|
+
if binding
|
105
|
+
if respond_to?(binding, true)
|
106
|
+
send(binding)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
return ch, binding
|
110
|
+
end
|
111
|
+
|
112
|
+
# pops up a list, taking a single key and returning if it is in range of 33 and 126
|
113
|
+
# Called by menu, print_help, show_marks etc
|
114
|
+
# You may pass valid chars or ints so it only returns on pressing those.
|
115
|
+
#
|
116
|
+
# @param Array of lines to print which may be formatted using :tmux format
|
117
|
+
# @return character pressed (ch.chr)
|
118
|
+
# @return nil if escape or C-q pressed
|
119
|
+
#
|
120
|
+
def padpopup list, config={}, &block
|
121
|
+
max_visible_items = config[:max_visible_items]
|
122
|
+
row = config[:row] || 5
|
123
|
+
col = config[:col] || 5
|
124
|
+
# format options are :ansi :tmux :none
|
125
|
+
fmt = config[:format] || :tmux
|
126
|
+
config.delete :format
|
127
|
+
relative_to = config[:relative_to]
|
128
|
+
if relative_to
|
129
|
+
layout = relative_to.form.window.layout
|
130
|
+
row += layout[:top]
|
131
|
+
col += layout[:left]
|
132
|
+
end
|
133
|
+
config.delete :relative_to
|
134
|
+
# still has the formatting in the string so length is wrong.
|
135
|
+
#longest = list.max_by(&:length)
|
136
|
+
width = config[:width] || 60
|
137
|
+
if config[:title]
|
138
|
+
width = config[:title].size + 2 if width < config[:title].size
|
139
|
+
end
|
140
|
+
height = config[:height]
|
141
|
+
height ||= [max_visible_items || 25, list.length+2].min
|
142
|
+
#layout(1+height, width+4, row, col)
|
143
|
+
layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
|
144
|
+
window = Canis::Window.new(layout)
|
145
|
+
form = Canis::Form.new window
|
146
|
+
|
147
|
+
## added 2013-03-13 - 18:07 so caller can be more specific on what is to be returned
|
148
|
+
valid_keys_int = config.delete :valid_keys_int
|
149
|
+
valid_keys_char = config.delete :valid_keys_char
|
150
|
+
|
151
|
+
listconfig = config[:listconfig] || {}
|
152
|
+
#listconfig[:list] = list
|
153
|
+
listconfig[:width] = width
|
154
|
+
listconfig[:height] = height
|
155
|
+
#listconfig[:selection_mode] ||= :single
|
156
|
+
listconfig.merge!(config)
|
157
|
+
listconfig.delete(:row);
|
158
|
+
listconfig.delete(:col);
|
159
|
+
# trying to pass populists block to listbox
|
160
|
+
lb = Canis::TextPad.new form, listconfig, &block
|
161
|
+
if fmt == :none
|
162
|
+
lb.text(list)
|
163
|
+
else
|
164
|
+
lb.text(list, fmt)
|
165
|
+
end
|
166
|
+
#
|
167
|
+
#window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
|
168
|
+
form.repaint
|
169
|
+
Ncurses::Panel.update_panels
|
170
|
+
if valid_keys_int.nil? && valid_keys_char.nil?
|
171
|
+
# changed 32 to 33 so space can scroll list
|
172
|
+
valid_keys_int = (33..126)
|
173
|
+
end
|
174
|
+
|
175
|
+
begin
|
176
|
+
while((ch = window.getchar()) != 999 )
|
177
|
+
|
178
|
+
# if a char range or array has been sent, check if the key is in it and send back
|
179
|
+
# else just stay here
|
180
|
+
if valid_keys_char
|
181
|
+
if ch > 32 && ch < 127
|
182
|
+
chr = ch.chr
|
183
|
+
return chr if valid_keys_char.include? chr
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# if the user specified an array or range of ints check against that
|
188
|
+
# therwise use the range of 33 .. 126
|
189
|
+
return ch.chr if valid_keys_int.include? ch
|
190
|
+
|
191
|
+
case ch
|
192
|
+
when ?\C-q.getbyte(0)
|
193
|
+
break
|
194
|
+
else
|
195
|
+
if ch == 13 || ch == 10
|
196
|
+
s = lb.current_value.to_s # .strip #if lb.selection_mode != :multiple
|
197
|
+
return s
|
198
|
+
end
|
199
|
+
# close if escape or double escape
|
200
|
+
if ch == 27 || ch == 2727
|
201
|
+
return nil
|
202
|
+
end
|
203
|
+
lb.handle_key ch
|
204
|
+
form.repaint
|
205
|
+
end
|
206
|
+
end
|
207
|
+
ensure
|
208
|
+
window.destroy
|
209
|
+
end
|
210
|
+
return nil
|
211
|
+
end
|
212
|
+
|
213
|
+
# indexes and returns indexed and colored version of list using alpha as indexes
|
214
|
+
# See $IDX and get_shortcut for index details
|
215
|
+
# @param Array a list of values to index which will then be displayed using a padpopup
|
216
|
+
# @returns Array formatted and indexed list
|
217
|
+
def index_this_list list
|
218
|
+
alist = []
|
219
|
+
list.each_with_index { |v, ix|
|
220
|
+
k = get_shortcut ix
|
221
|
+
alist << " #[fg=yellow, bold] #{k.ljust(2)} #[end] #[fg=green]#{v}#[end]"
|
222
|
+
# above gets truncated by columnate and results in errors in colorparsers etc
|
223
|
+
#alist << " #{k} #{v}"
|
224
|
+
}
|
225
|
+
return alist
|
226
|
+
end
|
227
|
+
|
228
|
+
$IDX=('a'..'y').to_a
|
229
|
+
$IDX.concat ('za'..'zz').to_a
|
230
|
+
$IDX.concat ('Za'..'Zz').to_a
|
231
|
+
$IDX.concat ('ZA'..'ZZ').to_a
|
232
|
+
|
233
|
+
# a general function that creates a popup list after indexing the current list,
|
234
|
+
# takes a key and returns the value from the list that was selected, or nil if the
|
235
|
+
# value was invalid.
|
236
|
+
# Called by show_list (cygnus gem) which is in turn called by several methods.
|
237
|
+
#
|
238
|
+
def indexed_list title, list, config={}, &block
|
239
|
+
raise ArgumentError, "Nil list received by indexed_list" unless list
|
240
|
+
$stact = 0
|
241
|
+
alist = index_this_list list
|
242
|
+
longest = list.max_by(&:length)
|
243
|
+
# s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
|
244
|
+
config[:title] = title
|
245
|
+
# if width is greater than size of screen then padfresh will return -1 and nothing will print
|
246
|
+
config[:width] = [ longest.size() + 10, FFI::NCurses.COLS - 1 ].min
|
247
|
+
config[:row] = config[:col] = 0
|
248
|
+
ch = padpopup alist, config, &block
|
249
|
+
return unless ch
|
250
|
+
if ch.size > 1
|
251
|
+
# could be a string due to pressing enter
|
252
|
+
# but what if we format into multiple columns
|
253
|
+
ch = ch.strip[0]
|
254
|
+
end
|
255
|
+
# we are checking this AFTER the popup has returned, what window will be used ?
|
256
|
+
ch = get_index ch
|
257
|
+
return nil unless ch
|
258
|
+
|
259
|
+
return list[ch]
|
260
|
+
end
|
261
|
+
# print in columns
|
262
|
+
# ary - array of data
|
263
|
+
# sz - lines in one column
|
264
|
+
# This is the original which did not format or color, but since we cannot truncate unless
|
265
|
+
# we have unformatted data i need to mix the functionality into columnate_with_indexing
|
266
|
+
# Called by print_help, this is generic and can be used as a common func
|
267
|
+
|
268
|
+
def columnate ary, sz
|
269
|
+
buff=Array.new
|
270
|
+
return buff if ary.nil? || ary.size == 0
|
271
|
+
|
272
|
+
# ix refers to the index in the complete file list, wherease we only show 60 at a time
|
273
|
+
ix=0
|
274
|
+
while true
|
275
|
+
## ctr refers to the index in the column
|
276
|
+
ctr=0
|
277
|
+
while ctr < sz
|
278
|
+
|
279
|
+
f = ary[ix]
|
280
|
+
# deleted truncate and pad part since we expect cols to be sized same
|
281
|
+
|
282
|
+
if buff[ctr]
|
283
|
+
buff[ctr] += f
|
284
|
+
else
|
285
|
+
buff[ctr] = f
|
286
|
+
end
|
287
|
+
|
288
|
+
ctr+=1
|
289
|
+
ix+=1
|
290
|
+
break if ix >= ary.size
|
291
|
+
end
|
292
|
+
break if ix >= ary.size
|
293
|
+
end
|
294
|
+
return buff
|
295
|
+
end
|
296
|
+
def pbold text
|
297
|
+
#puts "#{BOLD}#{text}#{BOLD_OFF}"
|
298
|
+
get_single text, :color_pair => $reversecolor
|
299
|
+
end
|
300
|
+
def perror text
|
301
|
+
##puts "#{RED}#{text}#{CLEAR}"
|
302
|
+
#get_char
|
303
|
+
#alert text
|
304
|
+
get_single text + " Press a key...", :color_pair => $errorcolor
|
305
|
+
end
|
306
|
+
def pause text=" Press a key ..."
|
307
|
+
get_single text
|
308
|
+
#get_char
|
309
|
+
end
|
310
|
+
## return shortcut for an index (offset in file array)
|
311
|
+
# use 2 more arrays to make this faster
|
312
|
+
# if z or Z take another key if there are those many in view
|
313
|
+
# Also, display ROWS * COLS so now we are not limited to 60.
|
314
|
+
def get_shortcut ix
|
315
|
+
return "<" if ix < $stact
|
316
|
+
ix -= $stact
|
317
|
+
i = $IDX[ix]
|
318
|
+
return i if i
|
319
|
+
return "->"
|
320
|
+
end
|
321
|
+
## returns the integer offset in view (file array based on a-y za-zz and Za - Zz
|
322
|
+
# Called when user types a key
|
323
|
+
# should we even ask for a second key if there are not enough rows
|
324
|
+
# What if we want to also trap z with numbers for other purposes
|
325
|
+
def get_index key, vsz=999
|
326
|
+
i = $IDX.index(key)
|
327
|
+
return i+$stact if i
|
328
|
+
#sz = $IDX.size
|
329
|
+
zch = nil
|
330
|
+
if vsz > 25
|
331
|
+
if key == "z" || key == "Z"
|
332
|
+
#print key
|
333
|
+
zch = get_char
|
334
|
+
#print zch
|
335
|
+
i = $IDX.index("#{key}#{zch}")
|
336
|
+
return i+$stact if i
|
337
|
+
end
|
338
|
+
end
|
339
|
+
return nil
|
340
|
+
end
|
341
|
+
## I thin we need to make this like the command line one TODO
|
342
|
+
def get_char
|
343
|
+
w = @window || $window
|
344
|
+
c = w.getchar
|
345
|
+
case c
|
346
|
+
when 13,10
|
347
|
+
return "ENTER"
|
348
|
+
when 32
|
349
|
+
return "SPACE"
|
350
|
+
when 127
|
351
|
+
return "BACKSPACE"
|
352
|
+
when 27
|
353
|
+
return "ESCAPE"
|
354
|
+
end
|
355
|
+
keycode_tos c
|
356
|
+
# if c > 32 && c < 127
|
357
|
+
#return c.chr
|
358
|
+
#end
|
359
|
+
## use keycode_tos from Utils.
|
360
|
+
end
|
361
|
+
##
|
362
|
+
# prints a prompt at bottom of screen, takes a character and returns textual representation
|
363
|
+
# of character (as per get_char) and not the int that window.getchar returns.
|
364
|
+
# It uses a window, so underlying text is not touched.
|
365
|
+
#
|
366
|
+
def get_single text, config={}
|
367
|
+
w = one_line_window
|
368
|
+
x = y = 0
|
369
|
+
color = config[:color_pair] || $datacolor
|
370
|
+
color=Ncurses.COLOR_PAIR(color);
|
371
|
+
w.attron(color);
|
372
|
+
w.mvprintw(x, y, "%s" % text);
|
373
|
+
w.attroff(color);
|
374
|
+
w.wrefresh
|
375
|
+
Ncurses::Panel.update_panels
|
376
|
+
chr = get_char
|
377
|
+
w.destroy
|
378
|
+
w = nil
|
379
|
+
return chr
|
380
|
+
end
|
381
|
+
|
382
|
+
##
|
383
|
+
# identical to get_string but does not show as a popup with buttons, just ENTER
|
384
|
+
# This is required if there are multiple inputs required and having several get_strings
|
385
|
+
# one after the other seems really odd due to multiple popups
|
386
|
+
# Unlike, get_string this does not return a nil if C-c pressed. Either returns a string if
|
387
|
+
# ENTER pressed or a blank if C-c or Double Escape. So only blank to be checked
|
388
|
+
# TODO up arrow can access history
|
389
|
+
def get_line text, config={}
|
390
|
+
begin
|
391
|
+
w = one_line_window
|
392
|
+
form = Canis::Form.new w
|
393
|
+
config[:label] = text
|
394
|
+
config[:row] = 0
|
395
|
+
config[:col] = 1
|
396
|
+
|
397
|
+
#f = Field.new form, :label => text, :row => 0, :col => 1
|
398
|
+
f = Field.new form, config
|
399
|
+
form.repaint
|
400
|
+
w.wrefresh
|
401
|
+
while((ch = w.getchar()) != FFI::NCurses::KEY_F10 )
|
402
|
+
break if ch == 13
|
403
|
+
if ch == 3 || ch == 27 || ch == 2727
|
404
|
+
return ""
|
405
|
+
end
|
406
|
+
begin
|
407
|
+
form.handle_key(ch)
|
408
|
+
w.wrefresh
|
409
|
+
rescue => err
|
410
|
+
$log.debug( err) if err
|
411
|
+
$log.debug(err.backtrace.join("\n")) if err
|
412
|
+
textdialog ["Error in Messagebox: #{err} ", *err.backtrace], :title => "Exception"
|
413
|
+
w.refresh # otherwise the window keeps showing (new FFI-ncurses issue)
|
414
|
+
$error_message.value = ""
|
415
|
+
ensure
|
416
|
+
end
|
417
|
+
end # while loop
|
418
|
+
|
419
|
+
ensure
|
420
|
+
w.destroy
|
421
|
+
w = nil
|
422
|
+
end
|
423
|
+
return f.text
|
424
|
+
end
|
425
|
+
def ask prompt, dt=nil
|
426
|
+
return get_line prompt
|
427
|
+
end
|
428
|
+
##
|
429
|
+
# justify or pad list with spaces so we can columnate, uses longest item to determine size
|
430
|
+
# @param list to pad
|
431
|
+
# @return list padded with spaces
|
432
|
+
def padup_list list
|
433
|
+
longest = list.max_by(&:length)
|
434
|
+
llen = longest.size
|
435
|
+
alist = list.collect { |x|
|
436
|
+
x.ljust(llen)
|
437
|
+
}
|
438
|
+
alist
|
439
|
+
end
|
440
|
+
|
441
|
+
|
442
|
+
# pops up a list, taking a single key and returning if it is in range of 33 and 126
|
443
|
+
# This is a specialized method and overrides textpads keys and behavior
|
444
|
+
#
|
445
|
+
def ri_full_indexed_list list, config={}, &block
|
446
|
+
config[:row] ||= 0
|
447
|
+
config[:col] ||= 0
|
448
|
+
config[:width] ||= FFI::NCurses.COLS - config[:col]
|
449
|
+
width = config[:width]
|
450
|
+
if config[:title]
|
451
|
+
width = config[:title].size + 2 if width < config[:title].size
|
452
|
+
end
|
453
|
+
height = config[:height]
|
454
|
+
#height ||= [max_visible_items || 25, list.length+2].min
|
455
|
+
height ||= FFI::NCurses.LINES - config[:row]
|
456
|
+
config[:height] = height
|
457
|
+
config[:name] = "fil"
|
458
|
+
row = config[:row]
|
459
|
+
col = config[:col]
|
460
|
+
#layout(1+height, width+4, row, col)
|
461
|
+
layout = { :height => 0+height, :width => 0+width, :top => row, :left => col }
|
462
|
+
window = Canis::Window.new(layout)
|
463
|
+
form = Canis::Form.new window
|
464
|
+
|
465
|
+
|
466
|
+
#config[:suppress_border] = true
|
467
|
+
lb = Canis::TextPad.new form, config, &block
|
468
|
+
grows = lb.rows
|
469
|
+
gviscols ||= 3
|
470
|
+
pagesize = grows * gviscols
|
471
|
+
files = list
|
472
|
+
patt = nil
|
473
|
+
view = nil
|
474
|
+
sta = cursor = 0
|
475
|
+
$stact = 0 # to prevent crash
|
476
|
+
ignorecase = true
|
477
|
+
|
478
|
+
while true
|
479
|
+
break if $quitting
|
480
|
+
if patt
|
481
|
+
if ignorecase
|
482
|
+
view = files.grep(/#{patt}/i)
|
483
|
+
else
|
484
|
+
view = files.grep(/#{patt}/)
|
485
|
+
end
|
486
|
+
else
|
487
|
+
view = files
|
488
|
+
end
|
489
|
+
fl=view.size
|
490
|
+
sta = 0 if sta < 0
|
491
|
+
cursor = fl -1 if cursor >= fl
|
492
|
+
cursor = 0 if cursor < 0
|
493
|
+
sta = calc_sta pagesize, cursor
|
494
|
+
$log.debug "XXX: sta is #{sta}, size is #{fl}"
|
495
|
+
viewport = view[sta, pagesize]
|
496
|
+
fin = sta + viewport.size
|
497
|
+
#alist = columnate_with_indexing viewport, grows
|
498
|
+
viewport = padup_list viewport
|
499
|
+
viewport = index_this_list viewport
|
500
|
+
alist = columnate viewport, grows
|
501
|
+
lb.text(alist, :tmux)
|
502
|
+
# we need to show the next 1 to n of n for long lists
|
503
|
+
#@header.text_right "#{$sta+1} to #{fin} of #{fl}"
|
504
|
+
#
|
505
|
+
form.repaint
|
506
|
+
Ncurses::Panel.update_panels
|
507
|
+
|
508
|
+
begin
|
509
|
+
|
510
|
+
ch = window.getchar()
|
511
|
+
|
512
|
+
# if a char range or array has been sent, check if the key is in it and send back
|
513
|
+
# else just stay here
|
514
|
+
#if ( ( ch >= ?a.ord && ch <= ?z.ord ) || ( ch >= ?A.ord && ch <= ?Z.ord ) )
|
515
|
+
if ( ( ch >= ?a.ord && ch <= ?z.ord ) || ( ch == ?Z.ord ) )
|
516
|
+
|
517
|
+
chr = ch.chr
|
518
|
+
|
519
|
+
chr = get_index chr, viewport.size
|
520
|
+
# viewport has indexed data
|
521
|
+
#viewport = view[sta, pagesize]
|
522
|
+
return view[sta+chr] if chr
|
523
|
+
next
|
524
|
+
end
|
525
|
+
|
526
|
+
|
527
|
+
case ch
|
528
|
+
when 32, "SPACE", ?\M-n.getbyte(0)
|
529
|
+
#next_page
|
530
|
+
sta += pagesize
|
531
|
+
cursor = sta if cursor < sta
|
532
|
+
next
|
533
|
+
when ?\M-p.getbyte(0)
|
534
|
+
# prev page
|
535
|
+
sta -= pagesize
|
536
|
+
cursor = sta
|
537
|
+
next
|
538
|
+
when ?/.getbyte(0)
|
539
|
+
# filter data on regex
|
540
|
+
patt = get_line "Enter pattern: "
|
541
|
+
next
|
542
|
+
when ?\C-q.getbyte(0)
|
543
|
+
return nil
|
544
|
+
else
|
545
|
+
# close if escape or double escape or C-c
|
546
|
+
if ch == 27 || ch == 2727 || ch == 3
|
547
|
+
# this just closes the app ! since my finger remains on Ctrl which is Escape
|
548
|
+
return nil
|
549
|
+
end
|
550
|
+
# lets check our own bindings so textpad doesn't take over
|
551
|
+
# Either that or check form's first
|
552
|
+
# but this way we can just reuse from cetus
|
553
|
+
#retval = c_process_key ch
|
554
|
+
#next if retval
|
555
|
+
|
556
|
+
retval = form.handle_key ch #if retval == :UNHANDLED
|
557
|
+
next if retval != :UNHANDLED
|
558
|
+
$log.debug "XXXX form returned #{retval} for #{ch}"
|
559
|
+
#alert "got key before lb.handle #{ch.chr}"
|
560
|
+
retval = lb.handle_key ch if retval.nil? || retval == :UNHANDLED
|
561
|
+
# if retval == :UNHANDLED
|
562
|
+
#alert "got key in unhdnalde lb.handle #{ch}, #{retval}"
|
563
|
+
$log.debug "XXXX textpad returned #{retval} for #{ch}"
|
564
|
+
end
|
565
|
+
|
566
|
+
form.repaint
|
567
|
+
#end # while getchar
|
568
|
+
end
|
569
|
+
end # while true
|
570
|
+
return nil
|
571
|
+
ensure
|
572
|
+
window.destroy
|
573
|
+
end
|
574
|
+
##
|
575
|
+
# calculate start of display based on current position
|
576
|
+
# This is the value 'sta' should have (starting index)
|
577
|
+
# @param int pagesize : number of items on one page
|
578
|
+
# @param int cur : current cursor position
|
579
|
+
# @return int : position where 'sta' should be
|
580
|
+
def calc_sta pagesize, cur
|
581
|
+
pages = (cur * 1.001 / pagesize).ceil
|
582
|
+
pages -= 1 if pages > 0
|
583
|
+
return pages * pagesize
|
584
|
+
end
|
585
|
+
|
586
|
+
end # module
|
587
|
+
include Rigel
|
data/rigel.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rigel/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rigel"
|
8
|
+
spec.version = Rigel::VERSION
|
9
|
+
spec.authors = ["kepler"]
|
10
|
+
spec.email = ["githubkepler.50@gishpuppy.com"]
|
11
|
+
spec.summary = %q{ruby documentation browser in terminal (text/ncurses)}
|
12
|
+
spec.description = %q{Ruby documentation (ri) browser in text terminals using ncurses}
|
13
|
+
spec.homepage = "https://github.com/mare-imbrium/rigel"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake", ">= 0.9.6"
|
23
|
+
spec.add_runtime_dependency "canis", ">= 0.0.3", ">= 0.0.3"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rigel
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- kepler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-12 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.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.6
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.9.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: canis
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.3
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.3
|
55
|
+
description: Ruby documentation (ri) browser in text terminals using ncurses
|
56
|
+
email:
|
57
|
+
- githubkepler.50@gishpuppy.com
|
58
|
+
executables:
|
59
|
+
- rigel
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- Gemfile
|
65
|
+
- Gemfile.lock
|
66
|
+
- LICENSE
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/rigel
|
70
|
+
- lib/rigel.rb
|
71
|
+
- lib/rigel/version.rb
|
72
|
+
- rigel.gemspec
|
73
|
+
homepage: https://github.com/mare-imbrium/rigel
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.2.2
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: ruby documentation browser in terminal (text/ncurses)
|
97
|
+
test_files: []
|