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.
- data/README +52 -76
- data/lib/lazydoc.rb +48 -155
- data/lib/lazydoc/arguments.rb +26 -0
- data/lib/lazydoc/attributes.rb +111 -24
- data/lib/lazydoc/comment.rb +160 -353
- data/lib/lazydoc/document.rb +177 -101
- data/lib/lazydoc/method.rb +3 -77
- data/lib/lazydoc/subject.rb +19 -0
- data/lib/lazydoc/trailer.rb +19 -0
- data/lib/lazydoc/utils.rb +232 -0
- metadata +15 -15
@@ -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.
|
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-
|
12
|
+
date: 2008-12-31 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
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
|