Class: Tap::Support::Lazydoc::Comment
- Defined in:
- lib/tap/support/lazydoc/comment.rb
Overview
Comment represents a code comment parsed by Lazydoc. Comments consist of a subject and content.
sample_comment = %Q{
# this is the content
#
# content may stretch across
# multiple lines
this is the subject
}
Normally the subject is the first non-comment line following the content, although in some cases the subject will be manually set to something else (as in a Lazydoc constant attribute). The content is an array of comment fragments organized by line:
c = Comment.parse(sample_comment)
c.subject # => "this is the subject"
c.content
# => [
# ["this is the content"],
# [""],
# ["content may stretch across", "multiple lines"]]
Comments may be initialized to the subject line and then resolved later:
doc = %Q{
module Sample
# this is the content of the comment
# for method_one
def method_one
end
# this is the content of the comment
# for method_two
def method_two
end
end}
c1 = Comment.new(4).resolve(doc)
c1.subject # => " def method_one"
c1.content # => [["this is the content of the comment", "for method_one"]]
c2 = Comment.new(9).resolve(doc)
c2.subject # => " def method_two"
c2.content # => [["this is the content of the comment", "for method_two"]]
A Regexp (or Proc) may be used in place of a line number; during resolve, the lines will be scanned and the first matching line will be used.
c3 = Comment.new(/def method_two/).resolve(doc)
c3.subject # => " def method_two"
c3.content # => [["this is the content of the comment", "for method_two"]]
Direct Known Subclasses
Instance Attribute Summary collapse
-
#content ⇒ Object
readonly
An array of comment fragments organized into lines.
-
#line_number ⇒ Object
Returns the line number for the subject line, if known.
-
#subject ⇒ Object
The subject of the comment (normally set to the next non-comment line after the content ends; ie the line that would receive the comment in RDoc documentation).
Class Method Summary collapse
-
.parse(str, parse_subject = true) ⇒ Object
Parses the input string into a comment.
-
.scan(line) ⇒ Object
Scan determines if and how to add a line fragment to a comment and yields the appropriate fragments to the block.
-
.wrap(line, cols = 80, tabsize = 2) ⇒ Object
Splits a line of text along whitespace breaks into fragments of cols width.
Instance Method Summary collapse
-
#<<(fragment) ⇒ Object
Alias for push.
-
#==(another) ⇒ Object
Returns true if another is a Comment with the same line_number, subject, and content as self.
-
#append(line) ⇒ Object
Scans the comment line using Comment.scan and pushes the appropriate fragments onto self.
-
#empty? ⇒ Boolean
True if all lines in content are empty.
-
#initialize(line_number = nil) ⇒ Comment
constructor
A new instance of Comment.
-
#prepend(line) ⇒ Object
Scans the comment line using Comment.scan and unshifts the appropriate fragments onto self.
-
#push(fragment) ⇒ Object
Pushes the fragment onto the last line array of content.
-
#resolve(lines) ⇒ Object
Builds the subject and content of self using lines; resolve sets the subject to the line at line_number, and parses content up from there.
-
#to_s(fragment_sep = " ", line_sep = "\n", strip = true) ⇒ Object
Returns content as a string where line fragments are joined by fragment_sep and lines are joined by line_sep.
-
#trim ⇒ Object
Removes leading and trailing lines from content that are empty ([]) or whitespace ([”]).
-
#unshift(fragment) ⇒ Object
Unshifts the fragment to the first line array of content.
-
#value ⇒ Object
Alias for subject.
-
#value=(value) ⇒ Object
Alias for subject=.
-
#wrap(cols = 80, tabsize = 2, line_sep = "\n", fragment_sep = " ", strip = true) ⇒ Object
Like to_s, but wraps the content to the specified number of cols and expands tabs to tabsize spaces.
Constructor Details
#initialize(line_number = nil) ⇒ Comment
Returns a new instance of Comment.
229 230 231 232 233 |
# File 'lib/tap/support/lazydoc/comment.rb', line 229 def initialize(line_number=nil) @content = [] @subject = nil @line_number = line_number end |
Instance Attribute Details
#content ⇒ Object (readonly)
An array of comment fragments organized into lines
216 217 218 |
# File 'lib/tap/support/lazydoc/comment.rb', line 216 def content @content end |
#line_number ⇒ Object
Returns the line number for the subject line, if known. Although normally an integer, line_number may be set to a Regexp or Proc to dynamically determine the subject line during resolve
227 228 229 |
# File 'lib/tap/support/lazydoc/comment.rb', line 227 def line_number @line_number end |
#subject ⇒ Object
The subject of the comment (normally set to the next non-comment line after the content ends; ie the line that would receive the comment in RDoc documentation)
221 222 223 |
# File 'lib/tap/support/lazydoc/comment.rb', line 221 def subject @subject end |
Class Method Details
.parse(str, parse_subject = true) ⇒ Object
Parses the input string into a comment. Takes a string or a StringScanner and returns the comment.
comment_string = %Q{
# comments spanning multiple
# lines are collected
#
# while indented lines
# are preserved individually
#
this is the subject line
# this line is not parsed
}
c = Comment.parse(comment_string)
c.content
# => [
# ['comments spanning multiple', 'lines are collected'],
# [''],
# [' while indented lines'],
# [' are preserved individually'],
# [''],
# []]
c.subject # => "this is the subject line"
Parsing may be manually ended by providing a block; parse yields each line fragment to the block and stops parsing when the block returns true. Note that no subject will be parsed under these circumstances.
c = Comment.parse(comment_string) {|frag| frag.strip.empty? }
c.content
# => [
# ['comments spanning multiple', 'lines are collected']]
c.subject # => nil
Subject parsing may also be suppressed by setting parse_subject to false.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/tap/support/lazydoc/comment.rb', line 103 def parse(str, parse_subject=true) # :yields: fragment 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 comment = Comment.new while scanner.scan(/\r?\n?[ \t]*#[ \t]?(([ \t]*).*?)\r?$/) fragment = scanner[1] indent = scanner[2] # collect continuous description line # fragments and join into a single line if block_given? && yield(fragment) # break on comment if the description end is reached parse_subject = false break else categorize(fragment, indent) {|f| comment.push(f) } end end if parse_subject scanner.skip(/\s+/) unless scanner.peek(1) == '#' comment.subject = scanner.scan(/.+?$/) comment.subject.strip! unless comment.subject == nil end end comment end |
.scan(line) ⇒ Object
Scan determines if and how to add a line fragment to a comment and yields the appropriate fragments to the block. Returns true if fragments are yielded and false otherwise.
Content may be built from an array of lines using scan like so:
lines = [
"# comments spanning multiple",
"# lines are collected",
"#",
"# while indented lines",
"# are preserved individually",
"# ",
"not a comment line",
"# skipped since the loop breaks",
"# at the first non-comment line"]
c = Comment.new
lines.each do |line|
break unless Comment.scan(line) do |fragment|
c.push(fragment)
end
end
c.content
# => [
# ['comments spanning multiple', 'lines are collected'],
# [''],
# [' while indented lines'],
# [' are preserved individually'],
# [''],
# []]
170 171 172 173 174 175 176 |
# File 'lib/tap/support/lazydoc/comment.rb', line 170 def scan(line) # :yields: fragment return false unless line =~ /^[ \t]*#[ \t]?(([ \t]*).*?)\r?$/ categorize($1, $2) do |fragment| yield(fragment) end true end |
.wrap(line, cols = 80, tabsize = 2) ⇒ Object
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"]
Comment.wrap(" line that will wrap ", 10) # => [" line", "that will", "wrap"]
Comment.wrap(" ", 10) # => []
The wrapping algorithm is slightly modified from: blog.macromates.com/2006/wrapping-text-with-regular-expressions/
188 189 190 191 |
# File 'lib/tap/support/lazydoc/comment.rb', line 188 def wrap(line, cols=80, tabsize=2) line = line.gsub(/\t/, " " * tabsize) unless tabsize == nil line.gsub(/(.{1,#{cols}})( +|$\r?\n?)|(.{1,#{cols}})/, "\\1\\3\n").split(/\s*?\n/) end |
Instance Method Details
#<<(fragment) ⇒ Object
Alias for push.
275 276 277 |
# File 'lib/tap/support/lazydoc/comment.rb', line 275 def <<(fragment) push(fragment) end |
#==(another) ⇒ Object
Returns true if another is a Comment with the same line_number, subject, and content as self
483 484 485 486 487 488 |
# File 'lib/tap/support/lazydoc/comment.rb', line 483 def ==(another) another.kind_of?(Comment) && self.line_number == another.line_number && self.subject == another.subject && self.content == another.content end |
#append(line) ⇒ Object
Scans the comment line using Comment.scan and pushes the appropriate fragments onto self. Used to build a content by scanning down a set of lines.
lines = [
"# comment spanning multiple",
"# lines",
"#",
"# indented line one",
"# indented line two",
"# ",
"not a comment line"]
c = Comment.new
lines.each {|line| c.append(line) }
c.content
# => [
# ['comment spanning multiple', 'lines'],
# [''],
# [' indented line one'],
# [' indented line two'],
# [''],
# []]
304 305 306 |
# File 'lib/tap/support/lazydoc/comment.rb', line 304 def append(line) Comment.scan(line) {|f| push(f) } end |
#empty? ⇒ Boolean
True if all lines in content are empty.
456 457 458 |
# File 'lib/tap/support/lazydoc/comment.rb', line 456 def empty? !content.find {|line| !line.empty?} end |
#prepend(line) ⇒ Object
Scans the comment line using Comment.scan and unshifts the appropriate fragments onto self. Used to build a content by scanning up a set of lines.
lines = [
"# comment spanning multiple",
"# lines",
"#",
"# indented line one",
"# indented line two",
"# ",
"not a comment line"]
c = Comment.new
lines.reverse_each {|line| c.prepend(line) }
c.content
# => [
# ['comment spanning multiple', 'lines'],
# [''],
# [' indented line one'],
# [' indented line two'],
# ['']]
361 362 363 |
# File 'lib/tap/support/lazydoc/comment.rb', line 361 def prepend(line) Comment.scan(line) {|f| unshift(f) } end |
#push(fragment) ⇒ Object
Pushes the fragment onto the last line array of content. If the fragment is an array itself then it will be pushed onto content as a new line.
c = Comment.new
c.push "some line"
c.push "fragments"
c.push ["a", "whole", "new line"]
c.content
# => [
# ["some line", "fragments"],
# ["a", "whole", "new line"]]
259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/tap/support/lazydoc/comment.rb', line 259 def push(fragment) content << [] if content.empty? case fragment when Array if content[-1].empty? content[-1] = fragment else content.push fragment end else content[-1].push fragment end end |
#resolve(lines) ⇒ Object
Builds the subject and content of self using lines; resolve sets the subject to the line at line_number, and parses content up from there. Any previously set subject and content is overridden.
Returns self.
document = %Q{
module Sample
# this is the content of the comment
# for method_one
def method_one
end
# this is the content of the comment
# for method_two
def method_two
end
end}
c = Comment.new 4
c.resolve(document)
c.subject # => " def method_one"
c.content # => [["this is the content of the comment", "for method_one"]]
Lines may be an array or a string; string inputs are split into an array along newline boundaries.
dynamic line numbers
The line_number used by resolve may be determined dynamically from lines by setting line_number to a Regexp and Proc. In the case of a Regexp, the first line matching the regexp is used:
c = Comment.new(/def method/)
c.resolve(document)
c.line_number = 4
c.subject # => " def method_one"
c.content # => [["this is the content of the comment", "for method_one"]]
Procs are called with lines and are expected to return the actual line number.
c = Comment.new lambda {|lines| 9 }
c.resolve(document)
c.line_number = 9
c.subject # => " def method_two"
c.content # => [["this is the content of the comment", "for method_two"]]
As shown in the examples, in both cases the dynamically determined line_number overwrites the Regexp or Proc.
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/tap/support/lazydoc/comment.rb', line 413 def resolve(lines) lines = lines.split(/\r?\n/) if lines.kind_of?(String) # resolve late-evaluation line numbers n = case line_number when Regexp then match_index(line_number, lines) when Proc then line_number.call(lines) else line_number end # quietly exit if a line number was not found return self unless n.kind_of?(Integer) unless n < lines.length raise RangeError, "line_number outside of lines: #{line_number} (#{lines.length})" end self.line_number = n self.subject = lines[n] self.content.clear # remove whitespace lines n -= 1 n -= 1 while n >=0 && lines[n].strip.empty? # put together the comment while n >= 0 break unless prepend(lines[n]) n -= 1 end self end |
#to_s(fragment_sep = " ", line_sep = "\n", strip = true) ⇒ Object
Returns content as a string where line fragments are joined by fragment_sep and lines are joined by line_sep.
462 463 464 465 466 467 468 469 470 471 472 |
# File 'lib/tap/support/lazydoc/comment.rb', line 462 def to_s(fragment_sep=" ", line_sep="\n", strip=true) lines = content.collect {|line| line.join(fragment_sep)} # strip leading an trailing whitespace lines if strip lines.shift while !lines.empty? && lines[0].empty? lines.pop while !lines.empty? && lines[-1].empty? end line_sep ? lines.join(line_sep) : lines end |
#trim ⇒ Object
Removes leading and trailing lines from content that are empty ([]) or whitespace ([”]). Returns self.
449 450 451 452 453 |
# File 'lib/tap/support/lazydoc/comment.rb', line 449 def trim content.shift while !content.empty? && (content[0].empty? || content[0].join.strip.empty?) content.pop while !content.empty? && (content[-1].empty? || content[-1].join.strip.empty?) self end |
#unshift(fragment) ⇒ Object
Unshifts the fragment to the first line array of content. If the fragment is an array itself then it will be unshifted onto content as a new line.
c = Comment.new
c.unshift "some line"
c.unshift "fragments"
c.unshift ["a", "whole", "new line"]
c.content
# => [
# ["a", "whole", "new line"],
# ["fragments", "some line"]]
322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/tap/support/lazydoc/comment.rb', line 322 def unshift(fragment) content << [] if content.empty? case fragment when Array if content[0].empty? content[0] = fragment else content.unshift fragment end else content[0].unshift fragment end end |
#value ⇒ Object
Alias for subject
236 237 238 |
# File 'lib/tap/support/lazydoc/comment.rb', line 236 def value subject end |
#value=(value) ⇒ Object
Alias for subject=
241 242 243 |
# File 'lib/tap/support/lazydoc/comment.rb', line 241 def value=(value) self.subject = value end |
#wrap(cols = 80, tabsize = 2, line_sep = "\n", fragment_sep = " ", strip = true) ⇒ Object
Like to_s, but wraps the content to the specified number of cols and expands tabs to tabsize spaces.
476 477 478 479 |
# File 'lib/tap/support/lazydoc/comment.rb', line 476 def wrap(cols=80, tabsize=2, line_sep="\n", fragment_sep=" ", strip=true) lines = Comment.wrap(to_s(fragment_sep, "\n", strip), cols, tabsize) line_sep ? lines.join(line_sep) : lines end |