Module: Lazydoc::Utils

Included in:
Comment
Defined in:
lib/lazydoc/utils.rb

Overview

A number of utility methods used by Comment, factored out for testing and re-use.

Class Method Summary collapse

Class Method Details

.categorize(fragment, indent) ⇒ Object

utility method used by scan to categorize and yield the appropriate objects to add the fragment to a comment



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/lazydoc/utils.rb', line 219

def categorize(fragment, indent) # :nodoc:
  case
  when fragment == indent
    # empty comment line
    yield [""]
    yield []
  when indent.empty?
    # continuation line
    yield fragment.rstrip
  else 
    # indented line
    yield [fragment.rstrip]
    yield []
  end
end

.convert_to_scanner(str) ⇒ Object

Converts str to a StringScanner (or returns str if it already is a StringScanner). Raises a TypeError if str is not a String or a StringScanner.



17
18
19
20
21
22
23
# File 'lib/lazydoc/utils.rb', line 17

def convert_to_scanner(str)
  case str
  when String then StringScanner.new(str)
  when StringScanner then str
  else raise TypeError, "can't convert #{str.class} into StringScanner"
  end
end

.determine_line_number(scanner) ⇒ Object

Returns the line at which scanner currently resides. The position of scanner is not modified.



177
178
179
# File 'lib/lazydoc/utils.rb', line 177

def determine_line_number(scanner)
  scanner.string[0, scanner.pos].count("\n")
end

.match_index(lines, regexp) ⇒ Object

Returns the index of the line in lines matching regexp, or nil if no line matches regexp.



200
201
202
203
204
205
206
207
# File 'lib/lazydoc/utils.rb', line 200

def match_index(lines, regexp)
  index = 0
  lines.each do |line|
    return index if line =~ regexp
    index += 1
  end
  nil
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 Utils.scan(line) do |fragment|
    c.push(fragment)  
  end
end

c.content   
# => [
# ['comments spanning multiple', 'lines are collected'],
# [''],
# ['  while indented lines'],
# ['  are preserved individually'],
# [''],
# []]


58
59
60
61
62
63
64
# File 'lib/lazydoc/utils.rb', line 58

def scan(line) # :yields: fragment
  return false unless line =~ /^[ \t]*#[ \t]?(([ \t]*).*?)\r?$/
  categorize($1, $2) do |fragment|
    yield(fragment)
  end
  true
end

.scan_args(str) ⇒ Object

Parses an argument string (anything following the method name in a standard method definition, including parenthesis, comments, default values, etc) into an array of strings.

Utils.parse_args("(a, b='default', *c, &block)")  
# => ["a", "b='default'", "*c", "&block"]

Note the %-syntax for strings and arrays is not fully supported, ie %w, %Q, %q, etc. may not parse correctly. The same is true for multiline argument strings.

Accepts a String or a StringScanner.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/lazydoc/utils.rb', line 78

def scan_args(str)
  scanner = convert_to_scanner(str)
  str = scanner.string
  
  # skip whitespace and leading LPAREN
  scanner.skip(/\s*\(?\s*/) 
  
  args = []
  brakets = braces = parens = 0
  start = scanner.pos
  broke = false
  while scanner.skip(/.*?['"#,\(\)\{\}\[\]]/)
    pos = scanner.pos - 1
    
    case str[pos]
    when ?,,nil
      # skip if in brakets, braces, or parenthesis
      next if parens > 0 || brakets > 0 || braces > 0
      
      # ok, found an arg
      args << str[start, pos-start].strip
      start = pos + 1
    
    when ?# then broke = true; break        # break on a comment
    when ?' then skip_quote(scanner, /'/)   # parse over quoted strings
    when ?" then skip_quote(scanner, /"/)   # parse over double-quoted string
      
    when ?( then parens += 1                # for brakets, braces, and parenthesis
    when ?)                                 # simply track the nesting EXCEPT for
      if parens == 0                        # RPAREN.  If the closing parenthesis
        broke = true; break
      end
      parens -= 1                           # is found, break.
    when ?[ then braces += 1
    when ?] then braces -= 1
    when ?{ then brakets += 1
    when ?} then brakets -= 1
    end
  end
  
  # parse out the final arg.  if the loop broke (ie 
  # a comment or the closing parenthesis was found) 
  # then the end position is determined by the 
  # scanner, otherwise take all that remains
  pos = broke ? scanner.pos-1 : str.length
  args << str[start, pos-start].strip unless pos == start

  args
end

.scan_index(scanner, regexp) ⇒ Object

Returns the index of the line where scanner ends up after the first match to regexp (starting at position 0). The existing position of scanner is not modified by this method. Returns nil if the scanner cannot match regexp.

scanner = StringScanner.new %Q{zero\none\ntwo\nthree}
Utils.scan_index(scanner, /two/)         # => 2
Utils.scan_index(scanner, /no match/)    # => nil


190
191
192
193
194
195
196
# File 'lib/lazydoc/utils.rb', line 190

def scan_index(scanner, regexp)
  pos = scanner.pos
  scanner.pos = 0
  n = scanner.skip_until(regexp) ? determine_line_number(scanner) : nil
  scanner.pos = pos
  n
end

.scan_trailer(str) ⇒ Object

Scans a stripped trailing comment off the input. Returns nil for strings without a trailing comment.

Utils.scan_trailer "str with # trailer"           # => "trailer"
Utils.scan_trailer "'# in str' # trailer"         # => "trailer"
Utils.scan_trailer "str with without trailer"     # => nil

Note the %Q and %q syntax for defining strings is not supported within the leader and may not parse correctly:

Utils.scan_trailer "%Q{# in str} # trailer"       # => "in str} # trailer"

Accepts a String or a StringScanner.



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/lazydoc/utils.rb', line 141

def scan_trailer(str)
  scanner = convert_to_scanner(str)

  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

.skip_quote(scanner, regexp) ⇒ Object

helper method to skip to the next non-escaped instance matching the quote regexp (/‘/ or /“/).



211
212
213
214
# File 'lib/lazydoc/utils.rb', line 211

def skip_quote(scanner, regexp) # :nodoc:
  scanner.skip_until(regexp)
  scanner.skip_until(regexp) while scanner.string[scanner.pos-2] == ?\\
end

.split_lines(str) ⇒ Object



10
11
12
# File 'lib/lazydoc/utils.rb', line 10

def split_lines(str)
  (str.empty? ? [""] : str.split(/\r?\n/))
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.

Utils.wrap("some line that will wrap", 10)       # => ["some line", "that will", "wrap"]
Utils.wrap("     line that will wrap    ", 10)   # => ["     line", "that will", "wrap"]
Utils.wrap("                            ", 10)   # => []

The wrapping algorithm is slightly modified from: blog.macromates.com/2006/wrapping-text-with-regular-expressions/



170
171
172
173
# File 'lib/lazydoc/utils.rb', line 170

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