mvz-dnote 1.7.1
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.
- checksums.yaml +7 -0
- data/COPYING.rdoc +31 -0
- data/HISTORY.rdoc +171 -0
- data/README.rdoc +113 -0
- data/bin/dnote +4 -0
- data/lib/dnote.rb +4 -0
- data/lib/dnote.yml +1 -0
- data/lib/dnote/core_ext.rb +92 -0
- data/lib/dnote/format.rb +173 -0
- data/lib/dnote/note.rb +150 -0
- data/lib/dnote/notes.rb +320 -0
- data/lib/dnote/rake/dnotetask.rb +116 -0
- data/lib/dnote/session.rb +269 -0
- data/lib/dnote/templates/html.erb +49 -0
- data/lib/dnote/templates/html/file.erb +48 -0
- data/lib/dnote/templates/html/label.erb +49 -0
- data/lib/dnote/templates/html/list.erb +42 -0
- data/lib/dnote/templates/json.erb +8 -0
- data/lib/dnote/templates/json/file.erb +8 -0
- data/lib/dnote/templates/json/label.erb +8 -0
- data/lib/dnote/templates/json/list.erb +8 -0
- data/lib/dnote/templates/md.erb +19 -0
- data/lib/dnote/templates/md/file.erb +14 -0
- data/lib/dnote/templates/md/label.erb +19 -0
- data/lib/dnote/templates/md/list.erb +10 -0
- data/lib/dnote/templates/rdoc.erb +12 -0
- data/lib/dnote/templates/rdoc/file.erb +10 -0
- data/lib/dnote/templates/rdoc/label.erb +12 -0
- data/lib/dnote/templates/rdoc/list.erb +7 -0
- data/lib/dnote/templates/soap.erb +4 -0
- data/lib/dnote/templates/soap/file.erb +4 -0
- data/lib/dnote/templates/soap/label.erb +4 -0
- data/lib/dnote/templates/soap/list.erb +4 -0
- data/lib/dnote/templates/text.erb +12 -0
- data/lib/dnote/templates/text/file.erb +11 -0
- data/lib/dnote/templates/text/label.erb +11 -0
- data/lib/dnote/templates/text/list.erb +8 -0
- data/lib/dnote/templates/xml.erb +23 -0
- data/lib/dnote/templates/xml/file.erb +23 -0
- data/lib/dnote/templates/xml/label.erb +23 -0
- data/lib/dnote/templates/xml/list.erb +14 -0
- data/lib/dnote/templates/xoxo.erb +4 -0
- data/lib/dnote/templates/xoxo/file.erb +4 -0
- data/lib/dnote/templates/xoxo/label.erb +4 -0
- data/lib/dnote/templates/xoxo/list.erb +6 -0
- data/lib/dnote/templates/yaml.erb +1 -0
- data/lib/dnote/templates/yaml/file.erb +1 -0
- data/lib/dnote/templates/yaml/label.erb +1 -0
- data/lib/dnote/templates/yaml/list.erb +1 -0
- data/lib/dnote/version.rb +17 -0
- data/test/notes_case.rb +59 -0
- data/try/sample.bas +7 -0
- data/try/sample.js +11 -0
- data/try/sample.rb +11 -0
- metadata +131 -0
data/lib/dnote/note.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
module DNote
|
2
|
+
|
3
|
+
# The Note class encapsulates a single note made in a source file.
|
4
|
+
#
|
5
|
+
# Each note instance holds a reference, +notes+, to the set of notes
|
6
|
+
# being generated for a given session. This allows the note to access
|
7
|
+
# general options applicable to all notes.
|
8
|
+
class Note
|
9
|
+
|
10
|
+
# Number of lines to provide in source context.
|
11
|
+
#CONTEXT_DEPTH = 5
|
12
|
+
|
13
|
+
# Set of notes to which this note belongs.
|
14
|
+
attr :notes
|
15
|
+
|
16
|
+
# The file in which the note is made.
|
17
|
+
attr :file
|
18
|
+
|
19
|
+
# The type of note.
|
20
|
+
attr :label
|
21
|
+
|
22
|
+
# The line number of the note.
|
23
|
+
attr :line
|
24
|
+
|
25
|
+
# The verbatim text of the note.
|
26
|
+
attr :text
|
27
|
+
|
28
|
+
# Remark marker used in parsing the note.
|
29
|
+
attr :mark
|
30
|
+
|
31
|
+
# Contextual lines of code.
|
32
|
+
attr :capture
|
33
|
+
|
34
|
+
# Initialize new Note instance.
|
35
|
+
def initialize(notes, file, label, line, text, mark)
|
36
|
+
@notes = notes
|
37
|
+
|
38
|
+
@file = file
|
39
|
+
@label = label
|
40
|
+
@line = line
|
41
|
+
@text = text.rstrip
|
42
|
+
@mark = mark
|
43
|
+
@capture = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Convert to string representation.
|
47
|
+
def to_s
|
48
|
+
"#{label}: #{text}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Convert to string representation.
|
52
|
+
def to_str
|
53
|
+
"#{label}: #{text}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Remove newlines from note text.
|
57
|
+
def textline
|
58
|
+
text.gsub("\n", " ")
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sort by file name and line number.
|
62
|
+
def <=>(other)
|
63
|
+
s = file <=> other.file
|
64
|
+
return s unless s == 0
|
65
|
+
line <=> other.line
|
66
|
+
end
|
67
|
+
|
68
|
+
# Convert to Hash.
|
69
|
+
#--
|
70
|
+
# TODO: Add +url+?
|
71
|
+
# TODO: Add +code+? Problem is that xml needs code in CDATA.
|
72
|
+
#++
|
73
|
+
def to_h
|
74
|
+
{ 'label'=>label, 'text'=>textline, 'file'=>file, 'line'=>line }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Convert to Hash, leaving the note text verbatim.
|
78
|
+
def to_h_raw
|
79
|
+
{ 'label'=>label, 'text'=>text, 'file'=>file, 'line'=>line, 'code'=>code }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Convert to JSON.
|
83
|
+
def to_json(*args)
|
84
|
+
to_h_raw.to_json(*args)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Convert to YAML.
|
88
|
+
def to_yaml(*args)
|
89
|
+
to_h_raw.to_yaml(*args)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Return line URL based on URL template. If no template was set, then
|
93
|
+
# returns the file.
|
94
|
+
def url
|
95
|
+
if notes.url
|
96
|
+
notes.url % [file, line]
|
97
|
+
else
|
98
|
+
file
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
def code
|
104
|
+
unindent(capture).join
|
105
|
+
end
|
106
|
+
|
107
|
+
# Is there code to show?
|
108
|
+
def code?
|
109
|
+
!capture.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
=begin
|
113
|
+
# This isn't being used currently b/c the URL solution as deeemd better,
|
114
|
+
# but the code is here for custom templates.
|
115
|
+
def capture
|
116
|
+
@context ||= (
|
117
|
+
lines = file_cache(file) #.lines.to_a
|
118
|
+
count = line()
|
119
|
+
count +=1 while /^\s*#{mark}/ =~ lines[count]
|
120
|
+
lines[count, context_depth]
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Read in +file+, parse into lines and cache.
|
125
|
+
def file_cache(file)
|
126
|
+
@@file_cache ||= {}
|
127
|
+
@@file_cache[file] ||= File.read(file).lines.to_a
|
128
|
+
end
|
129
|
+
=end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# Remove blank space from lines.
|
134
|
+
def unindent(lines)
|
135
|
+
dents = []
|
136
|
+
lines.each do |line|
|
137
|
+
if md = /^([\ ]*)/.match(line)
|
138
|
+
size = md[1].size
|
139
|
+
dents << md[1]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
dent = dents.min{ |a,b| a.size <=> b.size }
|
143
|
+
lines.map do |line|
|
144
|
+
line.sub(dent, '')
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
data/lib/dnote/notes.rb
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'dnote/note'
|
3
|
+
|
4
|
+
module DNote
|
5
|
+
|
6
|
+
# = Developer Notes
|
7
|
+
#
|
8
|
+
# This class goes through you source files and compiles a list
|
9
|
+
# of any labeled comments. Labels are all-cap single word prefixes
|
10
|
+
# to a comment ending in a colon.
|
11
|
+
#
|
12
|
+
# Special labels do not require the colon. By default these are
|
13
|
+
# +TODO+, +FIXME+, +OPTIMIZE+, +THINK+ and +DEPRECATE+.
|
14
|
+
#
|
15
|
+
#--
|
16
|
+
# TODO: Add ability to read header notes. They often
|
17
|
+
# have a outline format, rather then the single line.
|
18
|
+
#++
|
19
|
+
class Notes
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
# Default paths (all ruby scripts).
|
23
|
+
DEFAULT_PATHS = ["**/*.rb"]
|
24
|
+
|
25
|
+
# Default note labels to look for in source code. (NOT CURRENTLY USED!)
|
26
|
+
DEFAULT_LABELS = ['TODO', 'FIXME', 'OPTIMIZE', 'THINK', 'DEPRECATE']
|
27
|
+
|
28
|
+
# Files to search for notes.
|
29
|
+
attr_accessor :files
|
30
|
+
|
31
|
+
# Labels to document. Defaults are: +TODO+, +FIXME+, +OPTIMIZE+ and +DEPRECATE+.
|
32
|
+
attr_accessor :labels
|
33
|
+
|
34
|
+
# Require label colon? Default is +true+.
|
35
|
+
attr_accessor :colon
|
36
|
+
|
37
|
+
# Specific remark marker (+nil+ for auto).
|
38
|
+
attr_accessor :marker
|
39
|
+
|
40
|
+
# Link template.
|
41
|
+
attr_accessor :url
|
42
|
+
|
43
|
+
# Number of lines of context to show.
|
44
|
+
attr_accessor :context
|
45
|
+
|
46
|
+
# New set of notes for give +files+ and optional special labels.
|
47
|
+
def initialize(files, options={})
|
48
|
+
@files = [files].flatten
|
49
|
+
@labels = [options[:labels] || DEFAULT_LABELS].flatten.compact
|
50
|
+
@colon = options[:colon].nil? ? true : options[:colon]
|
51
|
+
@marker = options[:marker] #|| '#'
|
52
|
+
@url = options[:url]
|
53
|
+
@context = options[:context] || 0
|
54
|
+
|
55
|
+
@remark = {}
|
56
|
+
|
57
|
+
parse
|
58
|
+
end
|
59
|
+
|
60
|
+
# Array of notes.
|
61
|
+
def notes
|
62
|
+
@notes
|
63
|
+
end
|
64
|
+
|
65
|
+
# Notes counts by label.
|
66
|
+
def counts
|
67
|
+
@counts ||= (
|
68
|
+
h = {}
|
69
|
+
by_label.each do |label, notes|
|
70
|
+
h[label] = notes.size
|
71
|
+
end
|
72
|
+
h
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Iterate through notes.
|
77
|
+
def each(&block)
|
78
|
+
notes.each(&block)
|
79
|
+
end
|
80
|
+
|
81
|
+
# No notes?
|
82
|
+
def empty?
|
83
|
+
notes.empty?
|
84
|
+
end
|
85
|
+
|
86
|
+
# Gather notes.
|
87
|
+
#--
|
88
|
+
# TODO: Play golf with Notes#parse.
|
89
|
+
#++
|
90
|
+
def parse
|
91
|
+
records = []
|
92
|
+
files.each do |fname|
|
93
|
+
next unless File.file?(fname)
|
94
|
+
#next unless fname =~ /\.rb$/ # TODO: should this be done?
|
95
|
+
mark = remark(fname)
|
96
|
+
lineno, note, text, capt = 0, nil, nil, nil
|
97
|
+
File.readlines(fname).each do |line|
|
98
|
+
#while line = f.gets
|
99
|
+
lineno += 1
|
100
|
+
note = match(line, lineno, fname)
|
101
|
+
if note
|
102
|
+
#file = fname
|
103
|
+
text = note.text
|
104
|
+
capt = note.capture
|
105
|
+
#note = {'label'=>label,'file'=>file,'line'=>line_no,'note'=>text}
|
106
|
+
records << note
|
107
|
+
else
|
108
|
+
if text
|
109
|
+
case line
|
110
|
+
when /^\s*#{mark}+\s*$/, /^\s*#{mark}\-\-/, /^\s*#{mark}\+\+/
|
111
|
+
text.strip!
|
112
|
+
text = nil
|
113
|
+
when /^\s*#{mark}/
|
114
|
+
if text[-1,1] == "\n"
|
115
|
+
text << line.gsub(/^\s*#{mark}\s*/,'')
|
116
|
+
else
|
117
|
+
text << "\n" << line.gsub(/^\s*#{mark}\s*/,'')
|
118
|
+
end
|
119
|
+
else
|
120
|
+
text.strip!
|
121
|
+
text = nil
|
122
|
+
end
|
123
|
+
else
|
124
|
+
if line !~ /^\s*#{mark}/
|
125
|
+
capt << line if capt && capt.size < context
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
#end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
@notes = records.sort
|
134
|
+
end
|
135
|
+
|
136
|
+
# Is this line a note?
|
137
|
+
def match(line, lineno, file)
|
138
|
+
if labels.empty?
|
139
|
+
match_general(line, lineno, file)
|
140
|
+
else
|
141
|
+
match_special(line, lineno, file)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Match special notes.
|
146
|
+
def match_special(line, lineno, file)
|
147
|
+
rec = nil
|
148
|
+
labels.each do |label|
|
149
|
+
if md = match_special_regex(label, file).match(line)
|
150
|
+
text = md[1]
|
151
|
+
#rec = {'label'=>label,'file'=>file,'line'=>lineno,'note'=>text}
|
152
|
+
rec = Note.new(self, file, label, lineno, text, remark(file))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
rec
|
156
|
+
end
|
157
|
+
|
158
|
+
#--
|
159
|
+
# TODO: ruby-1.9.1-p378 reports: `match': invalid byte sequence in UTF-8
|
160
|
+
#++
|
161
|
+
def match_special_regex(label, file)
|
162
|
+
mark = remark(file)
|
163
|
+
if colon
|
164
|
+
/#{mark}\s*#{Regexp.escape(label)}[:]\s+(.*?)$/
|
165
|
+
else
|
166
|
+
/#{mark}\s*#{Regexp.escape(label)}[:]?\s+(.*?)$/
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Match notes that are labeled with a colon.
|
171
|
+
def match_general(line, lineno, file)
|
172
|
+
rec = nil
|
173
|
+
if md = match_general_regex(file).match(line)
|
174
|
+
label, text = md[1], md[2]
|
175
|
+
#rec = {'label'=>label,'file'=>file,'line'=>lineno,'note'=>text}
|
176
|
+
rec = Note.new(self, file, label, lineno, text, remark(file))
|
177
|
+
end
|
178
|
+
return rec
|
179
|
+
end
|
180
|
+
|
181
|
+
# Keep in mind that general non-colon matches have a higher potential
|
182
|
+
# of false positives.
|
183
|
+
def match_general_regex(file)
|
184
|
+
mark = remark(file)
|
185
|
+
if colon
|
186
|
+
/#{mark}\s*([A-Z]+)[:]\s+(.*?)$/
|
187
|
+
else
|
188
|
+
/#{mark}\s*([A-Z]+)\s+(.*?)$/
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Organize notes into a hash with labels for keys.
|
193
|
+
def by_label
|
194
|
+
@by_label ||= (
|
195
|
+
list = {}
|
196
|
+
notes.each do |note|
|
197
|
+
list[note.label] ||= []
|
198
|
+
list[note.label] << note
|
199
|
+
list[note.label].sort #!{ |a,b| a.line <=> b.line }
|
200
|
+
end
|
201
|
+
list
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Organize notes into a hash with filename for keys.
|
206
|
+
def by_file
|
207
|
+
@by_file ||= (
|
208
|
+
list = {}
|
209
|
+
notes.each do |note|
|
210
|
+
list[note.file] ||= []
|
211
|
+
list[note.file] << note
|
212
|
+
list[note.file].sort! #!{ |a,b| a.line <=> b.line }
|
213
|
+
end
|
214
|
+
list
|
215
|
+
)
|
216
|
+
end
|
217
|
+
|
218
|
+
# Organize notes into a hash with labels for keys, followed
|
219
|
+
# by a hash with filename for keys.
|
220
|
+
def by_label_file
|
221
|
+
@by_label ||= (
|
222
|
+
list = {}
|
223
|
+
notes.each do |note|
|
224
|
+
list[note.label] ||= {}
|
225
|
+
list[note.label][note.file] ||= []
|
226
|
+
list[note.label][note.file] << note
|
227
|
+
list[note.label][note.file].sort! #{ |a,b| a.line <=> b.line }
|
228
|
+
end
|
229
|
+
list
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Organize notes into a hash with filenames for keys, followed
|
234
|
+
# by a hash with labels for keys.
|
235
|
+
def by_file_label
|
236
|
+
@by_file ||= (
|
237
|
+
list = {}
|
238
|
+
notes.each do |note|
|
239
|
+
list[note.file] ||= {}
|
240
|
+
list[note.file][note.label] ||= []
|
241
|
+
list[note.file][note.label] << note
|
242
|
+
list[note.file][note.label].sort! #{ |a,b| a.line <=> b.line }
|
243
|
+
end
|
244
|
+
list
|
245
|
+
)
|
246
|
+
end
|
247
|
+
|
248
|
+
# Convert to an array of hashes.
|
249
|
+
def to_a
|
250
|
+
notes.map{ |n| n.to_h }
|
251
|
+
end
|
252
|
+
|
253
|
+
# Same as #by_label.
|
254
|
+
def to_h
|
255
|
+
by_label
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
def remark(file)
|
260
|
+
@remark[File.extname(file)] ||= (
|
261
|
+
mark = guess_marker(file)
|
262
|
+
Regexp.escape(mark)
|
263
|
+
)
|
264
|
+
end
|
265
|
+
|
266
|
+
# Guess marker based on file extension. Fallsback to '#'
|
267
|
+
# if the extension is unknown.
|
268
|
+
#
|
269
|
+
# TODO: Continue to add comment types.
|
270
|
+
def guess_marker(file)
|
271
|
+
return @marker if @marker # forced marker
|
272
|
+
case File.extname(file)
|
273
|
+
when '.js', '.c', 'cpp', '.css'
|
274
|
+
'//'
|
275
|
+
when '.bas'
|
276
|
+
"'"
|
277
|
+
when '.sql', '.ada'
|
278
|
+
'--'
|
279
|
+
when '.asm'
|
280
|
+
';'
|
281
|
+
else
|
282
|
+
'#'
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# Convert to array of hashes then to YAML.
|
287
|
+
#def to_yaml
|
288
|
+
# require 'yaml'
|
289
|
+
# to_a.to_yaml
|
290
|
+
#end
|
291
|
+
|
292
|
+
# Convert to array of hashes then to JSON.
|
293
|
+
#def to_json
|
294
|
+
# begin
|
295
|
+
# require 'json'
|
296
|
+
# rescue LoadError
|
297
|
+
# require 'json_pure'
|
298
|
+
# end
|
299
|
+
# to_a.to_json
|
300
|
+
#end
|
301
|
+
|
302
|
+
# Convert to array of hashes then to a SOAP XML envelope.
|
303
|
+
#def to_soap
|
304
|
+
# require 'soap/marshal'
|
305
|
+
# SOAP::Marshal.marshal(to_a)
|
306
|
+
#end
|
307
|
+
|
308
|
+
# XOXO microformat.
|
309
|
+
#--
|
310
|
+
# TODO: Would to_xoxo be better organized by label and or file?
|
311
|
+
#++
|
312
|
+
#def to_xoxo
|
313
|
+
# require 'xoxo'
|
314
|
+
# to_a.to_xoxo
|
315
|
+
#end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|