kv 0.2.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 07f9b8b9be509c1a29acef17374146c9381713514e19c91a8a2942f9c2ba09e9
4
- data.tar.gz: fb877ac8dbf8b5fac3f9b2c4e6690aa01fc25bca3b11fcf7abf88c52ef41fa23
3
+ metadata.gz: 573415b451a9344ac21da113782892338606925a3dd04c57d3e82805a5e55de2
4
+ data.tar.gz: 7d29a38d796aacb6d769a31c52313fc6e510ff68de5e51615e9ce7357e2f3b1a
5
5
  SHA512:
6
- metadata.gz: c8c646ba69a9a977ea232dedaaff65db9f781d054e76d846913763821678c93c5362e7f4e153574d252505181da0a73a01b7865c38ac2c380c4bc9abaadcd6bc
7
- data.tar.gz: eb9f37454be0ce707fbc02f6caae238541fe1865460e67de6c730a2eb81e2ba90d2fbaefa97f418426f19a8343e651b843f9943c61cd5e49cb2d22d15e5cde32
6
+ metadata.gz: bf24a502a46eead71095315a4d1e7c76872e849c04360e2d740e6521704ce56b75126fa45f7bfceb1b983845f4e0d9bf189fbde870e001a3a580b6b076116139
7
+ data.tar.gz: 720ac127b0e1960712493473d12254ec39b28c84f80cc0b7a99631728038da9e31a85f1970274badc89b36943632b938b3b557ac0633dd815e477259d248cdcf
@@ -0,0 +1,3 @@
1
+ # These are supported funding model platforms
2
+
3
+ github: [ko1]
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # kv: A page viewer written by Ruby
1
+ # kv: A page viewer written in Ruby
2
2
 
3
- kv is a page viewer designed for streaming data written by Ruby.
3
+ kv is a page viewer designed for streaming data written in Ruby.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,9 +8,7 @@ Install it yourself as:
8
8
 
9
9
  $ gem install kv
10
10
 
11
- # Usage
12
-
13
- kv requires Ruby and curses gem.
11
+ kv requires recent Ruby and curses gem.
14
12
 
15
13
  ## Use kv
16
14
 
@@ -18,17 +16,27 @@ kv requires Ruby and curses gem.
18
16
  # View [FILE]
19
17
  $ kv [OPTIONS] [FILE]
20
18
 
19
+ # View [URI] source code
20
+ $ kv [URI]
21
+
21
22
  # View results of [CMD]
22
23
  $ [CMD] | kv [OPTIONS]
23
24
 
24
25
  # View command help
25
26
  $ kv
26
27
 
27
- Options:
28
+ Usage: kv [options]
28
29
  -f following mode like "tail -f"
29
30
  -n, --line-number LINE goto LINE
31
+ -N Show lines
32
+ -T, --time-stamp Enable time stamp
33
+ -e CMD Run CMD as a child process
34
+ -p, --pipe Open named pipe
35
+ -s Separation mode (tsv)
30
36
  ```
31
37
 
38
+ Note that `--pipe` option creates a named pipe (`~/.kv_pipe` or a specified file) if there is not a fifo file.
39
+
32
40
  ## Command on a pager
33
41
 
34
42
  ```
@@ -72,12 +80,21 @@ kv: A pager by Ruby Command list
72
80
 
73
81
  # Output
74
82
  s: Save screen buffer to file
83
+ P: gist -p
84
+
85
+ # Child process
86
+ You can run child process using -e command line option like -e CMD
87
+ and you can send a meesage to the child process with x command.
88
+
89
+ x: send a message to the child process
75
90
 
76
91
  # Modes
77
92
  N: toggle line mode
93
+ T: toggle time stamp mode
78
94
  m: toggle mouse mode
79
95
  t: terminal (REPL) mode
80
96
  v: vi ("vi filename +[LINE]")
