lib/lazydoc/comment.rb in lazydoc-0.1.0 vs lib/lazydoc/comment.rb in lazydoc-0.2.0

- old
+ new

@@ -103,11 +103,11 @@ when StringScanner then str when String then StringScanner.new(str) else raise TypeError, "can't convert #{str.class} into StringScanner or String" end - comment = Comment.new + comment = self.new while scanner.scan(/\r?\n?[ \t]*#[ \t]?(([ \t]*).*?)\r?$/) fragment = scanner[1] indent = scanner[2] # collect continuous description line @@ -122,12 +122,14 @@ end if parse_subject scanner.skip(/\s+/) unless scanner.peek(1) == '#' - comment.subject = scanner.scan(/.+?$/) - comment.subject.strip! unless comment.subject == nil + if subject = scanner.scan(/.+?$/) + subject.strip! + end + comment.subject = subject end end comment end @@ -170,11 +172,44 @@ categorize($1, $2) do |fragment| yield(fragment) end true end - + + # Scans a stripped trailing comment off of str, tolerant to a leader + # that uses '#' within a string. Returns nil for strings without a + # trailing comment. + # + # Comment.scan_trailer "str with # trailer" # => "trailer" + # Comment.scan_trailer "'# in str' # trailer" # => "trailer" + # Comment.scan_trailer "str with without trailer" # => nil + # + # Note the %-syntax for strings is not fully supported, ie %Q, %q, + # etc. may not parse correctly. Accepts Strings or a StringScanner. + def scan_trailer(str) + 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 + + args = [] + brakets = braces = parens = 0 + start = scanner.pos + while scanner.skip(/.*?['"#]/) + pos = scanner.pos - 1 + + case str[pos] + when ?# then return scanner.rest.strip # return the trailer + when ?' then skip_quote(scanner, /'/) # parse over quoted strings + when ?" then skip_quote(scanner, /"/) # parse over double-quoted string + end + end + + return nil + end + # Splits a line of text along whitespace breaks into fragments of cols # width. Tabs in the line will be expanded into tabsize spaces; # fragments are rstripped of whitespace. # # Comment.wrap("some line that will wrap", 10) # => ["some line", "that will", "wrap"] @@ -206,10 +241,17 @@ # indented line yield [fragment.rstrip] yield [] end end + + # helper method to skip to the next non-escaped instance + # matching the quote regexp (/'/ or /"/). + def skip_quote(scanner, regexp) # :nodoc: + scanner.skip_until(regexp) + scanner.skip_until(regexp) while scanner.string[scanner.pos-2] == ?\\ + end end # An array of comment fragments organized into lines attr_reader :content @@ -419,14 +461,16 @@ end # quietly exit if a line number was not found return self unless n.kind_of?(Integer) + # update negative line numbers + n += lines.length if n < 0 unless n < lines.length - raise RangeError, "line_number outside of lines: #{line_number} (#{lines.length})" + raise RangeError, "line_number outside of lines: #{n} (#{lines.length})" end - + self.line_number = n self.subject = lines[n] self.content.clear # remove whitespace lines @@ -453,9 +497,14 @@ # True if all lines in content are empty. def empty? !content.find {|line| !line.empty?} end + # Returns a comment trailing the subject. + def trailer + subject ? Comment.scan_trailer(subject) : nil + end + # Returns content as a string where line fragments are joined by # fragment_sep and lines are joined by line_sep. def to_s(fragment_sep=" ", line_sep="\n", strip=true) lines = content.collect {|line| line.join(fragment_sep)}