command-set 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/command-set/results.rb +181 -56
- data/lib/command-set/text-interpreter.rb +31 -23
- metadata +3 -3
data/lib/command-set/results.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'command-set/result-list'
|
2
|
+
require 'Win32/Console/ANSI' if PLATFORM =~ /win32/
|
2
3
|
|
3
4
|
module Kernel
|
4
5
|
def puts(*args)
|
@@ -50,7 +51,6 @@ module Command
|
|
50
51
|
class OutputStandin < IO
|
51
52
|
def initialize(io)
|
52
53
|
@_dc_obj = io
|
53
|
-
@dispatch_stack = nil
|
54
54
|
unless io.fileno.nil?
|
55
55
|
super(io.fileno,"w")
|
56
56
|
end
|
@@ -112,7 +112,18 @@ module Command
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def relevant_collector
|
115
|
-
Thread.current[thread_stack_index] ||
|
115
|
+
Thread.current[thread_stack_index] ||
|
116
|
+
Thread.main[thread_stack_index]
|
117
|
+
end
|
118
|
+
|
119
|
+
def define_dispatch_methods(dispatcher)# :nodoc:
|
120
|
+
dispatcher.dispatches.each do |dispatch|
|
121
|
+
(class << self; self; end).module_eval <<-EOS
|
122
|
+
def #{dispatch}(*args)
|
123
|
+
dispatched_method(:#{dispatch.to_s}, *args)
|
124
|
+
end
|
125
|
+
EOS
|
126
|
+
end
|
116
127
|
end
|
117
128
|
|
118
129
|
def dispatched_method(method, *args)# :nodoc:
|
@@ -128,35 +139,18 @@ module Command
|
|
128
139
|
define_dispatch_methods(collector)
|
129
140
|
end
|
130
141
|
alias set_thread_collector add_thread_local_dispatcher
|
131
|
-
|
132
|
-
#Puts the dispatcher in place to handle normal IO methods.
|
133
|
-
def add_dispatcher(collector)
|
134
|
-
@dispatch_stack = collector
|
135
|
-
define_dispatch_methods(collector)
|
136
|
-
end
|
142
|
+
alias add_dispatcher add_thread_local_dispatcher
|
137
143
|
alias set_default_collector add_dispatcher
|
138
144
|
|
139
|
-
def define_dispatch_methods(dispatcher)# :nodoc:
|
140
|
-
dispatcher.dispatches.each do |dispatch|
|
141
|
-
(class << self; self; end).module_eval <<-EOS
|
142
|
-
def #{dispatch}(*args)
|
143
|
-
dispatched_method(:#{dispatch.to_s}, *args)
|
144
|
-
end
|
145
|
-
EOS
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
145
|
#Unregisters the dispatcher.
|
150
146
|
def remove_dispatcher(dispatcher)
|
151
|
-
@dispatch_stack = nil if @dispatch_stack == dispatcher
|
152
|
-
end
|
153
|
-
alias remove_collector remove_dispatcher
|
154
|
-
|
155
|
-
def remove_thread_local_dispatcher(dispatcher)
|
156
147
|
if Thread.current[thread_stack_index] == dispatcher
|
157
148
|
Thread.current[thread_stack_index] = nil
|
158
149
|
end
|
159
150
|
end
|
151
|
+
|
152
|
+
alias remove_collector remove_dispatcher
|
153
|
+
alias remove_thread_local_dispatcher remove_dispatcher
|
160
154
|
alias remove_thread_collector remove_thread_local_dispatcher
|
161
155
|
end
|
162
156
|
|
@@ -405,6 +399,56 @@ module Command
|
|
405
399
|
#This class in particular is pretty quiet - probably not helpful for everyday use.
|
406
400
|
#Of course, for some purposes, singleton methods might be very useful
|
407
401
|
class Formatter
|
402
|
+
module Styler
|
403
|
+
Foregrounds = {
|
404
|
+
'black' => 30,
|
405
|
+
'red' => 31,
|
406
|
+
'green' => 32,
|
407
|
+
'yellow' => 33,
|
408
|
+
'blue' => 34,
|
409
|
+
'magenta' => 35,
|
410
|
+
'cyan' => 36,
|
411
|
+
'white' => 37
|
412
|
+
}
|
413
|
+
|
414
|
+
Backgrounds = {}
|
415
|
+
|
416
|
+
Foregrounds.each() do |name, value|
|
417
|
+
Backgrounds[name] = value + 10
|
418
|
+
end
|
419
|
+
|
420
|
+
Extras = {
|
421
|
+
'clear' => 0,
|
422
|
+
'bold' => 1,
|
423
|
+
'underline' => 4,
|
424
|
+
'reversed' => 7
|
425
|
+
}
|
426
|
+
|
427
|
+
def style(text, options)
|
428
|
+
options ||= {}
|
429
|
+
if options.key? :format_advice
|
430
|
+
options = options.merge(options[:format_advice])
|
431
|
+
end
|
432
|
+
aliased = {
|
433
|
+
:foreground => options[:color],
|
434
|
+
:extra => options[:text_style]
|
435
|
+
}
|
436
|
+
options = aliased.merge(options)
|
437
|
+
markup = code_for(Foregrounds, options[:foreground]) +
|
438
|
+
code_for(Backgrounds, options[:background]) +
|
439
|
+
code_for(Extras, options[:extra])
|
440
|
+
return text if markup.empty?
|
441
|
+
return markup + text + code_for(Extras, "clear")
|
442
|
+
end
|
443
|
+
|
444
|
+
def code_for(kind, name)
|
445
|
+
if kind.has_key?(name.to_s)
|
446
|
+
"\e[#{kind[name.to_s]}m"
|
447
|
+
else
|
448
|
+
""
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
408
452
|
extend Forwardable
|
409
453
|
|
410
454
|
class FormatAdvisor
|
@@ -458,8 +502,9 @@ module Command
|
|
458
502
|
end
|
459
503
|
end
|
460
504
|
|
461
|
-
def initialize(
|
462
|
-
@out_to =
|
505
|
+
def initialize(out = nil, err = nil)
|
506
|
+
@out_to = out || ::Command::raw_stdout
|
507
|
+
@err_to = err || ::Command::raw_stderr
|
463
508
|
@advisor = FormatAdvisor.new(self)
|
464
509
|
@advice = {:list => [], :item => [], :output => []}
|
465
510
|
end
|
@@ -471,8 +516,11 @@ module Command
|
|
471
516
|
@advice[type].inject(default_advice(type)) do |advice, advisor|
|
472
517
|
result = advisor[item]
|
473
518
|
break if result == :DONE
|
474
|
-
|
475
|
-
|
519
|
+
if Hash === result
|
520
|
+
advice.merge(result)
|
521
|
+
else
|
522
|
+
advice
|
523
|
+
end
|
476
524
|
end
|
477
525
|
end
|
478
526
|
|
@@ -532,6 +580,7 @@ module Command
|
|
532
580
|
class StrategyFormatter < Formatter
|
533
581
|
class FormatStrategy
|
534
582
|
extend Forwardable
|
583
|
+
include Formatter::Styler
|
535
584
|
|
536
585
|
def initialize(name, formatter)
|
537
586
|
@name = name
|
@@ -546,7 +595,10 @@ module Command
|
|
546
595
|
attr_reader :name
|
547
596
|
|
548
597
|
def switch_to(name)
|
549
|
-
|
598
|
+
unless name == self.name
|
599
|
+
return true
|
600
|
+
end
|
601
|
+
return false
|
550
602
|
end
|
551
603
|
|
552
604
|
def finish
|
@@ -564,13 +616,6 @@ module Command
|
|
564
616
|
|
565
617
|
#Presenter callback: a list opened, tree order
|
566
618
|
def closed_begin_list(list);
|
567
|
-
unless list.options[:strategy_start] == self or list.options[:format_advice].nil?
|
568
|
-
switch_to(list.options[:format_advice][:type])
|
569
|
-
if (next_strat = @formatter.current_strategy) != self
|
570
|
-
list.options[:strategy_start] = next_strat
|
571
|
-
next_strat.closed_begin_list(list)
|
572
|
-
end
|
573
|
-
end
|
574
619
|
end
|
575
620
|
|
576
621
|
#Presenter callback: an item added, tree order
|
@@ -582,6 +627,15 @@ module Command
|
|
582
627
|
finish
|
583
628
|
end
|
584
629
|
end
|
630
|
+
|
631
|
+
private
|
632
|
+
def out
|
633
|
+
@formatter.out_to
|
634
|
+
end
|
635
|
+
|
636
|
+
def err
|
637
|
+
@formatter.err_to
|
638
|
+
end
|
585
639
|
end
|
586
640
|
|
587
641
|
@strategies = {:default => FormatStrategy}
|
@@ -611,14 +665,16 @@ module Command
|
|
611
665
|
end
|
612
666
|
end
|
613
667
|
|
614
|
-
def initialize(
|
615
|
-
super(
|
668
|
+
def initialize(out = nil, err = nil)
|
669
|
+
super(out, err)
|
616
670
|
@strategies = self.class.strategy_set(self)
|
617
671
|
@strategy_stack = [@strategies[:default]]
|
618
672
|
end
|
619
673
|
|
674
|
+
attr_reader :out_to, :err_to
|
675
|
+
|
620
676
|
def_delegators :current_strategy, :saw_begin_list, :saw_item,
|
621
|
-
:saw_end_list
|
677
|
+
:saw_end_list
|
622
678
|
|
623
679
|
def current_strategy
|
624
680
|
@strategy_stack.last
|
@@ -636,6 +692,34 @@ module Command
|
|
636
692
|
end
|
637
693
|
end
|
638
694
|
|
695
|
+
def closed_begin_list(list)
|
696
|
+
going_to = current_strategy.name
|
697
|
+
unless list.options[:format_advice].nil? or
|
698
|
+
(next_strategy = list.options[:format_advice][:type]).nil? or
|
699
|
+
@strategies[next_strategy].nil? or
|
700
|
+
not current_strategy.switch_to(next_strategy)
|
701
|
+
going_to = next_strategy
|
702
|
+
end
|
703
|
+
push_strategy(going_to)
|
704
|
+
current_strategy.closed_begin_list(list)
|
705
|
+
end
|
706
|
+
|
707
|
+
def closed_end_list(list)
|
708
|
+
current_strategy.closed_end_list(list)
|
709
|
+
current_strategy.finish
|
710
|
+
end
|
711
|
+
|
712
|
+
def closed_item(item)
|
713
|
+
unless item.options[:format_advice].nil? or
|
714
|
+
(once = item.options[:format_advice][:type]).nil? or
|
715
|
+
@strategies[once].nil? or
|
716
|
+
not current_strategy.switch_to(once)
|
717
|
+
@strategies[once].closed_item(item)
|
718
|
+
else
|
719
|
+
current_strategy.closed_item(item)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
639
723
|
strategy :default do
|
640
724
|
def closed_item(value)
|
641
725
|
puts value
|
@@ -643,18 +727,21 @@ module Command
|
|
643
727
|
end
|
644
728
|
|
645
729
|
strategy :progress do
|
730
|
+
def switch_to(name); false; end
|
646
731
|
def closed_begin_list(list)
|
647
|
-
|
732
|
+
puts unless list.depth == 0
|
733
|
+
justify = 0 || list[:format_advice][:justify]
|
734
|
+
print style(list.to_s.ljust(justify), list.options)
|
648
735
|
end
|
649
736
|
|
650
737
|
def closed_item(item)
|
651
|
-
print "."
|
738
|
+
print style(".", item.options)
|
652
739
|
flush
|
653
740
|
end
|
654
741
|
|
655
|
-
def
|
656
|
-
super
|
742
|
+
def closed_end_list(list)
|
657
743
|
puts
|
744
|
+
super
|
658
745
|
end
|
659
746
|
end
|
660
747
|
|
@@ -664,31 +751,32 @@ module Command
|
|
664
751
|
end
|
665
752
|
|
666
753
|
def indent
|
667
|
-
return " " *
|
754
|
+
return " " * @indent_level
|
668
755
|
end
|
669
756
|
|
670
757
|
def closed_begin_list(list)
|
671
|
-
puts indent + list.to_s
|
672
|
-
@indent_level += 1
|
673
758
|
super
|
759
|
+
puts indent + style(list.to_s, list.options)
|
760
|
+
@indent_level += 1
|
761
|
+
@indent_level
|
674
762
|
end
|
675
763
|
|
676
764
|
def closed_item(item)
|
677
765
|
item.to_s.split(/\s*\n\s*/).each do |line|
|
678
|
-
puts indent + line
|
766
|
+
puts indent + style(line, item.options)
|
679
767
|
end
|
680
768
|
super
|
681
769
|
end
|
682
770
|
|
683
771
|
def closed_end_list(list)
|
684
772
|
@indent_level -= 1
|
773
|
+
@indent_level
|
685
774
|
super
|
686
775
|
end
|
687
776
|
end
|
688
777
|
|
689
778
|
strategy :invisible do
|
690
|
-
def closed_item(value)
|
691
|
-
end
|
779
|
+
def closed_item(value); end
|
692
780
|
end
|
693
781
|
|
694
782
|
strategy :skip do
|
@@ -698,15 +786,33 @@ module Command
|
|
698
786
|
end
|
699
787
|
|
700
788
|
strategy :chatty do
|
701
|
-
def
|
702
|
-
|
703
|
-
def
|
789
|
+
def switch_to(name); false; end
|
790
|
+
|
791
|
+
def saw_begin_list(list)
|
792
|
+
err.print style("B", list.options)
|
793
|
+
end
|
794
|
+
|
795
|
+
def saw_item(list)
|
796
|
+
err.print style(".", list.options)
|
797
|
+
end
|
798
|
+
|
799
|
+
def saw_end_list(list)
|
800
|
+
err.print style("E", list.options)
|
801
|
+
end
|
802
|
+
|
704
803
|
def closed_begin_list(list);
|
705
804
|
clean_options = list.options.dup
|
706
805
|
clean_options.delete(:strategy_start)
|
707
806
|
puts "> #{list.to_s} (depth=#{list.depth} #{clean_options.inspect})"
|
708
807
|
end
|
709
|
-
def closed_item(list)
|
808
|
+
def closed_item(list)
|
809
|
+
puts " " + list.to_s +
|
810
|
+
unless(list.options.empty?)
|
811
|
+
" " + list.options.inspect
|
812
|
+
else
|
813
|
+
""
|
814
|
+
end
|
815
|
+
end
|
710
816
|
def closed_end_list(list); puts "< " + list.to_s; end
|
711
817
|
end
|
712
818
|
end
|
@@ -715,8 +821,8 @@ module Command
|
|
715
821
|
#indents. Might be handy for more complicated output processing, since you could feed the document
|
716
822
|
#to a XSLT processor.
|
717
823
|
class XMLFormatter < Formatter
|
718
|
-
def initialize(
|
719
|
-
super(
|
824
|
+
def initialize(out = nil, err = nil, indent=" ", newline="\n")
|
825
|
+
super(out, err)
|
720
826
|
@indent = indent
|
721
827
|
@newline = newline
|
722
828
|
@indent_level=0
|
@@ -727,12 +833,12 @@ module Command
|
|
727
833
|
end
|
728
834
|
|
729
835
|
def closed_begin_list(name)
|
730
|
-
line "<#{name}>"
|
836
|
+
line "<#{name}#{xmlize_options(name)}>"
|
731
837
|
@indent_level += 1
|
732
838
|
end
|
733
839
|
|
734
840
|
def closed_item(value)
|
735
|
-
line "<item value=\"#{value}\" />"
|
841
|
+
line "<item value=\"#{value}\"#{xmlize_options(value)} />"
|
736
842
|
end
|
737
843
|
|
738
844
|
def closed_end_list(name)
|
@@ -743,6 +849,25 @@ module Command
|
|
743
849
|
end
|
744
850
|
line "</#{name}>"
|
745
851
|
end
|
852
|
+
|
853
|
+
private
|
854
|
+
|
855
|
+
def flatten_value(value)
|
856
|
+
case value
|
857
|
+
when Hash
|
858
|
+
return value.map do |name, value|
|
859
|
+
"#{name}: #{value}"
|
860
|
+
end.join("; ")
|
861
|
+
else
|
862
|
+
return value.to_s
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
def xmlize_options(item)
|
867
|
+
item.options.inject("") do |string, (name, value)|
|
868
|
+
string + " #{name}=\"#{flatten_value(value)}\""
|
869
|
+
end
|
870
|
+
end
|
746
871
|
end
|
747
872
|
end
|
748
873
|
end
|
@@ -131,6 +131,7 @@ module Command
|
|
131
131
|
end
|
132
132
|
line = new_line
|
133
133
|
end
|
134
|
+
line.pop if line.last.empty?
|
134
135
|
return CommandSetup.new(line)
|
135
136
|
end
|
136
137
|
|
@@ -153,39 +154,46 @@ module Command
|
|
153
154
|
end
|
154
155
|
end
|
155
156
|
|
156
|
-
def readline_complete(buffer,
|
157
|
+
def readline_complete(buffer, rl_prefix)
|
157
158
|
parsed_input = split_line(buffer)
|
158
|
-
|
159
|
-
|
160
|
-
|
159
|
+
prefix = parsed_input.pop
|
160
|
+
|
161
|
+
#Lest this kill coverage: hide exists to fix readline's irritating
|
162
|
+
#word splitting
|
163
|
+
hide = prefix.sub(/#{rl_prefix}$/, "")
|
161
164
|
|
162
|
-
|
165
|
+
completes = current_command_set.completion_list(parsed_input,
|
163
166
|
prefix, build_subject)
|
167
|
+
completes.map! do |complete|
|
168
|
+
complete = complete.sub(/^#{hide}/, "")
|
169
|
+
if split_line(complete).length > 1
|
170
|
+
if hide.empty?
|
171
|
+
'"' + complete + '"'
|
172
|
+
else
|
173
|
+
complete + '"'
|
174
|
+
end
|
175
|
+
else
|
176
|
+
complete
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
return completes
|
164
181
|
end
|
165
182
|
|
166
183
|
def split_line(line)
|
167
|
-
line_array = []
|
184
|
+
line_array = [""]
|
168
185
|
scanner = StringScanner.new(line)
|
186
|
+
scanner.scan(/\s*/)
|
169
187
|
|
170
188
|
until scanner.eos?
|
171
|
-
scanner.scan(
|
172
|
-
|
173
|
-
|
189
|
+
next_break = scanner.scan(/['"]/) || '\s'
|
190
|
+
line_array.last << scanner.scan(/[^#{next_break}\\]*/)
|
191
|
+
stopped_by = scanner.scan(/[#{next_break}\\]/)
|
192
|
+
if stopped_by == '\\'
|
193
|
+
line_array.last << scanner.getch
|
194
|
+
elsif not (stopped_by.nil? or stopped_by =~ /['"]/)
|
195
|
+
scanner.scan(/\s*/)
|
174
196
|
line_array << ""
|
175
|
-
until scanner.eos?
|
176
|
-
line_array.last << scanner.scan(/[^#{quote}\\]*/)
|
177
|
-
stopped_by = scanner.scan(/[#{quote}\\]/)
|
178
|
-
break unless stopped_by == '\\'
|
179
|
-
line_array.last << scanner.getch
|
180
|
-
end
|
181
|
-
else
|
182
|
-
line_array << ""
|
183
|
-
until scanner.eos?
|
184
|
-
line_array.last << scanner.scan(/[^\s\\]*/)
|
185
|
-
stopped_by = scanner.scan(/[\s\\]/)
|
186
|
-
break unless stopped_by == '\\'
|
187
|
-
line_array.last << scanner.getch
|
188
|
-
end
|
189
197
|
end
|
190
198
|
end
|
191
199
|
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: command-set
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.9.
|
7
|
-
date: 2008-01-
|
6
|
+
version: 0.9.2
|
7
|
+
date: 2008-01-15 00:00:00 -08:00
|
8
8
|
summary: Framework for interactive programs focused around a DSL for commands
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -56,7 +56,7 @@ rdoc_options:
|
|
56
56
|
- --main
|
57
57
|
- Command
|
58
58
|
- --title
|
59
|
-
- command-set-0.9.
|
59
|
+
- command-set-0.9.2 RDoc
|
60
60
|
extra_rdoc_files:
|
61
61
|
- doc/README
|
62
62
|
- doc/GUIDED_TOUR
|