rigel 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|