cygnus 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +1 -0
- data/bin/cygnus +900 -0
- data/cygnus.gemspec +23 -0
- data/lib/cygnus.rb +1520 -0
- data/lib/cygnus/textpad.rb +846 -0
- data/lib/cygnus/version.rb +3 -0
- metadata +95 -0
data/cygnus.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cygnus/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cygnus"
|
8
|
+
spec.version = Cygnus::VERSION
|
9
|
+
spec.authors = ["Rahul Kumar"]
|
10
|
+
spec.email = ["sentinel1879@gmail.com"]
|
11
|
+
spec.description = %q{best code browser eva}
|
12
|
+
spec.summary = %q{the finest code browser with exactly what you need and no more}
|
13
|
+
spec.homepage = "https://github.com/rkumar/cygnus"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
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.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/lib/cygnus.rb
ADDED
@@ -0,0 +1,1520 @@
|
|
1
|
+
require "cygnus/version"
|
2
|
+
require 'rbcurse/core/util/app'
|
3
|
+
|
4
|
+
module Cygnus
|
5
|
+
# Your code goes here...
|
6
|
+
# mmm what code ?
|
7
|
+
# http://www.ruby-doc.org/stdlib-1.9.3/libdoc/shellwords/rdoc/Shellwords.html
|
8
|
+
require 'shellwords'
|
9
|
+
require 'fileutils'
|
10
|
+
CONFIG_FILE="~/.lyrainfo"
|
11
|
+
|
12
|
+
|
13
|
+
$selected_files = Array.new
|
14
|
+
$bookmarks = {}
|
15
|
+
$mode = nil
|
16
|
+
$glines=%x(tput lines).to_i
|
17
|
+
$gcols=%x(tput cols).to_i
|
18
|
+
# grows depends on size of textpad @cols, not screen size, since this is no longer cli
|
19
|
+
$grows = $glines - 2
|
20
|
+
$pagesize = 60
|
21
|
+
$gviscols = 3
|
22
|
+
$pagesize = $grows * $gviscols
|
23
|
+
$stact = 0
|
24
|
+
$editor_mode = true
|
25
|
+
$enhanced_mode = true
|
26
|
+
$visual_block_start = nil
|
27
|
+
$pager_command = {
|
28
|
+
:text => 'most',
|
29
|
+
:image => 'open',
|
30
|
+
:zip => 'tar ztvf %% | most',
|
31
|
+
:unknown => 'open'
|
32
|
+
}
|
33
|
+
$dir_position = {}
|
34
|
+
## CONSTANTS
|
35
|
+
GMARK='*'
|
36
|
+
CURMARK='>'
|
37
|
+
MSCROLL = 10
|
38
|
+
SPACE=" "
|
39
|
+
#CLEAR = "\e[0m"
|
40
|
+
#BOLD = "\e[1m"
|
41
|
+
#BOLD_OFF = "\e[22m"
|
42
|
+
#RED = "\e[31m"
|
43
|
+
#ON_RED = "\e[41m"
|
44
|
+
#GREEN = "\e[32m"
|
45
|
+
#YELLOW = "\e[33m"
|
46
|
+
#BLUE = "\e[1;34m"
|
47
|
+
#
|
48
|
+
ON_BLUE = "\e[44m"
|
49
|
+
#REVERSE = "\e[7m"
|
50
|
+
CURSOR_COLOR = ""
|
51
|
+
CLEAR = ""
|
52
|
+
$patt=nil
|
53
|
+
$ignorecase = true
|
54
|
+
$quitting = false
|
55
|
+
$modified = $writing = false
|
56
|
+
$visited_files = []
|
57
|
+
## dir stack for popping
|
58
|
+
$visited_dirs = []
|
59
|
+
## dirs where some work has been done, for saving and restoring
|
60
|
+
$used_dirs = []
|
61
|
+
$default_sort_order = "om"
|
62
|
+
$sorto = $default_sort_order
|
63
|
+
$viewctr = 0
|
64
|
+
$history = []
|
65
|
+
$sta = $cursor = 0
|
66
|
+
$visual_mode = false
|
67
|
+
|
68
|
+
## main loop which calls all other programs
|
69
|
+
|
70
|
+
## code related to long listing of files
|
71
|
+
GIGA_SIZE = 1073741824.0
|
72
|
+
MEGA_SIZE = 1048576.0
|
73
|
+
KILO_SIZE = 1024.0
|
74
|
+
|
75
|
+
# Return the file size with a readable style.
|
76
|
+
def readable_file_size(size, precision)
|
77
|
+
case
|
78
|
+
#when size == 1 : "1 B"
|
79
|
+
when size < KILO_SIZE then "%d B" % size
|
80
|
+
when size < MEGA_SIZE then "%.#{precision}f K" % (size / KILO_SIZE)
|
81
|
+
when size < GIGA_SIZE then "%.#{precision}f M" % (size / MEGA_SIZE)
|
82
|
+
else "%.#{precision}f G" % (size / GIGA_SIZE)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
## format date for file given stat
|
86
|
+
def date_format t
|
87
|
+
t.strftime "%Y/%m/%d"
|
88
|
+
end
|
89
|
+
##
|
90
|
+
#
|
91
|
+
# print in columns
|
92
|
+
# ary - array of data
|
93
|
+
# sz - lines in one column
|
94
|
+
#
|
95
|
+
def columnate_with_indexing ary, sz
|
96
|
+
buff=Array.new
|
97
|
+
$log.warn "columnate_with_indexing got nil list " unless ary
|
98
|
+
return buff if ary.nil? || ary.size == 0
|
99
|
+
|
100
|
+
# determine width based on number of files to show
|
101
|
+
# if less than sz then 1 col and full width
|
102
|
+
#
|
103
|
+
wid = 30
|
104
|
+
ars = ary.size
|
105
|
+
ars = [$pagesize, ary.size].min
|
106
|
+
# 2 maybe for borders also
|
107
|
+
d = 0
|
108
|
+
if ars <= sz
|
109
|
+
wid = $gcols - d
|
110
|
+
else
|
111
|
+
tmp = (ars * 1.000/ sz).ceil
|
112
|
+
wid = $gcols / tmp - d
|
113
|
+
end
|
114
|
+
|
115
|
+
# ix refers to the index in the complete file list, wherease we only show 60 at a time
|
116
|
+
ix=0
|
117
|
+
while true
|
118
|
+
## ctr refers to the index in the column
|
119
|
+
ctr=0
|
120
|
+
while ctr < sz
|
121
|
+
|
122
|
+
cur=SPACE
|
123
|
+
cur = CURMARK if ix + $sta == $cursor
|
124
|
+
f = ary[ix]
|
125
|
+
|
126
|
+
if $long_listing
|
127
|
+
begin
|
128
|
+
# one of zsh's flags adds not just / but @ * and a space, so the a FNF error comes
|
129
|
+
unless File.exist? f
|
130
|
+
last = f[-1]
|
131
|
+
if last == " " || last == "@" || last == '*'
|
132
|
+
stat = File.stat(f.chop)
|
133
|
+
end
|
134
|
+
else
|
135
|
+
stat = File.stat(f)
|
136
|
+
end
|
137
|
+
f = "%10s %s %s" % [readable_file_size(stat.size,1), date_format(stat.mtime), f]
|
138
|
+
rescue Exception => e
|
139
|
+
f = "%10s %s %s" % ["?", "??????????", f]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# be careful of modifying f or original array gets modified XXX
|
144
|
+
k = get_shortcut ix
|
145
|
+
isdir = f[-1] == "/"
|
146
|
+
fsz = f.size + k.to_s.size + 0
|
147
|
+
fsz = f.size + 1
|
148
|
+
if fsz > wid
|
149
|
+
# truncated since longer
|
150
|
+
f = f[0, wid-2]+"$ "
|
151
|
+
## we do the coloring after trunc so ANSI escpe seq does not get get
|
152
|
+
#if ix + $sta == $cursor
|
153
|
+
#f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
|
154
|
+
#end
|
155
|
+
else
|
156
|
+
## we do the coloring before padding so the entire line does not get padded, only file name
|
157
|
+
#if ix + $sta == $cursor
|
158
|
+
#f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
|
159
|
+
#end
|
160
|
+
f = f.ljust(wid)
|
161
|
+
# pad with spaces
|
162
|
+
#f << " " * (wid-fsz)
|
163
|
+
#f = f + " " * (wid-fsz)
|
164
|
+
end
|
165
|
+
# now we add the shortcut with the coloring (we need to adjust the space of the shortcut)
|
166
|
+
#
|
167
|
+
colr = "white"
|
168
|
+
colr = "blue, bold" if isdir
|
169
|
+
# this directly modified the damned index resulting in searches failing
|
170
|
+
#k << " " if k.length == 1
|
171
|
+
k = k + " " if k.length == 1
|
172
|
+
|
173
|
+
f = "#{cur}#[fg=yellow, bold]#{k}#[end] #[fg=#{colr}]#{f}#[end]"
|
174
|
+
|
175
|
+
if buff[ctr]
|
176
|
+
buff[ctr] += f
|
177
|
+
else
|
178
|
+
buff[ctr] = f
|
179
|
+
end
|
180
|
+
|
181
|
+
ctr+=1
|
182
|
+
ix+=1
|
183
|
+
break if ix >= ary.size
|
184
|
+
end
|
185
|
+
break if ix >= ary.size
|
186
|
+
end
|
187
|
+
return buff
|
188
|
+
end
|
189
|
+
## formats the data with number, mark and details
|
190
|
+
def format ary
|
191
|
+
#buff = Array.new
|
192
|
+
buff = Array.new(ary.size)
|
193
|
+
return buff if ary.nil? || ary.size == 0
|
194
|
+
|
195
|
+
# determine width based on number of files to show
|
196
|
+
# if less than sz then 1 col and full width
|
197
|
+
#
|
198
|
+
# ix refers to the index in the complete file list, wherease we only show 60 at a time
|
199
|
+
ix=0
|
200
|
+
ctr=0
|
201
|
+
ary.each do |f|
|
202
|
+
## ctr refers to the index in the column
|
203
|
+
ind = get_shortcut(ix)
|
204
|
+
mark=SPACE
|
205
|
+
cur=SPACE
|
206
|
+
cur = CURMARK if ix + $sta == $cursor
|
207
|
+
mark=GMARK if $selected_files.index(ary[ix])
|
208
|
+
|
209
|
+
if $long_listing
|
210
|
+
begin
|
211
|
+
unless File.exist? f
|
212
|
+
last = f[-1]
|
213
|
+
if last == " " || last == "@" || last == '*'
|
214
|
+
stat = File.stat(f.chop)
|
215
|
+
end
|
216
|
+
else
|
217
|
+
stat = File.stat(f)
|
218
|
+
end
|
219
|
+
f = "%10s %s %s" % [readable_file_size(stat.size,1), date_format(stat.mtime), f]
|
220
|
+
rescue Exception => e
|
221
|
+
f = "%10s %s %s" % ["?", "??????????", f]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
s = "#{ind}#{mark}#{cur}#{f}"
|
226
|
+
# I cannot color the current line since format does the chopping
|
227
|
+
# so not only does the next lines alignment get skeweed, but also if the line is truncated
|
228
|
+
# then the color overflows.
|
229
|
+
#if ix + $sta == $cursor
|
230
|
+
#s = "#{RED}#{s}#{CLEAR}"
|
231
|
+
#end
|
232
|
+
|
233
|
+
buff[ctr] = s
|
234
|
+
|
235
|
+
ctr+=1
|
236
|
+
ix+=1
|
237
|
+
end
|
238
|
+
return buff
|
239
|
+
end
|
240
|
+
## select file based on key pressed
|
241
|
+
def select_hint view, ch
|
242
|
+
# a to y is direct
|
243
|
+
# if z or Z take a key IF there are those many
|
244
|
+
#
|
245
|
+
ix = get_index(ch, view.size)
|
246
|
+
if ix
|
247
|
+
f = view[ix]
|
248
|
+
return unless f
|
249
|
+
$cursor = $sta + ix
|
250
|
+
|
251
|
+
if $mode == 'SEL'
|
252
|
+
toggle_select f
|
253
|
+
elsif $mode == 'COM'
|
254
|
+
run_command f
|
255
|
+
else
|
256
|
+
open_file f
|
257
|
+
end
|
258
|
+
#selectedix=ix
|
259
|
+
end
|
260
|
+
end
|
261
|
+
## toggle selection state of file
|
262
|
+
def toggle_select f
|
263
|
+
if $selected_files.index f
|
264
|
+
$selected_files.delete f
|
265
|
+
else
|
266
|
+
$selected_files.push f
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
## run command on given file/s
|
271
|
+
# Accepts command from user
|
272
|
+
# After putting readline in place of gets, pressing a C-c has a delayed effect. It goes intot
|
273
|
+
# exception bloack after executing other commands and still does not do the return !
|
274
|
+
def run_command f
|
275
|
+
files=nil
|
276
|
+
case f
|
277
|
+
when Array
|
278
|
+
# escape the contents and create a string
|
279
|
+
files = Shellwords.join(f)
|
280
|
+
when String
|
281
|
+
files = Shellwords.escape(f)
|
282
|
+
end
|
283
|
+
begin
|
284
|
+
# TODO put all this get_line stuff into field history
|
285
|
+
command = get_line "Run a command on #{files}: "
|
286
|
+
return if command.size == 0
|
287
|
+
command2 = get_line "Second part of command: "
|
288
|
+
# FIXME we may need to go into cooked mode and all that for this
|
289
|
+
# cat and most mess with the output using system
|
290
|
+
c_system "#{command} #{files} #{command2}"
|
291
|
+
rescue Exception => ex
|
292
|
+
perror "Canceled command, (#{ex}) press a key"
|
293
|
+
return
|
294
|
+
end
|
295
|
+
|
296
|
+
c_refresh
|
297
|
+
push_used_dirs Dir.pwd
|
298
|
+
end
|
299
|
+
def c_system command
|
300
|
+
w = @window || @form.window
|
301
|
+
w.hide
|
302
|
+
Ncurses.endwin
|
303
|
+
ret = system command
|
304
|
+
Ncurses.refresh
|
305
|
+
w.show
|
306
|
+
return ret
|
307
|
+
end
|
308
|
+
|
309
|
+
## clear sort order and refresh listing, used typically if you are in some view
|
310
|
+
# such as visited dirs or files
|
311
|
+
def escape
|
312
|
+
$sorto = nil
|
313
|
+
$sorto = $default_sort_order
|
314
|
+
$viewctr = 0
|
315
|
+
$title = nil
|
316
|
+
$filterstr = "M"
|
317
|
+
visual_block_clear
|
318
|
+
c_refresh
|
319
|
+
end
|
320
|
+
|
321
|
+
## refresh listing after some change like option change, or toggle
|
322
|
+
# I think NCurses has a refresh which when called internally results in this chap
|
323
|
+
# getting called since both are included. or maybe App or somehting has a refresh
|
324
|
+
def c_refresh
|
325
|
+
$filterstr ||= "M"
|
326
|
+
#$files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
|
327
|
+
$patt=nil
|
328
|
+
$title = nil
|
329
|
+
display_dir
|
330
|
+
end
|
331
|
+
#
|
332
|
+
## unselect all files
|
333
|
+
def unselect_all
|
334
|
+
$selected_files = []
|
335
|
+
$visual_mode = nil
|
336
|
+
end
|
337
|
+
|
338
|
+
## select all files
|
339
|
+
def select_all
|
340
|
+
$selected_files = $view.dup
|
341
|
+
end
|
342
|
+
|
343
|
+
## accept dir to goto and change to that ( can be a file too)
|
344
|
+
def goto_dir
|
345
|
+
begin
|
346
|
+
path = get_line "Enter path: "
|
347
|
+
return if path.nil? || path == ""
|
348
|
+
rescue Exception => ex
|
349
|
+
perror "Cancelled cd, press a key"
|
350
|
+
return
|
351
|
+
end
|
352
|
+
f = File.expand_path(path)
|
353
|
+
unless File.directory? f
|
354
|
+
## check for env variable
|
355
|
+
tmp = ENV[path]
|
356
|
+
if tmp.nil? || !File.directory?( tmp )
|
357
|
+
## check for dir in home
|
358
|
+
tmp = File.expand_path("~/#{path}")
|
359
|
+
if File.directory? tmp
|
360
|
+
f = tmp
|
361
|
+
end
|
362
|
+
else
|
363
|
+
f = tmp
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
open_file f
|
368
|
+
end
|
369
|
+
|
370
|
+
## toggle mode to selection or not
|
371
|
+
# In selection, pressed hotkey selects a file without opening, one can keep selecting
|
372
|
+
# (or deselecting).
|
373
|
+
#
|
374
|
+
def selection_mode_toggle
|
375
|
+
if $mode == 'SEL'
|
376
|
+
# we seem to be coming out of select mode with some files
|
377
|
+
if $selected_files.size > 0
|
378
|
+
run_command $selected_files
|
379
|
+
end
|
380
|
+
$mode = nil
|
381
|
+
else
|
382
|
+
#$selection_mode = !$selection_mode
|
383
|
+
$mode = 'SEL'
|
384
|
+
end
|
385
|
+
end
|
386
|
+
## toggle command mode
|
387
|
+
def command_mode
|
388
|
+
if $mode == 'COM'
|
389
|
+
$mode = nil
|
390
|
+
return
|
391
|
+
end
|
392
|
+
$mode = 'COM'
|
393
|
+
end
|
394
|
+
def goto_parent_dir
|
395
|
+
change_dir ".."
|
396
|
+
end
|
397
|
+
## This actually filters, in zfm it goes to that entry since we have a cursor there
|
398
|
+
#
|
399
|
+
def goto_entry_starting_with fc=nil
|
400
|
+
unless fc
|
401
|
+
fc = get_single "Entries starting with: "
|
402
|
+
#fc = get_char
|
403
|
+
end
|
404
|
+
return if fc.size != 1
|
405
|
+
## this is wrong and duplicates the functionality of /
|
406
|
+
# It shoud go to cursor of item starting with fc
|
407
|
+
$patt = "^#{fc}"
|
408
|
+
end
|
409
|
+
|
410
|
+
|
411
|
+
## take regex from user, to run on files on screen, user can filter file names
|
412
|
+
def enter_regex
|
413
|
+
patt = get_line "Enter (regex) pattern: "
|
414
|
+
#$patt = gets().chomp
|
415
|
+
#$patt = Readline::readline('>', true)
|
416
|
+
$patt = patt
|
417
|
+
return patt
|
418
|
+
end
|
419
|
+
def next_page
|
420
|
+
# FIXME cursor position, take logic from zfm page calc
|
421
|
+
$sta += $pagesize
|
422
|
+
$cursor = $sta if $cursor < $sta
|
423
|
+
end
|
424
|
+
def prev_page
|
425
|
+
# FIXME cursor position, take logic from zfm page calc
|
426
|
+
$sta -= $pagesize
|
427
|
+
$cursor = $sta
|
428
|
+
end
|
429
|
+
def show_marks
|
430
|
+
list = []
|
431
|
+
$bookmarks.each_pair { |k, v| list << " #[fg=yellow, bold]#{k}#[/end] #[fg=green]#{v}#[/end]" }
|
432
|
+
# s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
|
433
|
+
config = {}
|
434
|
+
longestval = $bookmarks.values.max_by(&:length)
|
435
|
+
config[:title] = "Bookmarks"
|
436
|
+
config[:width] = [longestval.length + 5, FFI::NCurses.COLS - 5].min
|
437
|
+
$log.debug "XXX: LONGEST #{longestval}, #{longestval.length}"
|
438
|
+
ch = padpopup list, config
|
439
|
+
return unless ch
|
440
|
+
#$bookmarks.each_pair { |k, v| puts "#{k.ljust(7)} => #{v}" }
|
441
|
+
#puts
|
442
|
+
#print "Enter bookmark to goto: "
|
443
|
+
#ch = get_char
|
444
|
+
goto_bookmark(ch) if ch =~ /^[0-9A-Z]$/
|
445
|
+
end
|
446
|
+
# MENU MAIN -- keep consistent with zfm
|
447
|
+
def main_menu
|
448
|
+
h = {
|
449
|
+
:a => :ack,
|
450
|
+
"/" => :ffind,
|
451
|
+
:l => :locate,
|
452
|
+
:v => :viminfo,
|
453
|
+
:z => :z_interface,
|
454
|
+
:d => :child_dirs,
|
455
|
+
:r => :recent_files,
|
456
|
+
:t => :dirtree,
|
457
|
+
"4" => :tree,
|
458
|
+
:s => :sort_menu,
|
459
|
+
:F => :filter_menu,
|
460
|
+
:c => :command_menu ,
|
461
|
+
:B => :bindkey_ext_command,
|
462
|
+
:M => :newdir,
|
463
|
+
"%" => :newfile,
|
464
|
+
:x => :extras
|
465
|
+
}
|
466
|
+
menu "Main Menu", h
|
467
|
+
end
|
468
|
+
|
469
|
+
def toggle_menu
|
470
|
+
h = { :h => :toggle_hidden, :c => :toggle_case, :l => :toggle_long_list , "1" => :toggle_columns,
|
471
|
+
:p => :toggle_pager_mode, :e => :toggle_enhanced_list}
|
472
|
+
ch, menu_text = menu "Toggle Menu", h
|
473
|
+
case menu_text
|
474
|
+
when :toggle_hidden
|
475
|
+
#$hidden = $hidden ? nil : "D"
|
476
|
+
$hidden = !$hidden
|
477
|
+
c_refresh
|
478
|
+
when :toggle_case
|
479
|
+
#$ignorecase = $ignorecase ? "" : "i"
|
480
|
+
$ignorecase = !$ignorecase
|
481
|
+
c_refresh
|
482
|
+
when :toggle_columns
|
483
|
+
$gviscols = 3 if $gviscols == 1
|
484
|
+
#$long_listing = false if $gviscols > 1
|
485
|
+
x = $grows * $gviscols
|
486
|
+
$pagesize = $pagesize==x ? $grows : x
|
487
|
+
when :toggle_pager_mode
|
488
|
+
$editor_mode = !$editor_mode
|
489
|
+
if $editor_mode
|
490
|
+
$default_command = nil
|
491
|
+
else
|
492
|
+
$default_command = ENV['MANPAGER'] || ENV['PAGER']
|
493
|
+
end
|
494
|
+
when :toggle_enhanced_list
|
495
|
+
$enhanced_mode = !$enhanced_mode
|
496
|
+
|
497
|
+
when :toggle_long_list
|
498
|
+
$long_listing = !$long_listing
|
499
|
+
if $long_listing
|
500
|
+
$gviscols = 1
|
501
|
+
$pagesize = $grows
|
502
|
+
else
|
503
|
+
x = $grows * $gviscols
|
504
|
+
$pagesize = $pagesize==x ? $grows : x
|
505
|
+
end
|
506
|
+
c_refresh
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
def sort_menu
|
511
|
+
lo = nil
|
512
|
+
h = { :n => :newest, :a => :accessed, :o => :oldest,
|
513
|
+
:l => :largest, :s => :smallest , :m => :name , :r => :rname, :d => :dirs, :c => :clear }
|
514
|
+
ch, menu_text = menu "Sort Menu", h
|
515
|
+
case menu_text
|
516
|
+
when :newest
|
517
|
+
lo="om"
|
518
|
+
when :accessed
|
519
|
+
lo="oa"
|
520
|
+
when :oldest
|
521
|
+
lo="Om"
|
522
|
+
when :largest
|
523
|
+
lo="OL"
|
524
|
+
when :smallest
|
525
|
+
lo="oL"
|
526
|
+
when :name
|
527
|
+
lo="on"
|
528
|
+
when :rname
|
529
|
+
lo="On"
|
530
|
+
when :dirs
|
531
|
+
lo="/"
|
532
|
+
when :clear
|
533
|
+
lo=""
|
534
|
+
end
|
535
|
+
## This needs to persist and be a part of all listings, put in change_dir.
|
536
|
+
$sorto = lo
|
537
|
+
$files = `zsh -c 'print -rl -- *(#{lo}#{$hidden}M)'`.split("\n") if lo
|
538
|
+
$title = nil
|
539
|
+
#$files =$(eval "print -rl -- ${pattern}(${MFM_LISTORDER}$filterstr)")
|
540
|
+
end
|
541
|
+
|
542
|
+
def command_menu
|
543
|
+
##
|
544
|
+
# since these involve full paths, we need more space, like only one column
|
545
|
+
#
|
546
|
+
## in these cases, getting back to the earlier dir, back to earlier listing
|
547
|
+
# since we've basically overlaid the old listing
|
548
|
+
#
|
549
|
+
# should be able to sort THIS listing and not rerun command. But for that I'd need to use
|
550
|
+
# xargs ls -t etc rather than the zsh sort order. But we can run a filter using |.
|
551
|
+
#
|
552
|
+
h = { :t => :today, :D => :default_command , :R => :remove_from_list}
|
553
|
+
if $editor_mode
|
554
|
+
h[:e] = :pager_mode
|
555
|
+
else
|
556
|
+
h[:e] = :editor_mode
|
557
|
+
end
|
558
|
+
ch, menu_text = menu "Command Menu", h
|
559
|
+
case menu_text
|
560
|
+
when :pager_mode
|
561
|
+
$editor_mode = false
|
562
|
+
$default_command = ENV['MANPAGER'] || ENV['PAGER']
|
563
|
+
when :editor_mode
|
564
|
+
$editor_mode = true
|
565
|
+
$default_command = nil
|
566
|
+
when :ffind
|
567
|
+
ffind
|
568
|
+
when :locate
|
569
|
+
locate
|
570
|
+
when :today
|
571
|
+
$files = `zsh -c 'print -rl -- *(#{$hidden}Mm0)'`.split("\n")
|
572
|
+
$title = "Today's files"
|
573
|
+
when :default_command
|
574
|
+
print "Selecting a file usually invokes $EDITOR, what command do you want to use repeatedly on selected files: "
|
575
|
+
$default_command = gets().chomp
|
576
|
+
if $default_command != ""
|
577
|
+
print "Second part of command (maybe blank): "
|
578
|
+
$default_command2 = gets().chomp
|
579
|
+
else
|
580
|
+
print "Cleared default command, will default to $EDITOR"
|
581
|
+
$default_command2 = nil
|
582
|
+
$default_command = nil
|
583
|
+
end
|
584
|
+
end
|
585
|
+
end
|
586
|
+
def extras
|
587
|
+
h = { "1" => :one_column, "2" => :multi_column, :c => :columns, :r => :config_read , :w => :config_write}
|
588
|
+
ch, menu_text = menu "Extras Menu", h
|
589
|
+
case menu_text
|
590
|
+
when :one_column
|
591
|
+
$pagesize = $grows
|
592
|
+
when :multi_column
|
593
|
+
#$pagesize = 60
|
594
|
+
$pagesize = $grows * $gviscols
|
595
|
+
when :columns
|
596
|
+
ch = get_single "How many columns to show: 1-6 [current #{$gviscols}]? "
|
597
|
+
#ch = get_char
|
598
|
+
ch = ch.to_i
|
599
|
+
if ch > 0 && ch < 7
|
600
|
+
$gviscols = ch.to_i
|
601
|
+
$pagesize = $grows * $gviscols
|
602
|
+
end
|
603
|
+
end
|
604
|
+
end
|
605
|
+
def filter_menu
|
606
|
+
h = { :d => :dirs, :f => :files, :e => :emptydirs , "0" => :emptyfiles}
|
607
|
+
ch, menu_text = menu "Filter Menu", h
|
608
|
+
files = nil
|
609
|
+
case menu_text
|
610
|
+
when :dirs
|
611
|
+
$filterstr = "/M"
|
612
|
+
files = `zsh -c 'print -rl -- *(#{$sorto}/M)'`.split("\n")
|
613
|
+
$title = "Filter: directories only"
|
614
|
+
when :files
|
615
|
+
$filterstr = "."
|
616
|
+
files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}.)'`.split("\n")
|
617
|
+
$title = "Filter: files only"
|
618
|
+
when :emptydirs
|
619
|
+
$filterstr = "/D^F"
|
620
|
+
files = `zsh -c 'print -rl -- *(#{$sorto}/D^F)'`.split("\n")
|
621
|
+
$title = "Filter: empty directories"
|
622
|
+
when :emptyfiles
|
623
|
+
$filterstr = ".L0"
|
624
|
+
files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}.L0)'`.split("\n")
|
625
|
+
$title = "Filter: empty files"
|
626
|
+
end
|
627
|
+
if files
|
628
|
+
$files = files
|
629
|
+
show_list
|
630
|
+
$stact = 0
|
631
|
+
end
|
632
|
+
end
|
633
|
+
def select_used_dirs
|
634
|
+
$title = "Used Directories"
|
635
|
+
$files = $used_dirs.uniq
|
636
|
+
#show_list
|
637
|
+
end
|
638
|
+
def select_visited_files
|
639
|
+
# not yet a unique list, needs to be unique and have latest pushed to top
|
640
|
+
$title = "Visited Files"
|
641
|
+
files = $visited_files.uniq
|
642
|
+
show_list files
|
643
|
+
$title = nil
|
644
|
+
end
|
645
|
+
def select_bookmarks
|
646
|
+
$title = "Bookmarks"
|
647
|
+
$files = $bookmarks.values.collect do |x|
|
648
|
+
if x.include? ":"
|
649
|
+
ix = x.index ":"
|
650
|
+
x[0,ix]
|
651
|
+
else
|
652
|
+
x
|
653
|
+
end
|
654
|
+
end
|
655
|
+
#show_list files
|
656
|
+
end
|
657
|
+
|
658
|
+
## part copied and changed from change_dir since we don't dir going back on top
|
659
|
+
# or we'll be stuck in a cycle
|
660
|
+
def pop_dir
|
661
|
+
# the first time we pop, we need to put the current on stack
|
662
|
+
if !$visited_dirs.index(Dir.pwd)
|
663
|
+
$visited_dirs.push Dir.pwd
|
664
|
+
end
|
665
|
+
## XXX make sure thre is something to pop
|
666
|
+
d = $visited_dirs.delete_at 0
|
667
|
+
## XXX make sure the dir exists, cuold have been deleted. can be an error or crash otherwise
|
668
|
+
$visited_dirs.push d
|
669
|
+
Dir.chdir d
|
670
|
+
display_dir
|
671
|
+
|
672
|
+
return
|
673
|
+
# old stuff with zsh
|
674
|
+
$filterstr ||= "M"
|
675
|
+
$files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
|
676
|
+
post_cd
|
677
|
+
end
|
678
|
+
# TODO
|
679
|
+
def TODOpost_cd
|
680
|
+
$patt=nil
|
681
|
+
$sta = $cursor = 0
|
682
|
+
$title = nil
|
683
|
+
if $selected_files.size > 0
|
684
|
+
$selected_files = []
|
685
|
+
end
|
686
|
+
$visual_block_start = nil
|
687
|
+
$stact = 0
|
688
|
+
screen_settings
|
689
|
+
# i think this will screw with the dir_pos since it is not filename based.
|
690
|
+
enhance_file_list
|
691
|
+
revert_dir_pos
|
692
|
+
end
|
693
|
+
#
|
694
|
+
## read dirs and files and bookmarks from file
|
695
|
+
def config_read
|
696
|
+
#f = File.expand_path("~/.zfminfo")
|
697
|
+
f = File.expand_path(CONFIG_FILE)
|
698
|
+
if File.readable? f
|
699
|
+
load f
|
700
|
+
# maybe we should check for these existing else crash will happen.
|
701
|
+
$used_dirs.push(*(DIRS.split ":"))
|
702
|
+
$used_dirs.concat get_env_paths
|
703
|
+
$visited_files.push(*(FILES.split ":"))
|
704
|
+
#$bookmarks.push(*bookmarks) if bookmarks
|
705
|
+
chars = ('A'..'Z').to_a
|
706
|
+
chars.concat( ('0'..'9').to_a )
|
707
|
+
chars.each do |ch|
|
708
|
+
if Kernel.const_defined? "BM_#{ch}"
|
709
|
+
$bookmarks[ch] = Kernel.const_get "BM_#{ch}"
|
710
|
+
end
|
711
|
+
end
|
712
|
+
end
|
713
|
+
end
|
714
|
+
def get_env_paths
|
715
|
+
files = []
|
716
|
+
%w{ GEM_HOME PYTHONHOME}.each do |p|
|
717
|
+
d = ENV[p]
|
718
|
+
files.push d if d
|
719
|
+
end
|
720
|
+
%w{ RUBYLIB RUBYPATH GEM_PATH PYTHONPATH }.each do |p|
|
721
|
+
d = ENV[p]
|
722
|
+
files.concat d.split(":") if d
|
723
|
+
end
|
724
|
+
return files
|
725
|
+
end
|
726
|
+
|
727
|
+
## save dirs and files and bookmarks to a file
|
728
|
+
def config_write
|
729
|
+
# Putting it in a format that zfm can also read and write
|
730
|
+
f1 = File.expand_path("~/.cygnusinfo")
|
731
|
+
#f1 = File.expand_path(CONFIG_FILE)
|
732
|
+
d = $used_dirs.join ":"
|
733
|
+
f = $visited_files.join ":"
|
734
|
+
File.open(f1, 'w+') do |f2|
|
735
|
+
# use "\n" for two lines of text
|
736
|
+
f2.puts "DIRS=\"#{d}\""
|
737
|
+
f2.puts "FILES=\"#{f}\""
|
738
|
+
$bookmarks.each_pair { |k, val|
|
739
|
+
f2.puts "BM_#{k}=\"#{val}\""
|
740
|
+
#f2.puts "BOOKMARKS[\"#{k}\"]=\"#{val}\""
|
741
|
+
}
|
742
|
+
end
|
743
|
+
$writing = $modified = false
|
744
|
+
end
|
745
|
+
|
746
|
+
## accept a character to save this dir as a bookmark
|
747
|
+
def create_bookmark
|
748
|
+
ch = get_single "Enter A to Z or 0-9 for bookmark: "
|
749
|
+
#ch = get_char
|
750
|
+
if ch =~ /^[0-9A-Z]$/
|
751
|
+
#$bookmarks[ch] = "#{Dir.pwd}:#{$cursor}"
|
752
|
+
#
|
753
|
+
# The significance of putting a : and not a / is that with a
|
754
|
+
# : the dir will be opened with cursor on same object it was on, and not
|
755
|
+
# go into the dir. e.g, If bookmark is created with cursor on a dir, we don't want
|
756
|
+
# it to go into the dir.
|
757
|
+
$bookmarks[ch] = "#{Dir.pwd}:#{$view[$cursor]}"
|
758
|
+
$modified = true
|
759
|
+
else
|
760
|
+
perror "Bookmark must be upper-case character or number."
|
761
|
+
end
|
762
|
+
end
|
763
|
+
def subcommand
|
764
|
+
begin
|
765
|
+
command = get_line "Enter command: "
|
766
|
+
#command = gets().chomp
|
767
|
+
#command = Readline::readline('>', true)
|
768
|
+
return if command == ""
|
769
|
+
rescue Exception => ex
|
770
|
+
return
|
771
|
+
end
|
772
|
+
if command == "q"
|
773
|
+
if $modified
|
774
|
+
ch = get_single "Do you want to save bookmarks? (y/n): "
|
775
|
+
#ch = get_char
|
776
|
+
if ch == "y"
|
777
|
+
$writing = true
|
778
|
+
$quitting = true
|
779
|
+
elsif ch == "n"
|
780
|
+
$quitting = true
|
781
|
+
print "Quitting without saving bookmarks"
|
782
|
+
else
|
783
|
+
perror "No action taken."
|
784
|
+
end
|
785
|
+
else
|
786
|
+
$quitting = true
|
787
|
+
end
|
788
|
+
elsif command == "wq"
|
789
|
+
$quitting = true
|
790
|
+
$writing = true
|
791
|
+
elsif command == "x"
|
792
|
+
$quitting = true
|
793
|
+
$writing = true if $modified
|
794
|
+
elsif command == "p"
|
795
|
+
system "echo $PWD | pbcopy"
|
796
|
+
get_single "Stored PWD in clipboard (using pbcopy)"
|
797
|
+
end
|
798
|
+
end
|
799
|
+
def quit_command
|
800
|
+
if $modified
|
801
|
+
s = ""
|
802
|
+
s << "Press w to save bookmarks before quitting. " if $modified
|
803
|
+
s << "Press another q to quit "
|
804
|
+
ch = get_single s
|
805
|
+
#ch = get_char
|
806
|
+
else
|
807
|
+
$quitting = true
|
808
|
+
end
|
809
|
+
$quitting = true if ch == "q"
|
810
|
+
$quitting = $writing = true if ch == "w"
|
811
|
+
end
|
812
|
+
|
813
|
+
def views
|
814
|
+
views=%w[/ om oa Om OL oL On on]
|
815
|
+
viewlabels=%w[Dirs Newest Accessed Oldest Largest Smallest Reverse Name]
|
816
|
+
$sorto = views[$viewctr]
|
817
|
+
$title = viewlabels[$viewctr]
|
818
|
+
$viewctr += 1
|
819
|
+
$viewctr = 0 if $viewctr > views.size
|
820
|
+
|
821
|
+
$files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}M)'`.split("\n")
|
822
|
+
|
823
|
+
end
|
824
|
+
def child_dirs
|
825
|
+
$title = "Child directories"
|
826
|
+
$files = `zsh -c 'print -rl -- *(/#{$sorto}#{$hidden}M)'`.split("\n")
|
827
|
+
end
|
828
|
+
def dirtree
|
829
|
+
$title = "Child directories"
|
830
|
+
$files = `zsh -c 'print -rl -- **/*(/#{$sorto}#{$hidden}M)'`.split("\n")
|
831
|
+
end
|
832
|
+
#
|
833
|
+
# Get a full recursive listing of what's in this dir - useful for small projects with more
|
834
|
+
# structure than files.
|
835
|
+
def tree
|
836
|
+
# Caution: use only for small projects, don't use in root.
|
837
|
+
$title = "Full Tree"
|
838
|
+
$files = `zsh -c 'print -rl -- **/*(#{$sorto}#{$hidden}M)'`.split("\n")
|
839
|
+
end
|
840
|
+
def recent_files
|
841
|
+
# print -rl -- **/*(Dom[1,10])
|
842
|
+
$title = "Recent files"
|
843
|
+
$files = `zsh -c 'print -rl -- **/*(Dom[1,15])'`.split("\n")
|
844
|
+
end
|
845
|
+
def select_current
|
846
|
+
## vp is local there, so i can do $vp[0]
|
847
|
+
#open_file $view[$sta] if $view[$sta]
|
848
|
+
open_file $view[$cursor] if $view[$cursor]
|
849
|
+
end
|
850
|
+
|
851
|
+
## create a list of dirs in which some action has happened, for saving
|
852
|
+
def push_used_dirs d=Dir.pwd
|
853
|
+
$used_dirs.index(d) || $used_dirs.push(d)
|
854
|
+
end
|
855
|
+
## I thin we need to make this like the command line one TODO
|
856
|
+
def get_char
|
857
|
+
c = @window.getchar
|
858
|
+
case c
|
859
|
+
when 13,10
|
860
|
+
return "ENTER"
|
861
|
+
when 32
|
862
|
+
return "SPACE"
|
863
|
+
when 127
|
864
|
+
return "BACKSPACE"
|
865
|
+
when 27
|
866
|
+
return "ESCAPE"
|
867
|
+
end
|
868
|
+
keycode_tos c
|
869
|
+
# if c > 32 && c < 127
|
870
|
+
#return c.chr
|
871
|
+
#end
|
872
|
+
## use keycode_tos from Utils.
|
873
|
+
end
|
874
|
+
|
875
|
+
def pbold text
|
876
|
+
#puts "#{BOLD}#{text}#{BOLD_OFF}"
|
877
|
+
get_single text, :color_pair => $reversecolor
|
878
|
+
end
|
879
|
+
def perror text
|
880
|
+
##puts "#{RED}#{text}#{CLEAR}"
|
881
|
+
#get_char
|
882
|
+
#alert text
|
883
|
+
get_single text + " Press a key...", :color_pair => $errorcolor
|
884
|
+
end
|
885
|
+
def pause text=" Press a key ..."
|
886
|
+
get_single text
|
887
|
+
#get_char
|
888
|
+
end
|
889
|
+
## return shortcut for an index (offset in file array)
|
890
|
+
# use 2 more arrays to make this faster
|
891
|
+
# if z or Z take another key if there are those many in view
|
892
|
+
# Also, display ROWS * COLS so now we are not limited to 60.
|
893
|
+
def get_shortcut ix
|
894
|
+
return "<" if ix < $stact
|
895
|
+
ix -= $stact
|
896
|
+
i = $IDX[ix]
|
897
|
+
return i if i
|
898
|
+
return "->"
|
899
|
+
end
|
900
|
+
## returns the integer offset in view (file array based on a-y za-zz and Za - Zz
|
901
|
+
# Called when user types a key
|
902
|
+
# should we even ask for a second key if there are not enough rows
|
903
|
+
# What if we want to also trap z with numbers for other purposes
|
904
|
+
def get_index key, vsz=999
|
905
|
+
i = $IDX.index(key)
|
906
|
+
return i+$stact if i
|
907
|
+
#sz = $IDX.size
|
908
|
+
zch = nil
|
909
|
+
if vsz > 25
|
910
|
+
if key == "z" || key == "Z"
|
911
|
+
#print key
|
912
|
+
zch = get_char
|
913
|
+
#print zch
|
914
|
+
i = $IDX.index("#{key}#{zch}")
|
915
|
+
return i+$stact if i
|
916
|
+
end
|
917
|
+
end
|
918
|
+
return nil
|
919
|
+
end
|
920
|
+
|
921
|
+
def delete_file
|
922
|
+
file_actions :delete
|
923
|
+
end
|
924
|
+
|
925
|
+
## generic external command program
|
926
|
+
# prompt is the user friendly text of command such as list for ls, or extract for dtrx, page for less
|
927
|
+
# pauseyn is whether to pause after command as in file or ls
|
928
|
+
#
|
929
|
+
def command_file prompt, *command
|
930
|
+
pauseyn = command.shift
|
931
|
+
command = command.join " "
|
932
|
+
#print "[#{prompt}] Choose a file [#{$view[$cursor]}]: "
|
933
|
+
t = "[#{prompt}] Choose a file [#{$view[$cursor]}]: "
|
934
|
+
file = ask_hint t, $view[$cursor]
|
935
|
+
#print "#{prompt} :: Enter file shortcut: "
|
936
|
+
#file = ask_hint
|
937
|
+
perror "Command Cancelled" unless file
|
938
|
+
return unless file
|
939
|
+
file = File.expand_path(file)
|
940
|
+
if File.exists? file
|
941
|
+
file = Shellwords.escape(file)
|
942
|
+
pbold "#{command} #{file} (#{pauseyn})"
|
943
|
+
system "#{command} #{file}"
|
944
|
+
pause if pauseyn == "y"
|
945
|
+
c_refresh
|
946
|
+
else
|
947
|
+
perror "File #{file} not found"
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
951
|
+
## prompt user for file shortcut and return file or nil
|
952
|
+
#
|
953
|
+
def ask_hint text, deflt=nil
|
954
|
+
f = nil
|
955
|
+
|
956
|
+
#ch = get_char
|
957
|
+
ch = get_single text
|
958
|
+
if ch == "ENTER"
|
959
|
+
return deflt
|
960
|
+
end
|
961
|
+
ix = get_index(ch, $viewport.size)
|
962
|
+
f = $viewport[ix] if ix
|
963
|
+
return f
|
964
|
+
end
|
965
|
+
|
966
|
+
## check screen size and accordingly adjust some variables
|
967
|
+
#
|
968
|
+
def screen_settings
|
969
|
+
# TODO these need to become part of our new full_indexer class, not hang about separately.
|
970
|
+
$glines=%x(tput lines).to_i
|
971
|
+
$gcols=%x(tput cols).to_i
|
972
|
+
# this depends now on textpad size not screen size TODO FIXME
|
973
|
+
$grows = $glines - 1
|
974
|
+
$pagesize = 60
|
975
|
+
#$gviscols = 3
|
976
|
+
$pagesize = $grows * $gviscols
|
977
|
+
end
|
978
|
+
## moves column offset so we can reach unindexed columns or entries
|
979
|
+
# 0 forward and any other back/prev
|
980
|
+
def column_next dir=0
|
981
|
+
if dir == 0
|
982
|
+
$stact += $grows
|
983
|
+
$stact = 0 if $stact >= $viewport.size
|
984
|
+
else
|
985
|
+
$stact -= $grows
|
986
|
+
$stact = 0 if $stact < 0
|
987
|
+
end
|
988
|
+
end
|
989
|
+
# currently i am only passing the action in from the list there as a key
|
990
|
+
# I should be able to pass in new actions that are external commands
|
991
|
+
def file_actions action=nil
|
992
|
+
h = { :d => :delete, :m => :move, :r => :rename, :v => ENV["EDITOR"] || :vim,
|
993
|
+
:c => :copy, :C => :chdir,
|
994
|
+
:l => :less, :s => :most , :f => :file , :o => :open, :x => :dtrx, :z => :zip }
|
995
|
+
#acttext = h[action.to_sym] || action
|
996
|
+
acttext = action || ""
|
997
|
+
file = nil
|
998
|
+
|
999
|
+
sct = $selected_files.size
|
1000
|
+
if sct > 0
|
1001
|
+
text = "#{sct} files"
|
1002
|
+
file = $selected_files
|
1003
|
+
else
|
1004
|
+
#print "[#{acttext}] Choose a file [#{$view[$cursor]}]: "
|
1005
|
+
t = "[#{acttext}] Choose a file [#{$view[$cursor]}]: "
|
1006
|
+
file = ask_hint t, $view[$cursor]
|
1007
|
+
return unless file
|
1008
|
+
text = file
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
case file
|
1012
|
+
when Array
|
1013
|
+
# escape the contents and create a string
|
1014
|
+
files = Shellwords.join(file)
|
1015
|
+
when String
|
1016
|
+
files = Shellwords.escape(file)
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
|
1020
|
+
ch = nil
|
1021
|
+
if action
|
1022
|
+
menu_text = action
|
1023
|
+
else
|
1024
|
+
ch, menu_text = menu "File Menu for #{text}", h
|
1025
|
+
menu_text = :quit if ch == "q"
|
1026
|
+
end
|
1027
|
+
return unless menu_text
|
1028
|
+
case menu_text.to_sym
|
1029
|
+
when :quit
|
1030
|
+
when :delete
|
1031
|
+
ch = get_single "rmtrash #{files} ?[yn]: "
|
1032
|
+
#print "rmtrash #{files} ?[yn]: "
|
1033
|
+
#ch = get_char
|
1034
|
+
return if ch != "y"
|
1035
|
+
c_system "rmtrash #{files}"
|
1036
|
+
c_refresh
|
1037
|
+
when :move
|
1038
|
+
#print "move #{text} to : "
|
1039
|
+
#target = gets().chomp
|
1040
|
+
#target = Readline::readline('>', true)
|
1041
|
+
target = get_line "move #{text} to : "
|
1042
|
+
text=File.expand_path(text)
|
1043
|
+
return if target.nil? || target == ""
|
1044
|
+
if File.directory? target
|
1045
|
+
FileUtils.mv text, target
|
1046
|
+
c_refresh
|
1047
|
+
else
|
1048
|
+
perror "Target not a dir"
|
1049
|
+
end
|
1050
|
+
when :copy
|
1051
|
+
target = get_line "copy #{text} to : "
|
1052
|
+
#target = Readline::readline('>', true)
|
1053
|
+
return if target.nil? || target == ""
|
1054
|
+
text=File.expand_path(text)
|
1055
|
+
target = File.basename(text) if target == "."
|
1056
|
+
if File.exists? target
|
1057
|
+
perror "Target (#{target}) exists"
|
1058
|
+
else
|
1059
|
+
FileUtils.cp text, target
|
1060
|
+
c_refresh
|
1061
|
+
end
|
1062
|
+
when :chdir
|
1063
|
+
change_dir File.dirname(text)
|
1064
|
+
when :zip
|
1065
|
+
target = get_line "Archive name: "
|
1066
|
+
#target = gets().chomp
|
1067
|
+
#target = Readline::readline('>', true)
|
1068
|
+
return if target.nil? || target == ""
|
1069
|
+
# don't want a blank space or something screwing up
|
1070
|
+
if target && target.size > 3
|
1071
|
+
if File.exists? target
|
1072
|
+
perror "Target (#{target}) exists"
|
1073
|
+
else
|
1074
|
+
c_system "tar zcvf #{target} #{files}"
|
1075
|
+
c_refresh
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
when :rename
|
1079
|
+
when :most, :less, :vim, ENV['EDITOR']
|
1080
|
+
c_system "#{menu_text} #{files}"
|
1081
|
+
else
|
1082
|
+
return unless menu_text
|
1083
|
+
$log.debug "XXX: menu_text #{menu_text.to_sym}"
|
1084
|
+
get_single "#{menu_text} #{files}"
|
1085
|
+
#pause
|
1086
|
+
#print
|
1087
|
+
c_system "#{menu_text} #{files}"
|
1088
|
+
pause
|
1089
|
+
c_refresh
|
1090
|
+
end
|
1091
|
+
# remove non-existent files from select list due to move or delete or rename or whatever
|
1092
|
+
if sct > 0
|
1093
|
+
$selected_files.reject! {|x| x = File.expand_path(x); !File.exists?(x) }
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
def columns_incdec howmany
|
1098
|
+
$gviscols += howmany.to_i
|
1099
|
+
$gviscols = 1 if $gviscols < 1
|
1100
|
+
$gviscols = 6 if $gviscols > 6
|
1101
|
+
$pagesize = $grows * $gviscols
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
# bind a key to an external command wich can be then be used for files
|
1105
|
+
def bindkey_ext_command
|
1106
|
+
#print
|
1107
|
+
#pbold "Bind a capital letter to an external command"
|
1108
|
+
ch = get_single "Enter a capital letter to bind: "
|
1109
|
+
#ch = get_char
|
1110
|
+
return if ch == "Q"
|
1111
|
+
if ch =~ /^[A-Z]$/
|
1112
|
+
print "Enter an external command to bind to #{ch}: "
|
1113
|
+
com = gets().chomp
|
1114
|
+
if com != ""
|
1115
|
+
print "Enter prompt for command (blank if same as command): "
|
1116
|
+
pro = gets().chomp
|
1117
|
+
pro = com if pro == ""
|
1118
|
+
end
|
1119
|
+
print "Pause after output [y/n]: "
|
1120
|
+
yn = get_char
|
1121
|
+
$bindings[ch] = "command_file #{pro} #{yn} #{com}"
|
1122
|
+
end
|
1123
|
+
end
|
1124
|
+
def viminfo
|
1125
|
+
file = File.expand_path("~/.viminfo")
|
1126
|
+
if File.exists? file
|
1127
|
+
$title = "Files from ~/.viminfo"
|
1128
|
+
#$files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- | sed "s#~#$HOME#g"`.split("\n")
|
1129
|
+
$files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- `.split("\n")
|
1130
|
+
$files.reject! {|x| x = File.expand_path(x); !File.exists?(x) }
|
1131
|
+
show_list
|
1132
|
+
end
|
1133
|
+
end
|
1134
|
+
def z_interface
|
1135
|
+
file = File.expand_path("~/.z")
|
1136
|
+
if File.exists? file
|
1137
|
+
$title = "Directories from ~/.z"
|
1138
|
+
$files = `sort -rn -k2 -t '|' ~/.z | cut -f1 -d '|'`.split("\n")
|
1139
|
+
home = ENV['HOME']
|
1140
|
+
$files.collect! do |f|
|
1141
|
+
f.sub(/#{home}/,"~")
|
1142
|
+
end
|
1143
|
+
show_list
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
def ack
|
1147
|
+
pattern = get_line "Enter a pattern to search (ack): "
|
1148
|
+
return if pattern.nil? || pattern == ""
|
1149
|
+
$title = "Files found using 'ack' #{pattern}"
|
1150
|
+
#system("ack #{pattern}")
|
1151
|
+
#pause
|
1152
|
+
files = `ack -l #{pattern}`.split("\n")
|
1153
|
+
if files.size == 0
|
1154
|
+
perror "No files found."
|
1155
|
+
else
|
1156
|
+
$files = files
|
1157
|
+
show_list
|
1158
|
+
end
|
1159
|
+
end
|
1160
|
+
def ffind
|
1161
|
+
pattern = get_line "Enter a file name pattern to find: "
|
1162
|
+
return if pattern.nil? || pattern == ""
|
1163
|
+
$title = "Files found using 'find' #{pattern}"
|
1164
|
+
files = `find . -name '#{pattern}'`.split("\n")
|
1165
|
+
if files.size == 0
|
1166
|
+
perror "No files found."
|
1167
|
+
else
|
1168
|
+
$files = files
|
1169
|
+
show_list
|
1170
|
+
end
|
1171
|
+
end
|
1172
|
+
def locate
|
1173
|
+
pattern = get_line "Enter a file name pattern to locate: "
|
1174
|
+
return if pattern.nil? || pattern == ""
|
1175
|
+
$title = "Files found using 'locate' #{pattern}"
|
1176
|
+
files = `locate #{pattern}`.split("\n")
|
1177
|
+
files.reject! {|x| x = File.expand_path(x); !File.exists?(x) }
|
1178
|
+
if files.size == 0
|
1179
|
+
perror "No files found."
|
1180
|
+
else
|
1181
|
+
$files = files
|
1182
|
+
show_list
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
## Displays files from .viminfo file, if you use some other editor which tracks files opened
|
1187
|
+
# then you can modify this accordingly.
|
1188
|
+
#
|
1189
|
+
|
1190
|
+
## takes directories from the z program, if you use autojump you can
|
1191
|
+
# modify this accordingly
|
1192
|
+
#
|
1193
|
+
|
1194
|
+
## some cursor movement functions
|
1195
|
+
##
|
1196
|
+
#
|
1197
|
+
def cursor_scroll_dn
|
1198
|
+
moveto(pos() + MSCROLL)
|
1199
|
+
end
|
1200
|
+
def cursor_scroll_up
|
1201
|
+
moveto(pos() - MSCROLL)
|
1202
|
+
end
|
1203
|
+
def cursor_dn
|
1204
|
+
moveto(pos() + 1)
|
1205
|
+
end
|
1206
|
+
def cursor_up
|
1207
|
+
moveto(pos() - 1)
|
1208
|
+
end
|
1209
|
+
def pos
|
1210
|
+
$cursor
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
def moveto pos
|
1214
|
+
orig = $cursor
|
1215
|
+
$cursor = pos
|
1216
|
+
$cursor = [$cursor, $view.size - 1].min
|
1217
|
+
$cursor = [$cursor, 0].max
|
1218
|
+
star = [orig, $cursor].min
|
1219
|
+
fin = [orig, $cursor].max
|
1220
|
+
if $visual_mode
|
1221
|
+
# PWD has to be there in selction
|
1222
|
+
if $selected_files.index $view[$cursor]
|
1223
|
+
# this depends on the direction
|
1224
|
+
$selected_files = $selected_files - $view[star..fin]
|
1225
|
+
## current row remains in selection always.
|
1226
|
+
$selected_files.push $view[$cursor]
|
1227
|
+
else
|
1228
|
+
$selected_files.concat $view[star..fin]
|
1229
|
+
end
|
1230
|
+
end
|
1231
|
+
end
|
1232
|
+
def visual_mode_toggle
|
1233
|
+
$visual_mode = !$visual_mode
|
1234
|
+
if $visual_mode
|
1235
|
+
$visual_block_start = $cursor
|
1236
|
+
$selected_files.push $view[$cursor]
|
1237
|
+
end
|
1238
|
+
end
|
1239
|
+
def visual_block_clear
|
1240
|
+
if $visual_block_start
|
1241
|
+
star = [$visual_block_start, $cursor].min
|
1242
|
+
fin = [$visual_block_start, $cursor].max
|
1243
|
+
$selected_files = $selected_files - $view[star..fin]
|
1244
|
+
end
|
1245
|
+
$visual_block_start = nil
|
1246
|
+
$visual_mode = nil
|
1247
|
+
end
|
1248
|
+
def file_matching? file, patt
|
1249
|
+
file =~ /#{patt}/
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
## generic method to take cursor to next position for a given condition
|
1253
|
+
def return_next_match binding, *args
|
1254
|
+
first = nil
|
1255
|
+
ix = 0
|
1256
|
+
$view.each_with_index do |elem,ii|
|
1257
|
+
if binding.call(elem, *args)
|
1258
|
+
first ||= ii
|
1259
|
+
if ii > $cursor
|
1260
|
+
ix = ii
|
1261
|
+
break
|
1262
|
+
end
|
1263
|
+
end
|
1264
|
+
end
|
1265
|
+
return first if ix == 0
|
1266
|
+
return ix
|
1267
|
+
end
|
1268
|
+
##
|
1269
|
+
# position cursor on a specific line which could be on a nother page
|
1270
|
+
# therefore calculate the correct start offset of the display also.
|
1271
|
+
def goto_line pos
|
1272
|
+
pages = ((pos * 1.00)/$pagesize).ceil
|
1273
|
+
pages -= 1
|
1274
|
+
#$sta = pages * $pagesize + 1
|
1275
|
+
$sta = pages * $pagesize + 0
|
1276
|
+
$cursor = pos
|
1277
|
+
#$log.debug "XXX: GOTO_LINE #{$sta} :: #{$cursor}"
|
1278
|
+
end
|
1279
|
+
def filetype f
|
1280
|
+
return nil unless f
|
1281
|
+
f = Shellwords.escape(f)
|
1282
|
+
s = `file #{f}`
|
1283
|
+
if s.index "text"
|
1284
|
+
return :text
|
1285
|
+
elsif s.index(/[Zz]ip/)
|
1286
|
+
return :zip
|
1287
|
+
elsif s.index("archive")
|
1288
|
+
return :zip
|
1289
|
+
elsif s.index "image"
|
1290
|
+
return :image
|
1291
|
+
elsif s.index "data"
|
1292
|
+
return :text
|
1293
|
+
end
|
1294
|
+
nil
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def save_dir_pos
|
1298
|
+
return if $sta == 0 && $cursor == 0
|
1299
|
+
$dir_position[Dir.pwd] = [$sta, $cursor]
|
1300
|
+
end
|
1301
|
+
def revert_dir_pos
|
1302
|
+
$sta = 0
|
1303
|
+
$cursor = 0
|
1304
|
+
a = $dir_position[Dir.pwd]
|
1305
|
+
if a
|
1306
|
+
$sta = a.first
|
1307
|
+
$cursor = a[1]
|
1308
|
+
raise "sta is nil for #{Dir.pwd} : #{$dir_position[Dir.pwd]}" unless $sta
|
1309
|
+
raise "cursor is nil" unless $cursor
|
1310
|
+
end
|
1311
|
+
end
|
1312
|
+
def newdir
|
1313
|
+
#print
|
1314
|
+
#print "Enter directory name: "
|
1315
|
+
#str = Readline::readline('>', true)
|
1316
|
+
str = get_line "Enter directory name: "
|
1317
|
+
return if str.nil? || str == ""
|
1318
|
+
if File.exists? str
|
1319
|
+
perror "#{str} exists."
|
1320
|
+
return
|
1321
|
+
end
|
1322
|
+
begin
|
1323
|
+
FileUtils.mkdir str
|
1324
|
+
$used_dirs.insert(0, str) if File.exists?(str)
|
1325
|
+
c_refresh
|
1326
|
+
rescue Exception => ex
|
1327
|
+
perror "Error in newdir: #{ex}"
|
1328
|
+
end
|
1329
|
+
end
|
1330
|
+
def newfile
|
1331
|
+
#print
|
1332
|
+
str = get_line "Enter file name: "
|
1333
|
+
#str = Readline::readline('>', true)
|
1334
|
+
return if str.nil? || str == ""
|
1335
|
+
system "$EDITOR #{str}"
|
1336
|
+
$visited_files.insert(0, str) if File.exists?(str)
|
1337
|
+
c_refresh
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
##
|
1341
|
+
# Editing of the User Dir List.
|
1342
|
+
# remove current entry from used dirs list, since we may not want some entries being there
|
1343
|
+
#
|
1344
|
+
def remove_from_list
|
1345
|
+
if $selected_files.size > 0
|
1346
|
+
sz = $selected_files.size
|
1347
|
+
ch = get_single "Remove #{sz} files from used list (y)?: "
|
1348
|
+
#ch = get_char
|
1349
|
+
return if ch != "y"
|
1350
|
+
$used_dirs = $used_dirs - $selected_files
|
1351
|
+
$visited_files = $visited_files - $selected_files
|
1352
|
+
unselect_all
|
1353
|
+
$modified = true
|
1354
|
+
return
|
1355
|
+
end
|
1356
|
+
#print
|
1357
|
+
## what if selected some rows
|
1358
|
+
file = $view[$cursor]
|
1359
|
+
ch = get_single "Remove #{file} from used list (y)?: "
|
1360
|
+
#ch = get_char
|
1361
|
+
return if ch != "y"
|
1362
|
+
file = File.expand_path(file)
|
1363
|
+
if File.directory? file
|
1364
|
+
$used_dirs.delete(file)
|
1365
|
+
else
|
1366
|
+
$visited_files.delete(file)
|
1367
|
+
end
|
1368
|
+
c_refresh
|
1369
|
+
$modified = true
|
1370
|
+
end
|
1371
|
+
#
|
1372
|
+
# If there's a short file list, take recently mod and accessed folders and put latest
|
1373
|
+
# files from there and insert it here. I take both since recent mod can be binaries / object
|
1374
|
+
# files and gems created by a process, and not actually edited files. Recent accessed gives
|
1375
|
+
# latest source, but in some cases even this can be misleading since running a program accesses
|
1376
|
+
# include files.
|
1377
|
+
def enhance_file_list
|
1378
|
+
return unless $enhanced_mode
|
1379
|
+
# if only one entry and its a dir
|
1380
|
+
# get its children and maybe the recent mod files a few
|
1381
|
+
|
1382
|
+
if $files.size == 1
|
1383
|
+
# its a dir, let give the next level at least
|
1384
|
+
if $files.first[-1] == "/"
|
1385
|
+
d = $files.first
|
1386
|
+
f = `zsh -c 'print -rl -- #{d}*(omM)'`.split("\n")
|
1387
|
+
if f && f.size > 0
|
1388
|
+
$files.concat f
|
1389
|
+
return
|
1390
|
+
end
|
1391
|
+
else
|
1392
|
+
# just a file, not dirs here
|
1393
|
+
return
|
1394
|
+
end
|
1395
|
+
end
|
1396
|
+
#
|
1397
|
+
# check if a ruby project dir, although it could be a backup file too,
|
1398
|
+
# if so , expand lib and maby bin, put a couple recent files
|
1399
|
+
#
|
1400
|
+
if $files.index("Gemfile") || $files.grep(/\.gemspec/).size > 0
|
1401
|
+
# usually the lib dir has only one file and one dir
|
1402
|
+
flg = false
|
1403
|
+
if $files.index("lib/")
|
1404
|
+
f = `zsh -c 'print -rl -- lib/*(om[1,5]M)'`.split("\n")
|
1405
|
+
if f && f.size() > 0
|
1406
|
+
insert_into_list("lib/", f)
|
1407
|
+
flg = true
|
1408
|
+
end
|
1409
|
+
dd = File.basename(Dir.pwd)
|
1410
|
+
if f.index("lib/#{dd}/")
|
1411
|
+
f = `zsh -c 'print -rl -- lib/#{dd}/*(om[1,5]M)'`.split("\n")
|
1412
|
+
if f && f.size() > 0
|
1413
|
+
insert_into_list("lib/#{dd}/", f)
|
1414
|
+
flg = true
|
1415
|
+
end
|
1416
|
+
end
|
1417
|
+
end
|
1418
|
+
if $files.index("bin/")
|
1419
|
+
f = `zsh -c 'print -rl -- bin/*(om[1,5]M)'`.split("\n")
|
1420
|
+
insert_into_list("bin/", f) if f && f.size() > 0
|
1421
|
+
flg = true
|
1422
|
+
end
|
1423
|
+
return if flg
|
1424
|
+
|
1425
|
+
# lib has a dir in it with the gem name
|
1426
|
+
|
1427
|
+
end
|
1428
|
+
return if $files.size > 15
|
1429
|
+
|
1430
|
+
## first check accessed else modified will change accessed
|
1431
|
+
moda = `zsh -c 'print -rn -- *(/oa[1]M)'`
|
1432
|
+
if moda && moda != ""
|
1433
|
+
modf = `zsh -c 'print -rn -- #{moda}*(oa[1]M)'`
|
1434
|
+
if modf && modf != ""
|
1435
|
+
insert_into_list moda, modf
|
1436
|
+
end
|
1437
|
+
modm = `zsh -c 'print -rn -- #{moda}*(om[1]M)'`
|
1438
|
+
if modm && modm != "" && modm != modf
|
1439
|
+
insert_into_list moda, modm
|
1440
|
+
end
|
1441
|
+
end
|
1442
|
+
## get last modified dir
|
1443
|
+
modm = `zsh -c 'print -rn -- *(/om[1]M)'`
|
1444
|
+
if modm != moda
|
1445
|
+
modmf = `zsh -c 'print -rn -- #{modm}*(oa[1]M)'`
|
1446
|
+
insert_into_list modm, modmf
|
1447
|
+
modmf1 = `zsh -c 'print -rn -- #{modm}*(om[1]M)'`
|
1448
|
+
insert_into_list(modm, modmf1) if modmf1 != modmf
|
1449
|
+
else
|
1450
|
+
# if both are same then our options get reduced so we need to get something more
|
1451
|
+
# If you access the latest mod dir, then come back you get only one, since mod and accessed
|
1452
|
+
# are the same dir, so we need to find the second modified dir
|
1453
|
+
end
|
1454
|
+
end
|
1455
|
+
def insert_into_list dir, file
|
1456
|
+
ix = $files.index(dir)
|
1457
|
+
raise "something wrong can find #{dir}." unless ix
|
1458
|
+
$files.insert ix, *file
|
1459
|
+
end
|
1460
|
+
#
|
1461
|
+
# prints a prompt at bottom of screen, takes a character and returns textual representation
|
1462
|
+
# of character (as per get_char) and not the int that window.getchar returns.
|
1463
|
+
# It uses a window, so underlying text is not touched.
|
1464
|
+
#
|
1465
|
+
def get_single text, config={}
|
1466
|
+
w = one_line_window
|
1467
|
+
x = y = 0
|
1468
|
+
color = config[:color_pair] || $datacolor
|
1469
|
+
color=Ncurses.COLOR_PAIR(color);
|
1470
|
+
w.attron(color);
|
1471
|
+
w.mvprintw(x, y, "%s" % text);
|
1472
|
+
w.attroff(color);
|
1473
|
+
w.wrefresh
|
1474
|
+
Ncurses::Panel.update_panels
|
1475
|
+
chr = get_char
|
1476
|
+
w.destroy
|
1477
|
+
w = nil
|
1478
|
+
return chr
|
1479
|
+
end
|
1480
|
+
|
1481
|
+
# identical to get_string but does not show as a popup with buttons, just ENTER
|
1482
|
+
# This is required if there are multiple inputs required and having several get_strings
|
1483
|
+
# one after the other seems really odd due to multiple popups
|
1484
|
+
# Unlike, get_string this does not return a nil if C-c pressed. Either returns a string if
|
1485
|
+
# ENTER pressed or a blank if C-c or Double Escape. So only blank to be checked
|
1486
|
+
def get_line text, config={}
|
1487
|
+
begin
|
1488
|
+
w = one_line_window
|
1489
|
+
form = RubyCurses::Form.new w
|
1490
|
+
|
1491
|
+
f = Field.new form, :label => text, :row => 0, :col => 1
|
1492
|
+
form.repaint
|
1493
|
+
w.wrefresh
|
1494
|
+
while((ch = w.getchar()) != FFI::NCurses::KEY_F10 )
|
1495
|
+
break if ch == 13
|
1496
|
+
if ch == 3 || ch == 27 || ch == 2727
|
1497
|
+
return ""
|
1498
|
+
end
|
1499
|
+
begin
|
1500
|
+
form.handle_key(ch)
|
1501
|
+
w.wrefresh
|
1502
|
+
rescue => err
|
1503
|
+
$log.debug( err) if err
|
1504
|
+
$log.debug(err.backtrace.join("\n")) if err
|
1505
|
+
textdialog ["Error in Messagebox: #{err} ", *err.backtrace], :title => "Exception"
|
1506
|
+
w.refresh # otherwise the window keeps showing (new FFI-ncurses issue)
|
1507
|
+
$error_message.value = ""
|
1508
|
+
ensure
|
1509
|
+
end
|
1510
|
+
end # while loop
|
1511
|
+
|
1512
|
+
ensure
|
1513
|
+
w.destroy
|
1514
|
+
w = nil
|
1515
|
+
end
|
1516
|
+
return f.text
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
#run if __FILE__ == $PROGRAM_NAME
|
1520
|
+
end
|