lazydoc 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ module Lazydoc
2
+
3
+ # A special type of self-resolving Comment that whose to_s returns the
4
+ # trailer, or an empty string if trailer is nil.
5
+ #
6
+ # t = Trailer.new
7
+ # t.subject = "def method # trailer string"
8
+ # t.to_s # => "trailer string"
9
+ #
10
+ class Trailer < Comment
11
+
12
+ # Self-resolves and returns trailer, or an empty
13
+ # string if trailer is nil.
14
+ def to_s
15
+ resolve
16
+ trailer.to_s
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,232 @@
1
+ require 'strscan'
2
+
3
+ module Lazydoc
4
+
5
+ # A number of utility methods used by Comment, factored out for
6
+ # testing and re-use.
7
+ module Utils
8
+ module_function
9
+
10
+ def split_lines(str)
11
+ (str.empty? ? [""] : str.split(/\r?\n/))
12
+ end
13
+
14
+ # Converts str to a StringScanner (or returns str if it already is
15
+ # a StringScanner). Raises a TypeError if str is not a String
16
+ # or a StringScanner.
17
+ def convert_to_scanner(str)
18
+ case str
19
+ when String then StringScanner.new(str)
20
+ when StringScanner then str
21
+ else raise TypeError, "can't convert #{str.class} into StringScanner"
22
+ end
23
+ end
24
+
25
+ # Scan determines if and how to add a line fragment to a comment and
26
+ # yields the appropriate fragments to the block. Returns true if
27
+ # fragments are yielded and false otherwise.
28
+ #
29
+ # Content may be built from an array of lines using scan like so:
30
+ #
31
+ # lines = [
32
+ # "# comments spanning multiple",
33
+ # "# lines are collected",
34
+ # "#",
35
+ # "# while indented lines",
36
+ # "# are preserved individually",
37
+ # "# ",
38
+ # "not a comment line",
39
+ # "# skipped since the loop breaks",
40
+ # "# at the first non-comment line"]
41
+ #
42
+ # c = Comment.new
43
+ # lines.each do |line|
44
+ # break unless Utils.scan(line) do |fragment|
45
+ # c.push(fragment)
46
+ # end
47
+ # end
48
+ #
49
+ # c.content
50
+ # # => [
51
+ # # ['comments spanning multiple', 'lines are collected'],
52
+ # # [''],
53
+ # # [' while indented lines'],
54
+ # # [' are preserved individually'],
55
+ # # [''],
56
+ # # []]
57
+ #
58
+ def scan(line) # :yields: fragment
59
+ return false unless line =~ /^[ \t]*#[ \t]?(([ \t]*).*?)\r?$/
60
+ categorize($1, $2) do |fragment|
61
+ yield(fragment)
62
+ end
63
+ true
64
+ end
65
+
66
+ # Parses an argument string (anything following the method name in a
67
+ # standard method definition, including parenthesis, comments, default
68
+ # values, etc) into an array of strings.
69
+ #
70
+ # Utils.parse_args("(a, b='default', *c, &block)")
71
+ # # => ["a", "b='default'", "*c", "&block"]
72
+ #
73
+ # Note the %-syntax for strings and arrays is not fully supported,
74
+ # ie %w, %Q, %q, etc. may not parse correctly. The same is true
75
+ # for multiline argument strings.
76
+ #
77
+ # Accepts a String or a StringScanner.
78
+ def scan_args(str)
79
+ scanner = convert_to_scanner(str)
80
+ str = scanner.string
81
+
82
+ # skip whitespace and leading LPAREN
83
+ scanner.skip(/\s*\(?\s*/)
84
+
85
+ args = []
86
+ brakets = braces = parens = 0
87
+ start = scanner.pos
88
+ broke = while scanner.skip(/.*?['"#,\(\)\{\}\[\]]/)
89
+ pos = scanner.pos - 1
90
+
91
+ case str[pos]
92
+ when ?,,nil
93
+ # skip if in brakets, braces, or parenthesis
94
+ next if parens > 0 || brakets > 0 || braces > 0
95
+
96
+ # ok, found an arg
97
+ args << str[start, pos-start].strip
98
+ start = pos + 1
99
+
100
+ when ?# then break(true) # break on a comment
101
+ when ?' then skip_quote(scanner, /'/) # parse over quoted strings
102
+ when ?" then skip_quote(scanner, /"/) # parse over double-quoted string
103
+
104
+ when ?( then parens += 1 # for brakets, braces, and parenthesis
105
+ when ?) # simply track the nesting EXCEPT for
106
+ break(true) if parens == 0 # RPAREN. If the closing parenthesis
107
+ parens -= 1 # is found, break.
108
+ when ?[ then braces += 1
109
+ when ?] then braces -= 1
110
+ when ?{ then brakets += 1
111
+ when ?} then brakets -= 1
112
+ end
113
+ end
114
+
115
+ # parse out the final arg. if the loop broke (ie
116
+ # a comment or the closing parenthesis was found)
117
+ # then the end position is determined by the
118
+ # scanner, otherwise take all that remains
119
+ pos = broke ? scanner.pos-1 : str.length
120
+ args << str[start, pos-start].strip unless pos == start
121
+
122
+ args
123
+ end
124
+
125
+ # Scans a stripped trailing comment off the input. Returns nil for
126
+ # strings without a trailing comment.
127
+ #
128
+ # Utils.scan_trailer "str with # trailer" # => "trailer"
129
+ # Utils.scan_trailer "'# in str' # trailer" # => "trailer"
130
+ # Utils.scan_trailer "str with without trailer" # => nil
131
+ #
132
+ # Note the %Q and %q syntax for defining strings is not supported
133
+ # within the leader and may not parse correctly:
134
+ #
135
+ # Utils.scan_trailer "%Q{# in str} # trailer" # => "in str} # trailer"
136
+ #
137
+ # Accepts a String or a StringScanner.
138
+ def scan_trailer(str)
139
+ scanner = convert_to_scanner(str)
140
+
141
+ args = []
142
+ brakets = braces = parens = 0
143
+ start = scanner.pos
144
+ while scanner.skip(/.*?['"#]/)
145
+ pos = scanner.pos - 1
146
+
147
+ case str[pos]
148
+ when ?# then return scanner.rest.strip # return the trailer
149
+ when ?' then skip_quote(scanner, /'/) # parse over quoted strings
150
+ when ?" then skip_quote(scanner, /"/) # parse over double-quoted string
151
+ end
152
+ end
153
+
154
+ return nil
155
+ end
156
+
157
+ # Splits a line of text along whitespace breaks into fragments of cols
158
+ # width. Tabs in the line will be expanded into tabsize spaces;
159
+ # fragments are rstripped of whitespace.
160
+ #
161
+ # Utils.wrap("some line that will wrap", 10) # => ["some line", "that will", "wrap"]
162
+ # Utils.wrap(" line that will wrap ", 10) # => [" line", "that will", "wrap"]
163
+ # Utils.wrap(" ", 10) # => []
164
+ #
165
+ # The wrapping algorithm is slightly modified from:
166
+ # http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
167
+ def wrap(line, cols=80, tabsize=2)
168
+ line = line.gsub(/\t/, " " * tabsize) unless tabsize == nil
169
+ line.gsub(/(.{1,#{cols}})( +|$\r?\n?)|(.{1,#{cols}})/, "\\1\\3\n").split(/\s*?\n/)
170
+ end
171
+
172
+ # Returns the line at which scanner currently resides. The position
173
+ # of scanner is not modified.
174
+ def determine_line_number(scanner)
175
+ scanner.string[0, scanner.pos].count("\n")
176
+ end
177
+
178
+ # Returns the index of the line where scanner ends up after the first
179
+ # match to regexp (starting at position 0). The existing position of
180
+ # scanner is not modified by this method. Returns nil if the scanner
181
+ # cannot match regexp.
182
+ #
183
+ # scanner = StringScanner.new %Q{zero\none\ntwo\nthree}
184
+ # Utils.scan_index(scanner, /two/) # => 2
185
+ # Utils.scan_index(scanner, /no match/) # => nil
186
+ #
187
+ def scan_index(scanner, regexp)
188
+ pos = scanner.pos
189
+ scanner.pos = 0
190
+ n = scanner.skip_until(regexp) ? determine_line_number(scanner) : nil
191
+ scanner.pos = pos
192
+ n
193
+ end
194
+
195
+ # Returns the index of the line in lines matching regexp,
196
+ # or nil if no line matches regexp.
197
+ def match_index(lines, regexp)
198
+ index = 0
199
+ lines.each do |line|
200
+ return index if line =~ regexp
201
+ index += 1
202
+ end
203
+ nil
204
+ end
205
+
206
+ # helper method to skip to the next non-escaped instance
207
+ # matching the quote regexp (/'/ or /"/).
208
+ def skip_quote(scanner, regexp) # :nodoc:
209
+ scanner.skip_until(regexp)
210
+ scanner.skip_until(regexp) while scanner.string[scanner.pos-2] == ?\\
211
+ end
212
+
213
+ # utility method used by scan to categorize and yield
214
+ # the appropriate objects to add the fragment to a
215
+ # comment
216
+ def categorize(fragment, indent) # :nodoc:
217
+ case
218
+ when fragment == indent
219
+ # empty comment line
220
+ yield [""]
221
+ yield []
222
+ when indent.empty?
223
+ # continuation line
224
+ yield fragment.rstrip
225
+ else
226
+ # indented line
227
+ yield [fragment.rstrip]
228
+ yield []
229
+ end
230
+ end
231
+ end
232
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lazydoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Chiang
@@ -9,19 +9,10 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-07 00:00:00 -07:00
12
+ date: 2008-12-31 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: tap
17
- type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 0.11.1
24
- version:
14
+ dependencies: []
15
+
25
16
  description:
26
17
  email: simon.a.chiang@gmail.com
27
18
  executables: []
@@ -33,17 +24,26 @@ extra_rdoc_files:
33
24
  - MIT-LICENSE
34
25
  files:
35
26
  - lib/lazydoc.rb
27
+ - lib/lazydoc/arguments.rb
36
28
  - lib/lazydoc/attributes.rb
37
29
  - lib/lazydoc/comment.rb
38
30
  - lib/lazydoc/document.rb
39
31
  - lib/lazydoc/method.rb
32
+ - lib/lazydoc/subject.rb
33
+ - lib/lazydoc/trailer.rb
34
+ - lib/lazydoc/utils.rb
40
35
  - README
41
36
  - MIT-LICENSE
42
37
  has_rdoc: true
43
38
  homepage: http://tap.rubyforge.org/lazydoc
44
39
  post_install_message:
45
- rdoc_options: []
46
-
40
+ rdoc_options:
41
+ - --title
42
+ - Lazydoc
43
+ - --main
44
+ - README
45
+ - --line-numbers
46
+ - --inline-source
47
47
  require_paths:
48
48
  - lib
49
49
  required_ruby_version: !ruby/object:Gem::Requirement