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)}