command-set 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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] || @dispatch_stack
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(io)
462
- @out_to = io
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
- advice.merge!(result) if Hash === result
475
- advice
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
- @formatter.push_strategy(name)
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(io)
615
- super(io)
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, :closed_begin_list, :closed_item, :closed_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
- print list.to_s.ljust(50)
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 finish
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 " " * [0, @indent_level].max
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 saw_begin_list(list); $stderr.print "B"; end
702
- def saw_item(list); $stderr.print "."; end
703
- def saw_end_list(list); $stderr.print "E"; end
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); puts " " + list.to_s; end
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(io, indent=" ", newline="\n")
719
- super(io)
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, prefix)
157
+ def readline_complete(buffer, rl_prefix)
157
158
  parsed_input = split_line(buffer)
158
- unless prefix.empty?
159
- parsed_input.pop
160
- end
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
- return current_command_set.completion_list(parsed_input,
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(/\s*/)
172
- quote = scanner.scan(/['"]/)
173
- unless quote.nil?
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.1
7
- date: 2008-01-11 00:00:00 -08:00
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.1 RDoc
59
+ - command-set-0.9.2 RDoc
60
60
  extra_rdoc_files:
61
61
  - doc/README
62
62
  - doc/GUIDED_TOUR