bri 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog CHANGED
@@ -1,3 +1,17 @@
1
+ 0.2.0
2
+ - Add extensive specs for matching, searching and rendering
3
+ - Fix bug with rendering very narrow horizontal rules
4
+ - Fix bug with wrong color for +monospaced+ vs <tt>monospaced</tt> styles
5
+ - Recognize <tt> as an alias for <code>
6
+ - Recognize <i> as an alias for <em>
7
+ - Add ANSI bold sequences for *bold* and <b>bold</b> text
8
+ - Add recognition and styling of links
9
+ - Correctly indent the second and further parts of a list item
10
+ - Correctly display lettered lists
11
+ - Correctly display aligned labeled lists
12
+ - Allow escaping of \<html> tags
13
+ - Be more robust when missing encountering a missing ri file reference
14
+
1
15
  0.1.5
2
16
  - Fix rendering of nested lists
3
17
  - Fix highlighting of sequences of + and _ characters
data/README CHANGED
@@ -3,9 +3,9 @@ During the days of Ruby 1.8, the ri command was slow. I mean really slow. Glacia
3
3
 
4
4
  Then Mauricio Fernandez produced fastri and qri that produced results a whole lot faster than the native ri tool. Unfortunately, Mauricio seems to have lost interest and didn't port fastri to ruby 1.9. There are patches porting fastri to 1.9.1, but they are iffy and don't work well with 1.9.2 and the new rdoc cache format.
5
5
 
6
- Now with ruby 1.9 and the rdoc 2.x gem ri is very responsive. However the output format looks like darkfish without the HTML and uses up a lot of space. And subjectively, it's ugly.
6
+ Now with ruby 1.9 and the rdoc 2.x/3.x gem ri is very responsive. However the output format looks like darkfish without the HTML and uses up a lot of space. And subjectively, it's ugly.
7
7
 
8
- This is where bri comes in: it tries to emulate fastri/qris output format and lookup patterns while using the new rdoc 2.x infrastructure.
8
+ This is where bri comes in: it tries to emulate fastri/qris output format and lookup patterns while using the new rdoc 2.x/3.x infrastructure.
9
9
 
10
10
  To compare, do 'ri Array' and 'bri Array' and see which appeals more to you.
11
11
 
@@ -31,7 +31,7 @@ Bri is a Beautiful RI formatter.
31
31
 
32
32
  = Requirements
33
33
  * Ruby 1.9.2
34
- * rdoc 2.5.x
34
+ * rdoc 3.5.3 and above
35
35
  * term-ansicolor
36
36
 
37
37
 
data/TODO CHANGED
@@ -1,3 +1,5 @@
1
1
  - Extend unqualified search to include class methods and case insensitive searches
2
- - Misc. error handlings
3
- - Do more specs
2
+ - {multi word label}[links] won't display correctly if the label is right where
3
+ a line wrap occurs
4
+ - Refactor the convoluted rendering of lists and list items
5
+ - Detect ansi capabilities and use what is possible with graceful degradation?
data/bin/bri CHANGED
@@ -27,7 +27,8 @@ def parse_options
27
27
  end
28
28
  parser.parse!( ARGV )
29
29
 
30
- if ARGV.size == 0
30
+ if ARGV.size == 0 &&
31
+ !( @options[:list_classes] || @options[:list_methods] || @options[:list_names] )
31
32
  puts parser.summarize
32
33
  exit 0
33
34
  end
@@ -34,7 +34,8 @@ module Bri
34
34
 
35
35
  # We want: system, site, home and gem documentation
36
36
  RDoc::RI::Paths.each( true, true, true, true ) do |path, type|
37
- @stores << RDoc::RI::Store.new( path, type ).tap { |store| store.load_cache }
37
+ @stores << RDoc::RI::Store.new( path, type ).
38
+ tap { |store| store.load_cache }
38
39
  end
39
40
  end
40
41
 
@@ -13,7 +13,7 @@ module Bri
13
13
 
