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.
@@ -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