97
+ H: show HTTP response header
81
98
  ```
82
99
 
83
100
  `G` is notable feature, `less` doesn't have. This feature jumps to "current" last line even if the pipe source command does not close output (== input for kv). You can refresh the last line by putting any command.
data/lib/kv.rb CHANGED
@@ -4,22 +4,24 @@ require "kv/version"
4
4
  require "curses"
5
5
  require 'stringio'
6
6
  require 'optparse'
7
+ require 'open-uri'
7
8
 
8
9
  module KV
9
- class KV_PushScreen < Exception
10
+ class PushScreen < Exception
10
11
  attr_reader :screen
11
12
  def initialize screen
12
13
  @screen = screen
13
14
  end
14
15
  end
15
16
 
16
- class KV_PopScreen < Exception
17
+ class PopScreen < Exception
17
18
  end
18
19
 
19
- class KV_Screen
20
+ class Screen
20
21
  RenderStatus = Struct.new(
21
- :c_cols, :c_lines, :x, :y,
22
- :search, :goto, :line_mode, :render_full, :last_lineno
22
+ :c_cols, :c_lines, :x, :y, :last_lineno,
23
+ :search, :goto, :wrapping,
24
+ :line_mode, :ts_mode, :separation_mode,
23
25
  )
24
26
  class RenderStatus
25
27
  def to_s
@@ -27,7 +29,10 @@ class KV_Screen
27
29
  end
28
30
  end
29
31
 
30
- def initialize input, lines: [], search: nil, name: nil, following_mode: false, first_line: 0, line_mode: false
32
+ def initialize input, lines: [],
33
+ name: nil, search: nil, first_line: 0,
34
+ following_mode: false, line_mode: false, separation_mode: false,
35
+ time_stamp: nil, ext_input: nil, fifo_file: nil
31
36
  @rs = RenderStatus.new
32
37
  @last_rs = nil
33
38
  @rs.y = first_line
@@ -36,14 +41,22 @@ class KV_Screen
36
41
  @rs.last_lineno = 0
37
42
  @rs.line_mode = line_mode
38
43
  @rs.search = search
44
+ @rs.wrapping = false
45
+ @rs.ts_mode = false
46
+ @rs.separation_mode = separation_mode
39
47
 
40
48
  @name = name
41
49
  @filename = @name if @name && File.exist?(@name)
42
50
 
51
+ @time_stamp = time_stamp
52
+ @ext_input = ext_input
53
+ @fifo_file = fifo_file
54
+
43
55
  @lines = lines
44
56
  @mode = :screen
45
57
 
46
- @following_mode = following_mode
58
+ @following = following_mode
59
+ @apos = 0
47
60
 
48
61
  @mouse = false
49
62
  @search_ignore_case = false
@@ -51,25 +64,35 @@ class KV_Screen
51
64
  @loading = false
52
65
  @buffer_lines = 10_000
53
66
  @yq = Queue.new
54
- @load_unlimited = false
67
+ if @filename
68
+ @load_unlimited = true
69
+ else
70
+ @load_unlimited = false
71
+ end
72
+
55
73
  @prev_render = {}
74
+ @meta = input.respond_to?(:meta) ? input.meta : nil
56
75
 
57
76
  read_async input if input
58
77
  end
59
78
 
60
79
  def setup_line line
61
80
  line = line.chomp
81
+ line.instance_variable_set(:@time_stamp, Time.now.strftime('%H:%M:%S')) if @time_stamp
62
82
  line.instance_variable_set(:@lineno, @rs.last_lineno += 1)
63
83
  line
64
84
  end
65
85
 
66
86
  def read_async input
67
87
  @loading = true
68
- data = input.read_nonblock(4096)
88
+ begin
89
+ data = input.read_nonblock(800_000)
90
+ rescue IO::EAGAINWaitReadable, EOFError
91
+ data = ''
92
+ end
69
93
 
70
- lines = data.each_line.to_a
71
94
  last_line = nil
72
- lines.each{|line|
95
+ data.each_line{|line|
73
96
  if line[-1] != "\n"
74
97
  last_line = line
75
98
  break
@@ -86,6 +109,7 @@ class KV_Screen
86
109
  end
87
110
 
88
111
  @lines << setup_line(line)
112
+
89
113
  while !@load_unlimited && @lines.size > self.y + @buffer_lines
90
114
  @yq.pop; @yq.clear
91
115
  end
@@ -95,6 +119,10 @@ class KV_Screen
95
119
  if @filename
96
120
  @file_mtime = File.mtime(@filename)
97
121
  @file_lastpos = input.tell
122
+ elsif @fifo_file
123
+ input = open(@fifo_file)
124
+ log(input)
125
+ redo
98
126
  end
99
127
  input.close
100
128
  @loading = false
@@ -102,7 +130,8 @@ class KV_Screen
102
130
  end
103
131
 
104
132
  def y_max
105
- @lines.size - Curses.lines + 2
133
+ max = @lines.size - Curses.lines + 2
134
+ max < 0 ? 0 : max
106
135
  end
107
136
 
108
137
  def y
@@ -131,6 +160,11 @@ class KV_Screen
131
160
  @rs.x
132
161
  end
133
162
 
163
+ def set_load_unlimited b
164
+ @load_unlimited = b
165
+ @yq << true
166
+ end
167
+
134
168
  def init_screen
135
169
  Curses.init_screen
136
170
  Curses.stdscr.keypad(true)
@@ -141,6 +175,11 @@ class KV_Screen
141
175
  else
142
176
  Curses.mousemask(0)
143
177
  end
178
+
179
+ if @loading && self.y_max < @rs.y
180
+ log [:going, self.y_max, @rs.y]
181
+ @following = :going
182
+ end
144
183
  self.y = @rs.y
145
184
  end
146
185
 
@@ -169,24 +208,12 @@ class KV_Screen
169
208
  end
170
209
  end
171
210
 
172
- def screen_status status, post = nil
173
- Curses.setpos Curses.lines-1, 0
174
- Curses.addstr ' '.ljust(Curses.cols)
175
-
176
- standout{
177
- Curses.setpos Curses.lines-1, 0
178
- Curses.addstr status
179
- }
180
- Curses.addstr post if post
181
- Curses.standend
182
- end
183
-
184
211
  LINE_ATTR = Curses::A_DIM
185
212
 
186
213
  def render_data
187
214
  # check update
188
- c_lines = Curses.lines
189
- c_cols = Curses.cols
215
+ c_lines = @rs.c_lines = Curses.lines
216
+ c_cols = @rs.c_cols = Curses.cols
190
217
 
191
218
  if @rs != @last_rs
192
219
  @last_rs = @rs.dup
@@ -196,6 +223,15 @@ class KV_Screen
196
223
 
197
224
  Curses.clear
198
225
 
226
+ if @rs.separation_mode && (lines = @lines[self.y ... (self.y + c_lines - 1)])
227
+ max_cols = []
228
+ lines.each.with_index{|line, ln|
229
+ line.split("\t").each_with_index{|w, i|
230
+ max_cols[i] = max_cols[i] ? [max_cols[i], w.size].max : w.size
231
+ }
232
+ }
233
+ end
234
+
199
235
  (c_lines-1).times{|i|
200
236
  lno = i + self.y
201
237
  line = @lines[lno]
@@ -229,8 +265,26 @@ class KV_Screen
229
265
  end
230
266
  end
231
267
 
268
+ if @rs.ts_mode && ts = line.instance_variable_get(:@time_stamp)
269
+ cattr LINE_ATTR do
270
+ ts = line.instance_variable_get(:@time_stamp)
271
+ Curses.addstr("#{ts} |")
272
+ end
273
+ end
274
+
232
275
  line = line[self.x, cols] || ''
233
276
 
277
+ if @rs.separation_mode
278
+ line = line.split(/\t/).tap{|e|
279
+ if (max = max_cols.size) > 0
280
+ # fill empty columns
281
+ e[max - 1] ||= nil
282
+ end
283
+ }.map.with_index{|w, i|
284
+ "%-#{max_cols[i]}s" % w
285
+ }.join(' | ')
286
+ end
287
+
234
288
  if !@rs.search || !(Regexp === @rs.search)
235
289
  Curses.addstr line
236
290
  else
@@ -263,11 +317,49 @@ class KV_Screen
263
317
  name = @name ? "<#{@name}>" : ''
264
318
  mouse = @mouse ? ' [MOUSE]' : ''
265
319
  search = @rs.search ? " search[#{search_str}]" : ''
266
- loading = @loading ? " (loading...#{@load_unlimited ? '!' : nil}#{@following_mode ? ' following' : ''}) " : ''
320
+ loading = @loading ? " (loading...#{@load_unlimited ? '!' : nil}#{@following ? ' following' : ''}) " : ''
267
321
  x = self.x > 0 ? " x:#{self.x}" : ''
268
322
  screen_status "#{name} lines:#{self.y+1}/#{@lines.size}#{x}#{loading}#{search}#{mouse}"
269
323
  end
270
324
 
325
+ ANIMATION = ['[O ]',
326
+ '[o. ]',
327
+ '[... ]',
328
+ '[ ... ]',
329
+ '[ ... ]',
330
+ '[ ...]',
331
+ '[ .o]',
332
+ '[ O]',
333
+ '[ .o]',
334
+ '[ ...]',
335
+ '[ ... ]',
336
+ '[ ... ]',
337
+ '[ ... ]',
338
+ '[... ]',
339
+ '[o. ]',
340
+ ]
341
+
342
+ def screen_status status, post = nil
343
+ cols = Curses.cols
344
+ line = Curses.lines-1
345
+ Curses.setpos line, 0
346
+ Curses.addstr ' '.ljust(cols)
347
+ len = status.size
348
+ len += post.size if post
349
+
350
+ standout{
351
+ Curses.setpos Curses.lines-1, 0
352
+ Curses.addstr status
353
+ }
354
+ Curses.addstr post if post
355
+
356
+ if !post && len < cols - ANIMATION.first.size
357
+ Curses.setpos line, cols - ANIMATION.first.size - 1
358
+ @apos = (@apos + 1) % ANIMATION.size
359
+ Curses.addstr ANIMATION[@apos]
360
+ end
361
+ end
362
+
271
363
  def check_update
272
364
  if @loading == false
273
365
  if @filename && File.exist?(@filename) && File.mtime(@filename) > @file_mtime
@@ -288,7 +380,7 @@ class KV_Screen
288
380
  def render_screen
289
381
  ev = nil
290
382
 
291
- ms = @following_mode ? 100 : 500
383
+ ms = @following ? 100 : 500
292
384
 
293
385
  ctimeout ms do
294
386
  while ev == nil
@@ -296,39 +388,62 @@ class KV_Screen
296
388
  render_status
297
389
  ev = Curses.getch
298
390
  check_update
299
- if @following_mode
300
- if @rs.search && search_next_move(self.y + 1)
301
- break
391
+ y_max = self.y_max
392
+
393
+ if @following
394
+ case @following
395
+ when :searching
396
+ break if search_next_move
397
+ when :going
398
+ if @rs.goto <= y_max
399
+ self.y = @rs.goto
400
+ break
401
+ end
402
+ when true
403
+ # ok
404
+ else
405
+ raise "unknown following mode: #{@following}"
302
406
  end
303
- self.y = self.y_max
407
+
408
+ self.y = y_max
304
409
  end
305
410
  end
306
411
 
307
- if @following_mode
308
- @following_mode = false
309
- @load_unlimited = false
310
- end
412
+ @following = false
413
+ set_load_unlimited false
311
414
 
312
415
  return ev
313
416
  end
314
417
  end
315
418
 
316
- def search_next_move start
317
- (start...@lines.size).each{|i|
419
+ def search_next_move
420
+ last_line = @lines.size
421
+ # log (@searching..last_line)
422
+
423
+ (@searching...last_line).each{|i|
318
424
  if @rs.search === @lines[i]
319
425
  self.y = i
426
+ @searching = false
320
427
  return true
321
428
  end
322
429
  }
430
+ @searching = last_line
323
431
  return false
324
432
  end
325
433
 
326
434
  def search_next start
327
- if search_next_move start
328
- # OK
435
+ @searching = start
436
+ if search_next_move
437
+ # OK. self.y is updated.
329
438
  else
330
- screen_status "not found: [#{self.search_str}]"
331
- pause
439
+ if @loading
440
+ set_load_unlimited true
441
+ @following = :searching
442
+ else
443
+ screen_status "not found: [#{self.search_str}]"
444
+ pause
445
+ @searching = false
446
+ end
332
447
  end
333
448
  end
334
449
 
@@ -350,26 +465,32 @@ class KV_Screen
350
465
  ev
351
466
  end
352
467
 
353
- def input_str pattern, str = ''.dup, other_actions: nil
354
- loop{
355
- ev = Curses.getch
468
+ def input_str pattern, str = ''.dup, other_actions: {}
469
+ update_action = other_actions[:update]
356
470
 
357
- case ev
358
- when 10
359
- return str
360
- when Curses::KEY_BACKSPACE
361
- str.chop!
362
- when pattern
363
- str << ev
364
- else
365
- if other_actions && (action = other_actions[ev])
366
- action.call(ev)
471
+ ctimeout update_action ? 200 : -1 do
472
+ loop do
473
+ ev = Curses.getch
474
+
475
+ case ev
476
+ when 10
477
+ return str
478
+ when Curses::KEY_BACKSPACE
479
+ str.chop!
480
+ when pattern
481
+ str << ev
482
+ when nil # timeout
483
+ update_action[str]
367
484
  else
368
- log "failure: #{key_name ev}"
369
- return nil
485
+ if action = other_actions[ev]
486
+ action.call(ev)
487
+ else
488
+ log "failure: #{key_name ev}"
489
+ return nil
490
+ end
370
491
  end
371
492
  end
372
- }
493
+ end
373
494
  end
374
495
 
375
496
  def pause
@@ -382,7 +503,7 @@ class KV_Screen
382
503
 
383
504
  case ev
384
505
  when 'q'
385
- raise KV_PopScreen
506
+ raise PopScreen
386
507
 
387
508
  when Curses::KEY_UP, 'k'
388
509
  self.y -= 1
@@ -412,13 +533,11 @@ class KV_Screen
412
533
  end
413
534
 
414
535
  when 'F'
415
- @following_mode = true
416
- @load_unlimited = true
417
- @yq << true
536
+ @following = true
537
+ set_load_unlimited true
418
538
 
419
539
  when 'L'
420
- @load_unlimited = !@load_unlimited
421
- @yq << true
540
+ set_load_unlimited !@load_unlimited
422
541
 
423
542
  when '/'
424
543
  search_str = ''.dup
@@ -470,8 +589,8 @@ class KV_Screen
470
589
  filter_mode_title = "*filter mode [#{self.search_str}]*"
471
590
  if @name != filter_mode_title
472
591
  lines = @lines.grep(@rs.search)
473
- fscr = KV_Screen.new nil, lines: lines, search: @rs.search, name: filter_mode_title
474
- raise KV_PushScreen.new(fscr)
592
+ fscr = Screen.new nil, lines: lines, search: @rs.search, name: filter_mode_title
593
+ raise PushScreen.new(fscr)
475
594
  end
476
595
  end
477
596
 
@@ -498,10 +617,34 @@ class KV_Screen
498
617
 
499
618
  when 'v'
500
619
  if @filename
501
- system("vi #{@filename} +#{self.y + 1}")
620
+ syste m("vi #{@filename} +#{self.y + 1}")
502
621
  @last_rs = nil
503
622
  end
504
623
 
624
+ when 'P'
625
+ begin
626
+ if v = `gist -v` and /^gist v\d/ =~ v
627
+ screen_status "gist-ing..."
628
+
629
+ url = IO.popen('gist -p', 'a+'){|rw|
630
+ @lines.each{|line| rw.puts line}
631
+ rw.close_write
632
+ rw.read
633
+ }
634
+ msg = "gist URL: #{url}"
635
+ at_exit{
636
+ puts msg
637
+ }
638
+ screen_status msg
639
+ pause
640
+ else
641
+ raise v.inspect
642
+ end
643
+ rescue Errno::ENOENT
644
+ screen_status 'gist command is not found'
645
+ pause
646
+ end
647
+
505
648
  when 'm'
506
649
  @mouse = !@mouse
507
650
  Curses.close_screen
@@ -514,16 +657,52 @@ class KV_Screen
514
657
 
515
658
  when 'N'
516
659
  @rs.line_mode = !@rs.line_mode
660
+ when 'T'
661
+ @rs.ts_mode = !@rs.ts_mode if @time_stamp
662
+ when 'S'
663
+ @rs.separation_mode = !@rs.separation_mode
517
664
  when 't'
518
665
  Curses.close_screen
519
666
  @mode = :terminal
667
+ when 'x'
668
+ while @ext_input && !@ext_input.closed?
669
+ update_ext_status = -> str do
670
+ screen_status "input for ext:", str
671
+ end
672
+ update_ext_status['']
673
+ actions = {
674
+ update: -> str do
675
+ self.y = self.y_max
676
+ render_data
677
+ update_ext_status[str]
678
+ end
679
+ }
680
+ str = input_str(/./, other_actions: actions)
681
+ if str && !str.empty?
682
+ @ext_input.puts str unless @ext_input.closed?
683
+ else
684
+ break
685
+ end
686
+ end
687
+
688
+ when 'H'
689
+ if @meta
690
+ lines = @meta.map{|k, v| "#{k}: #{v}"}
691
+ raise PushScreen.new(Screen.new nil, lines: lines, name: "Response header [#{@name}]")
692
+ end
693
+
694
+ when Curses::KEY_CTRL_G
695
+ # do nothing
520
696
 
521
697
  when '?'
522
- raise KV_PushScreen.new(KV_Screen.new help_io)
698
+ raise PushScreen.new(Screen.new help_io)
523
699
 
524
700
  when nil
525
701
  # ignore
526
702
 
703
+ when Curses::KEY_RESIZE
704
+ # ignore
705
+
527
706
  else
528
707
  screen_status "unknown: #{key_name(ev)}"
529
708
  pause
@@ -548,6 +727,10 @@ class KV_Screen
548
727
  raise
549
728
  end
550
729
  end
730
+
731
+ def redraw!
732
+ @last_rs = nil
733
+ end
551
734
  end
552
735
 
553
736
  class KV
@@ -559,11 +742,39 @@ class KV
559
742
  }
560
743
 
561
744
  files = parse_option(argv)
745
+ name = files.shift
562
746
 
563
747
  @pipe_in = nil
564
748
 
565
- if files.empty?
566
- if STDIN.isatty
749
+ if @opts[:pipe] || (name && File.pipe?(name))
750
+ @opts.delete(:pipe)
751
+ @opts[:fifo_file] = name || '/tmp/kv_pipe'
752
+
753
+ if name && File.pipe?(name)
754
+ # ok
755
+ else
756
+ begin
757
+ name ||= File.expand_path('~/.kv_pipe')
758
+ unlink_name = name
759
+ File.mkfifo(name)
760
+ at_exit{ puts "$ rm #{unlink_name}"; File.unlink(unlink_name) }
761
+ rescue Errno::EEXIST
762
+ raise "#{name} already exists."
763
+ end
764
+ end
765
+
766
+ puts "waiting for #{name}"
767
+ input = @pipe_in = open(name)
768
+ name = nil
769
+ elsif !name
770
+ case
771
+ when @opts[:e]
772
+ cmd = @opts.delete(:e)
773
+ input = IO.popen(cmd, 'a+')
774
+ name = nil
775
+ @pipe_in = input
776
+ @opts[:ext_input] = input
777
+ when STDIN.isatty
567
778
  input = help_io
568
779
  name = 'HELP'
569
780
  else
@@ -573,16 +784,20 @@ class KV
573
784
  @pipe_in = input
574
785
  end
575
786
  else
576
- name = files.shift
577
787
  begin
578
788
  input = open(name)
579
- rescue
580
- if /(.+):(\d+)/ =~ name
789
+ rescue Errno::ENOENT
790
+ case name
791
+ when /(.+):(\d+)/
581
792
  name = $1
582
- @first_line = $2.to_i - 1
793
+ @opts[:first_line] = $2.to_i - 1
583
794
  retry
795
+ when URI.regexp
796
+ input = URI.open(name)
797
+ else
798
+ STDERR.puts "#{name}: No such file or directory"
799
+ exit 1
584
800
  end
585
- raise
586
801
  end
587
802
  end
588
803
 
@@ -590,7 +805,7 @@ class KV
590
805
  log "SIGINT"
591
806
  }
592
807
 
593
- @screens = [KV_Screen.new(input, name: name, **@opts)]
808
+ @screens = [Screen.new(input, name: name, **@opts)]
594
809
  end
595
810
 
596
811
  def parse_option argv
@@ -604,6 +819,18 @@ class KV
604
819
  opts.on('-N', 'Show lines'){
605
820
  @opts[:line_mode] = true
606
821
  }
822
+ opts.on('-T', '--time-stamp', 'Enable time stamp'){
823
+ @opts[:time_stamp] = true
824
+ }
825
+ opts.on('-e CMD', 'Run CMD as a child process'){|cmd|
826
+ @opts[:e] = cmd
827
+ }
828
+ opts.on('-p', '--pipe', 'Open named pipe'){
829
+ @opts[:pipe] = true
830
+ }
831
+ opts.on('-s', 'Separation mode (tsv)'){
832
+ @opts[:separation_mode] = true
833
+ }
607
834
  opts.parse!(argv)
608
835
  end
609
836
 
@@ -612,10 +839,12 @@ class KV
612
839
  until @screens.empty?
613
840
  begin
614
841
  @screens.last.control
615
- rescue KV_PopScreen
842
+ rescue PopScreen
616
843
  @screens.pop
617
- rescue KV_PushScreen => e
844
+ @screens.last.redraw!
845
+ rescue PushScreen => e
618
846
  @screens.push e.screen
847
+ @screens.last.redraw!
619
848
  end
620
849
  end
621
850
  ensure
@@ -1,3 +1,3 @@
1
- module KV
2
- VERSION = "0.2.0"
3
- end
1
+ module KV
2
+ VERSION = "0.7.0"
3
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-02-08 00:00:00.000000000 Z
11
+ date: 2020-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses
@@ -32,6 +32,7 @@ executables:
32
32
  extensions: []
33
33
  extra_rdoc_files: []
34
34
  files:
35
+ - ".github/FUNDING.yml"
35
36
  - ".gitignore"
36
37
  - LICENSE
37
38
  - README.md
@@ -61,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
61
62
  - !ruby/object:Gem::Version
62
63
  version: '0'
63
64
  requirements: []
64
- rubygems_version: 3.2.0.pre1
65
+ rubygems_version: 3.1.2
65
66
  signing_key:
66
67
  specification_version: 4
67
68
  summary: 'kv: A page viewer written by Ruby'