14
14
  def initialize( rdoc_result )
15
15
  @type = rdoc_result.type
16
- @name = rdoc_result.name
16
+ @name = rdoc_result.full_name
17
17
  @description_paragraphs = build_description( rdoc_result.comment.parts )
18
18
  @includes = rdoc_result.includes.collect { |i| i.full_name }
19
19
  @constants = rdoc_result.constants.collect do |c|
@@ -3,8 +3,10 @@ require 'strscan'
3
3
  module Bri
4
4
  module Renderer
5
5
  INDENT = ' ' * 2
6
+ ALPHABET = ('a'..'z').to_a
6
7
 
7
- def self.render( element, width = Bri.width )
8
+ def self.render( element, width = Bri.width, alignment_width = 0 )
9
+ # STDERR.puts "Rendering #{element.inspect}"
8
10
  case element
9
11
  when RDoc::Markup::Verbatim
10
12
  text = extract_text( element, width )
@@ -13,32 +15,49 @@ module Bri
13
15
 
14
16
  when RDoc::Markup::List
15
17
  item_width = width - INDENT.length
16
- rendered_items = element.items.collect { |item| render item, item_width }
17
- rendered_items.map! { |item| item.gsub( /\n/, "\n#{INDENT}" ) }
18
18
  case element.type
19
19
  when :BULLET
20
+ rendered_items = element.items.collect { |item| render item, item_width }
21
+ rendered_items.map! { |item| item.gsub( /\n/, "\n#{INDENT}" ) }
20
22
  rendered_items.map! { |item| ' *' + item }
23
+
21
24
  when :NUMBER
22
25
  i = 0
26
+ rendered_items = element.items.collect { |item| render item, item_width }
27
+ rendered_items.map! { |item| item.gsub( /\n/, "\n#{INDENT}" ) }
23
28
  rendered_items.map! { |item| i+=1; sprintf "%d.%s", i, item }
24
- when :NOTE, :LABEL
29
+
30
+ when :LALPHA
31
+ i = -1
32
+ rendered_items = element.items.collect { |item| render item, item_width }
33
+ rendered_items.map! { |item| item.gsub( /\n/, "\n#{INDENT}" ) }
34
+ rendered_items.map! { |item| i+=1; sprintf "%s.%s", ALPHABET[i], item }
35
+
36
+ when :LABEL
25
37
  # do nothing
38
+ rendered_items = element.items.collect { |item| render item, item_width }
39
+ rendered_items.map! { |item| item.gsub( /\n/, "\n#{INDENT}" ) }
40
+
41
+ when :NOTE
42
+ alignment_width = element.items.collect { |item| item.label.size }.max + 1
43
+ rendered_items = element.items.collect { |item| render item, item_width, alignment_width }
44
+ rendered_items.map! { |item| item.gsub( /\n/, "\n#{INDENT}" ) }
26
45
  end
27
46
 
28
47
  rendered_items.join( "\n\n" ) + "\n"
29
48
 
30
49
  else
31
- text = extract_text( element, width )
50
+ text = extract_text( element, width, alignment_width )
32
51
  styled_text = replace_markup( text )
33
52
  wrapped_text = wrap_to_width( styled_text, width )
34
53
  indent( wrapped_text )
35
54
  end
36
55
  end
37
56
 
38
- def self.extract_text( element, width )
57
+ def self.extract_text( element, width, label_alignment_width = 0 )
39
58
  text = case element
40
59
  when RDoc::Markup::Paragraph
41
- element.text
60
+ element.parts.join( "\n" )
42
61
  when RDoc::Markup::BlankLine
43
62
  ""
44
63
  when RDoc::Markup::Rule
@@ -49,7 +68,7 @@ module Bri
49
68
  "<h>#{element.text}</h>"
50
69
  when RDoc::Markup::ListItem
51
70
  parts = element.parts.collect { |part| extract_text part, width }.join
