lib/lazydoc.rb in lazydoc-0.2.0 vs lib/lazydoc.rb in lazydoc-0.3.0

- old
+ new

@@ -1,202 +1,95 @@ require 'lazydoc/document' module Lazydoc - autoload(:Attributes, 'lazydoc/attributes') - - # A regexp matching an attribute start or end. After a match: - # - # $1:: const_name - # $3:: key - # $4:: end flag - # - ATTRIBUTE_REGEXP = /([A-Z][A-z]*(::[A-Z][A-z]*)*)?::([a-z_]+)(-?)/ - - # A regexp matching constants from the ATTRIBUTE_REGEXP leader - CONSTANT_REGEXP = /#.*?([A-Z][A-z]*(::[A-Z][A-z]*)*)?$/ - - # A regexp matching a caller line, to extract the calling file - # and line number. After a match: - # - # $1:: file - # $3:: line number (as a string, obviously) - # - # Note that line numbers in caller start at 1, not 0. - CALLER_REGEXP = /^(([A-z]:)?[^:]+):(\d+)/ - module_function - # A hash of (source_file, lazydoc) pairs tracking the - # Lazydoc instance for the given source file. + # An array of documents registered with Lazydoc. def registry @registry ||= [] end - # Returns the lazydoc in registry for the specified source file. - # If no such lazydoc exists, one will be created for it. + # Returns the Document in registry for the specified source file. + # If no such Document exists, one will be created for it. def [](source_file) source_file = File.expand_path(source_file.to_s) + registry.find {|doc| doc.source_file == source_file } || register_file(source_file) + end + + # Generates a Document the source_file and default_const_name and adds it to + # registry, or returns the document already registered to source_file. An + # error is raised if you try to re-register a source_file with an inconsistent + # default_const_name. + def register_file(source_file, default_const_name=nil) + source_file = File.expand_path(source_file.to_s) lazydoc = registry.find {|doc| doc.source_file == source_file } - if lazydoc == nil - lazydoc = Document.new(source_file) + + unless lazydoc + lazydoc = Document.new(source_file, default_const_name) registry << lazydoc end + + if lazydoc.default_const_name != default_const_name + raise ArgumentError, "inconsistent default_const_name specified for #{source_file}: #{lazydoc.default_const_name.inspect} != #{default_const_name.inspect}" + end + lazydoc end - # Register the specified line numbers to the lazydoc for source_file. - # Returns a comment_class instance corresponding to the line. + # Registers the line number to the document for source_file and + # returns the corresponding comment. def register(source_file, line_number, comment_class=Comment) Lazydoc[source_file].register(line_number, comment_class) end - # Registers the method at the specified index in the call stack, to + # Registers the method at the specified index in the call stack to # the file where the method was called. Using the default index of # 1, register_caller registers the caller of the method where - # register_caller is called. For instance: + # register_caller is called (whew!). For instance: # # module Sample # module_function # def method # Lazydoc.register_caller # end # end # # # this is the line that gets registered - # Sample.method + # c = Sample.method # + # c.resolve + # c.subject # => "c = Sample.method" + # c.comment # => "this is the line that gets registered" + # def register_caller(comment_class=Comment, caller_index=1) caller[caller_index] =~ CALLER_REGEXP Lazydoc[$1].register($3.to_i - 1, comment_class) end - # Resolves all lazydocs which include the specified code comments. - def resolve_comments(comments) - registry.each do |doc| - next if (comments & doc.comments).empty? - doc.resolve - end - end - - # Scans the specified file for attributes keyed by key and stores - # the resulting comments in the source_file lazydoc. Returns the - # lazydoc. - def scan_doc(source_file, key) - lazydoc = nil - scan(File.read(source_file), key) do |const_name, attr_key, comment| - lazydoc = self[source_file] unless lazydoc - lazydoc[const_name][attr_key] = comment - end - lazydoc - end - - # Scans the string or StringScanner for attributes matching the key - # (keys may be patterns, they are incorporated into a regexp). Yields - # each (const_name, key, value) triplet to the mandatory block and - # skips regions delimited by the stop and start keys <tt>:-</tt> - # and <tt>:+</tt>. + # Parses the usage for a file (ie the first comment in the file + # following an optional bang line), wrapped to n cols. For + # example, with this: # - # str = %Q{ - # # Const::Name::key value - # # ::alt alt_value + # [hello_world.rb] + # #!/usr/bin/env ruby + # # This is your basic hello world + # # script: # # - # # Ignored::Attribute::not_matched value - # # :::- - # # Also::Ignored::key value - # # :::+ - # # Another::key another value + # # % ruby hello_world.rb # - # Ignored::key value - # } + # puts 'hello world' # - # results = [] - # Lazydoc.scan(str, 'key|alt') do |const_name, key, value| - # results << [const_name, key, value] - # end + # You get this: # - # results - # # => [ - # # ['Const::Name', 'key', 'value'], - # # ['', 'alt', 'alt_value'], - # # ['Another', 'key', 'another value']] + # "\n" + Lazydoc.usage('hello_world.rb') + # # => %Q{ + # # This is your basic hello world script: + # # + # # % ruby hello_world.rb} # - # Returns the StringScanner used during scanning. - def scan(str, key) # :yields: const_name, key, value - scanner = case str - when StringScanner then str - when String then StringScanner.new(str) - else raise TypeError, "can't convert #{str.class} into StringScanner or String" - end - - regexp = /^(.*?)::(:-|#{key})/ - while !scanner.eos? - break if scanner.skip_until(regexp) == nil - - if scanner[2] == ":-" - scanner.skip_until(/:::\+/) - else - next unless scanner[1] =~ CONSTANT_REGEXP - key = scanner[2] - yield($1.to_s, key, scanner.matched.strip) if scanner.scan(/[ \r\t].*$|$/) - end - end - - scanner - end - - # Parses constant attributes from the string or StringScanner. Yields - # each (const_name, key, comment) triplet to the mandatory block - # and skips regions delimited by the stop and start keys <tt>:-</tt> - # and <tt>:+</tt>. - # - # str = %Q{ - # # Const::Name::key subject for key - # # comment for key - # - # # :::- - # # Ignored::key value - # # :::+ - # - # # Ignored text before attribute ::another subject for another - # # comment for another - # } - # - # results = [] - # Lazydoc.parse(str) do |const_name, key, comment| - # results << [const_name, key, comment.subject, comment.to_s] - # end - # - # results - # # => [ - # # ['Const::Name', 'key', 'subject for key', 'comment for key'], - # # ['', 'another', 'subject for another', 'comment for another']] - # - # Returns the StringScanner used during scanning. - def parse(str) # :yields: const_name, key, comment - scanner = case str - when StringScanner then str - when String then StringScanner.new(str) - else raise TypeError, "can't convert #{str.class} into StringScanner or String" - end - - scan(scanner, '[a-z_]+') do |const_name, key, value| - comment = Comment.parse(scanner, false) do |line| - if line =~ ATTRIBUTE_REGEXP - # rewind to capture the next attribute unless an end is specified. - scanner.unscan unless $4 == '-' && $3 == key && $1.to_s == const_name - true - else false - end - end - comment.subject = value - yield(const_name, key, comment) - end - end - - # Parses the usage for a file, ie the first comment in the file - # following an optional bang line. def usage(path, cols=80) scanner = StringScanner.new(File.read(path)) - scanner.scan(/^#!.*?$/) - Comment.parse(scanner, false).wrap(cols, 2).strip + scanner.scan(/#!.*?\r?\n/) + scanner.scan(/\s*#/m) + Comment.new.parse_down(scanner, nil, false).wrap(cols, 2).strip end end \ No newline at end of file