52
- element.label ? "#{element.label}: #{parts}" : parts
71
+ element.label ? sprintf( "%*s %s", -label_alignment_width, "#{element.label}:", parts ) : parts
53
72
  when RDoc::Markup::List
54
73
  render( element, width - INDENT.length )
55
74
  else
@@ -59,19 +78,42 @@ module Bri
59
78
  end
60
79
 
61
80
  def self.replace_markup( text )
62
- text.gsub!( "<tt>", Term::ANSIColor::cyan )
63
- text.gsub!( "</tt>", Term::ANSIColor::reset )
81
+ text.gsub!( /(?<!\\)<(?:tt|code)>/, Term::ANSIColor::cyan )
82
+ text.gsub!( /(?<!\\)<\/(?:tt|code)>/, Term::ANSIColor::reset )
83
+
84
+ text.gsub!( /(?<!\\)<b>/, Term::ANSIColor::bold )
85
+ text.gsub!( /(?<!\\)<\/b>/, Term::ANSIColor::reset )
64
86
 
65
- text.gsub!( "<code>", Term::ANSIColor::cyan )
66
- text.gsub!( "</code>", Term::ANSIColor::reset )
87
+ text.gsub!( /(?<!\\)<(?:em|i)>/, Term::ANSIColor::yellow )
88
+ text.gsub!( /(?<!\\)<\/(?:em|i)>/, Term::ANSIColor::reset )
67
89
 
68
90
  text.gsub!( "<h>", Term::ANSIColor::green )
69
91
  text.gsub!( "</h>", Term::ANSIColor::reset )
70
92
 
93
+ text.gsub!( "\\<", "<" )
94
+
95
+ text.gsub!( /(^|\s)\*(.*?[a-zA-Z0-9]+.*?)\*/,
96
+ "\\1#{Term::ANSIColor::bold}\\2#{Term::ANSIColor::reset}" )
71
97
  text.gsub!( /(^|\s)\+(.*?[a-zA-Z0-9]+.*?)\+/,
72
- "\\1#{Term::ANSIColor::yellow}\\2#{Term::ANSIColor::reset}" )
98
+ "\\1#{Term::ANSIColor::cyan}\\2#{Term::ANSIColor::reset}" )
73
99
  text.gsub!( /(^|\s)_(.*?[a-zA-Z0-9]+.*?)_/,
74
100
  "\\1#{Term::ANSIColor::yellow}\\2#{Term::ANSIColor::reset}" )
101
+
102
+ text.gsub!( %r{\b((?:https?|ftp)://[-\w.?%&=/]+)\b},
103
+ "#{Term::ANSIColor::underline}\\1#{Term::ANSIColor::reset}" )
104
+
105
+ text.gsub!( %r{\b(mailto:[-\w.%]+@[-\w.]+)\b},
106
+ "#{Term::ANSIColor::underline}\\1#{Term::ANSIColor::reset}" )
107
+
108
+ text.gsub!( %r{\b((?<!:\/\/)www.[-\w.?%&=]+)\b},
109
+ "#{Term::ANSIColor::underline}\\1#{Term::ANSIColor::reset}" )
110
+
111
+ text.gsub!( %r{\blink:(.*?)(\s|$)},
112
+ "#{Term::ANSIColor::underline}\\1#{Term::ANSIColor::reset}\\2" )
113
+
114
+ text.gsub!( %r{\{(.*?)\}\[(.*?)\]}, "\\1 (\\2)" )
115
+ text.gsub!( %r{\[(#{Regexp.escape( Term::ANSIColor::underline )}.*?#{Regexp.escape( Term::ANSIColor::reset )})\]},
116
+ " (\\1)" )
75
117
  text
76
118
  end
77
119
 
@@ -8,6 +8,8 @@ module Bri
8
8
 
9
9
  def method_rdoc( store, klass = @class_term, method = @method_term )
10
10
  store.load_method( klass, method )
11
+ rescue Errno::ENOENT
12
+ nil
11
13
  end
12
14
  end
13
15
  end
@@ -8,6 +8,8 @@ module Bri
8
8
 
9
9
  def method_rdoc( store, klass = @class_term, method = @method_term )
10
10
  store.load_method( klass, "#" + method )
11
+ rescue Errno::ENOENT
12
+ nil
11
13
  end
12
14
  end
13
15
  end
@@ -55,7 +55,10 @@ module Bri
55
55
  def partially_qualified_search
56
56
  Bri::Mall.instance.stores.each do |store|
57
57
  classes_with_method( store, @method_term ).each do |klass|
58
- @matches << Bri::Match::Method.new( method_rdoc( store, klass, @method_term ) )
58
+ match_data = method_rdoc( store, klass, @method_term )
59
+ next unless match_data
60
+
61
+ @matches << Bri::Match::Method.new( match_data )
59
62
  end
60
63
  end
61
64
  end
@@ -73,7 +76,10 @@ module Bri
73
76
  Bri::Mall.instance.stores.each do |store|
74
77
  candidates_from_method_re( store, method_re ).each do |klass, methods|
75
78
  methods.each do |method|
76
- @matches << Bri::Match::Method.new( method_rdoc( store, klass, method ) )
79
+ match_data = method_rdoc( store, klass, method )
80
+ next unless match_data
81
+
82
+ @matches << Bri::Match::Method.new( match_data )
77
83
  end
78
84
  end
79
85
  end
@@ -68,7 +68,7 @@ module Bri
68
68
  '-' * width + "\n"
69
69
  else
70
70
  text = " " + text if text != ''
71
- '-' * ( width - text.length ) + Term::ANSIColor::bold( text ) + "\n"
71
+ ( '-' * [ ( width - text.length ), 1 ].max ) + Term::ANSIColor::bold( text ) + "\n"
72
72
  end
73
73
  end
74
74
  module_function :hrule
@@ -0,0 +1,132 @@
1
+ # This is a class description
2
+ class BriDummySpecClassTwo
3
+ include BriDummySpecModule
4
+ extend BriDummySpecModuleTwo
5
+ CONSTANT = 'value'
6
+ OTHER_CONSTANT = nil
7
+ attr_reader :read_attr
8
+ attr_writer :write_attr
9
+ attr_accessor :access_attr
10
+
11
+ def self.bri_dummy_spec_second_singleton_method; end
12
+ def bri_dummy_spec_instance_method_with_arguments( a, b ); end
13
+ end
14
+
15
+ class BriDummySpecClassEmpty; end
16
+
17
+ module BriDummySpecModule; end
18
+ module BriDummySpecModuleTwo; end
19
+
20
+ module BriDummySpec
21
+ class Class; end
22
+ end
23
+
24
+ class BriDummySpecClass
25
+ def self.bri_dummy_spec_singleton_method; end
26
+ def self.bri_dummy_spec_second_singleton_method; end
27
+
28
+ def bri_dummy_spec_instance_method; end
29
+
30
+ def bri_dummy_spec_instance_method_with_arguments( a, b ); end
31
+
32
+ def bri_dummy_spec_instance_method_with_default_arguments( a, b, c = nil ); end
33
+
34
+ def bri_dummy_spec_instance_method_which_yields
35
+ yield yield_param_one, yield_param_two
36
+ end
37
+
38
+ def bri_dummy_spec_instance_method_with_yield_override # :yields: foo, bar
39
+ yield yield_param_one, yield_param_two
40
+ end
41
+
42
+
43
+ # = This is a headline
44
+ #
45
+ # Followed by some introduction text.
46
+ def basic_headline_and_paragraph_rendering_test_method; end
47
+
48
+ # == This is a level two headline
49
+ # This is a paragraph with a really really really really really really really really really really long line that needs to be wrapped.
50
+ def level_two_headline_and_line_wrapping_rendering_test_method; end
51
+
52
+ # This is some text before a horizontal rule.
53
+ # ---
54
+ # After a horizontal rule, the text continues.
55
+ def horizontal_rule_rendering_test_method; end
56
+
57
+ # * First item in a bulleted list
58
+ # With a second line
59
+ # * Second item in a bulleted list
60
+ # * First item of a nested bulleted list
61
+ # * Second item of a nested bulleted list
62
+ # * Ending a bulleted list with a really really really really really really really really long line that needs to be wrapped
63
+ def bulleted_list_rendering_test_method; end
64
+
65
+ # - A second bulleted list
66
+ # - Second item in second bulleted list
67
+ # - Nested bulleted list
68
+ # - Second nested bulleted list item
69
+ # - Ending the second bulleted list
70
+ def second_bulleted_list_rendering_test_method; end
71
+
72
+ # 1. First numbered list item
73
+ # 2. Second numbered list
74
+ # 1. Nested numbered list item
75
+ # 2. Second nested numbered list item
76
+ # 3. Ending the main numbered list item
77
+ def numbered_list_rendering_test_method; end
78
+
79
+ # a. Some goes for lettered lists
80
+ # b. Second item in a lettered list
81
+ # a. And a nested lettered list item
82
+ # c. Second nested lettered list item
83
+ # c. Ending the main lettered list item.
84
+ def lettered_list_rendering_test_method; end
85
+
86
+ # * A mixed list, containing
87
+ # * bullets in the main list
88
+ # 1. And numbers in a sublist
89
+ def mixed_list_rendering_test_method; end
90
+
91
+ # 1. Also in reverse
92
+ # 2. Second item
93
+ # * Nested bulleted list
94
+ def second_mixed_list_rendering_test_method; end
95
+
96
+ # [First] And this is the list item body
97
+ # With a second line containing more text
98
+ #
99
+ # [Second] Another labled list item
100
+ def labeled_list_rendering_test_method; end
101
+
102
+ # First:: With some text.
103
+ # Secondarily:: Lets see if this lines up.
104
+ def lined_up_labeled_list_rendering_test_method; end
105
+
106
+ # * a list item
107
+ # with a long item text
108
+ # Containing verbatim text
109
+ def list_containing_verbatim_text_rendering_test_method; end
110
+
111
+ # Text with stylings: *bold*, _emphasized_ and +monospaced+.
112
+ def simple_styling_rendering_test_method; end
113
+
114
+ # Also with html: <b>Bold</b>, <em>emphasized</em>, <i>also emphasized</i>
115
+ # and <tt>monospaced tt</tt> or <code>monospaced code</code>.
116
+ def html_styling_rendering_test_method; end
117
+
118
+ # These should not be styled: \<b>Not bold\</b>, \<em>not emphasized\</em>.
119
+ def escaped_styling_rendering_test_method; end
120
+
121
+ # Furthermore, this text contains links to raw links http://www.google.com mailto:spamidyspam@spam.com ftp://warez.teuto.de and plain web links: www.test.com .
122
+ #
123
+ # Then we have local links to other files: link:/etc/fstab
124
+ def raw_link_rendering_test_method; end
125
+
126
+ # Plus: Labled links SingleWordLabel[http://duckduckgo.com] and
127
+ # {Multi Word Labels}[http://www.github.com].
128
+ def labeled_link_rendering_test_method; end
129
+
130
+ # Conversion characters: this: -- or --- should be an em-dash. We also have an ellipsis: ... . Copyright: (c) and registered trademark (r).
131
+ def conversion_character_rendering_test_method; end
132
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bri::Match::Class do
4
+ let( :fully_developed_class ) do
5
+ search_instance = Bri::Search::Class.new( "BriDummySpecClassTwo" )
6
+ search_instance.search
7
+ search_instance.matches.first
8
+ end
9
+
10
+ let( :empty_class ) do
11
+ search_instance = Bri::Search::Class.new( "BriDummySpecClassEmpty" )
12
+ search_instance.search
13
+ search_instance.matches.first
14
+ end
15
+
16
+ let( :empty_module ) do
17
+ search_instance = Bri::Search::Class.new( "BriDummySpecModule" )
18
+ search_instance.search
19
+ search_instance.matches.first
20
+ end
21
+
22
+ let( :namespaced_class ) do
23
+ search_instance = Bri::Search::Class.new( "BriDummySpec::Class" )
24
+ search_instance.search
25
+ search_instance.matches.first
26
+ end
27
+
28
+ describe "#type" do
29
+ it "should be class for a class" do
30
+ empty_class.type.should == "class"
31
+ end
32
+
33
+ it "should be module for a module" do
34
+ empty_module.type.should == "module"
35
+ end
36
+ end
37
+
38
+ describe "#name" do
39
+ it "should contain the name of a class" do
40
+ empty_class.name.should == "BriDummySpecClassEmpty"
41
+ end
42
+
43
+ it "should contain the fully qualified name of a namespaced class" do
44
+ namespaced_class.name.should == "BriDummySpec::Class"
45
+ end
46
+ end
47
+
48
+ describe "#description_paragraphs" do
49
+ it "should be empty for an undocumented class" do
50
+ empty_class.description_paragraphs.should be_empty
51
+ end
52
+
53
+ it "should contain rendered text for a documented class" do
54
+ fully_developed_class.description_paragraphs.size.should == 1
55
+ fully_developed_class.description_paragraphs.first.should =~ /This is a class description/
56
+ end
57
+ end
58
+
59
+ describe "#includes" do
60
+ it "should be empty for a class without includes" do
61
+ empty_class.includes.should be_empty
62
+ end
63
+
64
+ it "should contain a list of includes for classes with includes" do
65
+ fully_developed_class.includes.size.should == 1
66
+ fully_developed_class.includes.should include( "BriDummySpecModule" )
67
+ end
68
+ end
69
+
70
+ describe "#constants" do
71
+ it "should be empty for a class without constants" do
72
+ empty_class.constants.should be_empty
73
+ end
74
+
75
+ it "should contain a list of constants for classes with constants" do
76
+ fully_developed_class.constants.size.should == 2
77
+ fully_developed_class.constants.should include( "CONSTANT" )
78
+ fully_developed_class.constants.should include( "OTHER_CONSTANT" )
79
+ end
80
+ end
81
+
82
+ describe "#attributes" do
83
+ it "should be empty for a class without attributes" do
84
+ empty_class.attributes.should be_empty
85
+ end
86
+
87
+ it "should contain a list of attributes for classes with attr_* declarations" do
88
+ fully_developed_class.attributes.should_not be_empty
89
+ end
90
+
91
+ it "should mark attr_readers as read only" do
92
+ fully_developed_class.attributes.should include( "read_attr (R)" )
93
+ end
94
+
95
+ it "should mark attr_writers as write only" do
96
+ fully_developed_class.attributes.should include( "write_attr (W)" )
97
+ end
98
+
99
+ it "should mark attr_accessors as read/writeable" do
100
+ fully_developed_class.attributes.should include( "access_attr (RW)" )
101
+ end
102
+ end
103
+
104
+ describe "#class_methods" do
105
+ it "should be empty for a class without class methods" do
106
+ empty_class.class_methods.should be_empty
107
+ end
108
+
109
+ it "should contain class methods for classes with class methods" do
110
+ fully_developed_class.class_methods.size.should == 1
111
+ fully_developed_class.class_methods.first.should == "bri_dummy_spec_second_singleton_method"
112
+ end
113
+ end
114
+
115
+ describe "#instance_methods" do
116
+ it "should be empty for a class without instance methods" do
117
+ empty_class.instance_methods.should be_empty
118
+ end
119
+
120
+ it "should contain class methods for classes with class methods" do
121
+ fully_developed_class.instance_methods.size.should == 1
122
+ fully_developed_class.instance_methods.first.should == "bri_dummy_spec_instance_method_with_arguments"
123
+ end
124
+ end
125
+ end