mvz-dnote 1.7.2 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/HISTORY.rdoc +18 -0
- data/README.md +116 -0
- data/bin/dnote +10 -1
- data/lib/dnote/core_ext.rb +11 -67
- data/lib/dnote/format.rb +59 -83
- data/lib/dnote/note.rb +25 -49
- data/lib/dnote/notes.rb +83 -210
- data/lib/dnote/notes_collection.rb +73 -0
- data/lib/dnote/options.rb +125 -0
- data/lib/dnote/rake/dnotetask.rb +55 -57
- data/lib/dnote/session.rb +49 -152
- data/lib/dnote/version.rb +3 -15
- data/lib/dnote.rb +4 -2
- metadata +128 -26
- data/README.rdoc +0 -113
- data/lib/dnote.yml +0 -1
- data/try/sample.bas +0 -7
- data/try/sample.js +0 -11
- data/try/sample.rb +0 -11
data/lib/dnote/notes.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
2
|
-
require 'dnote/note'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
3
|
+
require "pathname"
|
4
|
+
require "dnote/note"
|
5
|
+
require "dnote/notes_collection"
|
5
6
|
|
7
|
+
module DNote
|
6
8
|
# = Developer Notes
|
7
9
|
#
|
8
10
|
# This class goes through you source files and compiles a list
|
@@ -17,70 +19,45 @@ module DNote
|
|
17
19
|
# have a outline format, rather then the single line.
|
18
20
|
#++
|
19
21
|
class Notes
|
20
|
-
include Enumerable
|
21
|
-
|
22
22
|
# Default paths (all ruby scripts).
|
23
|
-
DEFAULT_PATHS
|
23
|
+
DEFAULT_PATHS = ["**/*.rb"].freeze
|
24
24
|
|
25
25
|
# Default note labels to look for in source code. (NOT CURRENTLY USED!)
|
26
|
-
DEFAULT_LABELS = [
|
26
|
+
DEFAULT_LABELS = %w[TODO FIXME OPTIMIZE THINK DEPRECATE].freeze
|
27
27
|
|
28
28
|
# Files to search for notes.
|
29
|
-
|
29
|
+
attr_reader :files
|
30
30
|
|
31
31
|
# Labels to document. Defaults are: +TODO+, +FIXME+, +OPTIMIZE+ and +DEPRECATE+.
|
32
|
-
|
32
|
+
attr_reader :labels
|
33
33
|
|
34
34
|
# Require label colon? Default is +true+.
|
35
|
-
|
35
|
+
attr_reader :colon
|
36
36
|
|
37
37
|
# Specific remark marker (+nil+ for auto).
|
38
|
-
|
38
|
+
attr_reader :marker
|
39
39
|
|
40
40
|
# Link template.
|
41
|
-
|
41
|
+
attr_reader :url
|
42
42
|
|
43
43
|
# Number of lines of context to show.
|
44
|
-
|
44
|
+
attr_reader :context
|
45
45
|
|
46
46
|
# New set of notes for give +files+ and optional special labels.
|
47
|
-
def initialize(files, options={})
|
48
|
-
@files
|
49
|
-
@labels
|
50
|
-
@colon
|
51
|
-
@marker
|
52
|
-
@url
|
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
53
|
@context = options[:context] || 0
|
54
|
-
|
55
|
-
@remark = {}
|
54
|
+
@remark = {}
|
56
55
|
|
57
56
|
parse
|
58
57
|
end
|
59
58
|
|
60
|
-
|
61
|
-
|
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?
|
59
|
+
def notes_collection
|
60
|
+
@notes_collection ||= NotesCollection.new(@notes)
|
84
61
|
end
|
85
62
|
|
86
63
|
# Gather notes.
|
@@ -90,47 +67,45 @@ module DNote
|
|
90
67
|
def parse
|
91
68
|
records = []
|
92
69
|
files.each do |fname|
|
93
|
-
|
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
|
70
|
+
records += parse_file(fname)
|
131
71
|
end
|
132
72
|
|
133
|
-
@notes
|
73
|
+
@notes = records.sort
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_file(fname)
|
77
|
+
return [] unless File.file?(fname)
|
78
|
+
|
79
|
+
records = []
|
80
|
+
mark = remark(fname)
|
81
|
+
lineno = 0
|
82
|
+
note = nil
|
83
|
+
text = nil
|
84
|
+
capt = nil
|
85
|
+
File.readlines(fname).each do |line|
|
86
|
+
lineno += 1
|
87
|
+
note = match(line, lineno, fname)
|
88
|
+
if note
|
89
|
+
text = note.text
|
90
|
+
capt = note.capture
|
91
|
+
records << note
|
92
|
+
elsif text
|
93
|
+
case line
|
94
|
+
when /^\s*#{mark}+\s*$/, /^\s*#{mark}--/, /^\s*#{mark}\+\+/
|
95
|
+
text.strip!
|
96
|
+
text = nil
|
97
|
+
when /^\s*#{mark}/
|
98
|
+
text << "\n" unless text[-1, 1] == "\n"
|
99
|
+
text << line.gsub(/^\s*#{mark}\s*/, "")
|
100
|
+
else
|
101
|
+
text.strip!
|
102
|
+
text = nil
|
103
|
+
end
|
104
|
+
elsif !/^\s*#{mark}/.match?(line)
|
105
|
+
capt << line if capt && capt.size < context
|
106
|
+
end
|
107
|
+
end
|
108
|
+
records
|
134
109
|
end
|
135
110
|
|
136
111
|
# Is this line a note?
|
@@ -146,36 +121,33 @@ module DNote
|
|
146
121
|
def match_special(line, lineno, file)
|
147
122
|
rec = nil
|
148
123
|
labels.each do |label|
|
149
|
-
if md = match_special_regex(label, file).match(line)
|
124
|
+
if (md = match_special_regex(label, file).match(line))
|
150
125
|
text = md[1]
|
151
|
-
#rec = {'label'=>label,'file'=>file,'line'=>lineno,'note'=>text}
|
152
126
|
rec = Note.new(self, file, label, lineno, text, remark(file))
|
153
127
|
end
|
154
128
|
end
|
155
129
|
rec
|
156
130
|
end
|
157
131
|
|
158
|
-
#--
|
159
|
-
# TODO: ruby-1.9.1-p378 reports: `match': invalid byte sequence in UTF-8
|
160
|
-
#++
|
161
132
|
def match_special_regex(label, file)
|
162
133
|
mark = remark(file)
|
134
|
+
label = Regexp.escape(label)
|
163
135
|
if colon
|
164
|
-
/#{mark}\s*#{
|
136
|
+
/#{mark}\s*#{label}:\s+(.*?)$/
|
165
137
|
else
|
166
|
-
/#{mark}\s*#{
|
138
|
+
/#{mark}\s*#{label}:?\s+(.*?)$/
|
167
139
|
end
|
168
140
|
end
|
169
141
|
|
170
142
|
# Match notes that are labeled with a colon.
|
171
143
|
def match_general(line, lineno, file)
|
172
144
|
rec = nil
|
173
|
-
if md = match_general_regex(file).match(line)
|
174
|
-
label
|
175
|
-
|
145
|
+
if (md = match_general_regex(file).match(line))
|
146
|
+
label = md[1]
|
147
|
+
text = md[2]
|
176
148
|
rec = Note.new(self, file, label, lineno, text, remark(file))
|
177
149
|
end
|
178
|
-
|
150
|
+
rec
|
179
151
|
end
|
180
152
|
|
181
153
|
# Keep in mind that general non-colon matches have a higher potential
|
@@ -183,84 +155,18 @@ module DNote
|
|
183
155
|
def match_general_regex(file)
|
184
156
|
mark = remark(file)
|
185
157
|
if colon
|
186
|
-
/#{mark}\s*([A-Z]+)
|
187
|
-
else
|
158
|
+
/#{mark}\s*([A-Z]+):\s+(.*?)$/
|
159
|
+
else
|
188
160
|
/#{mark}\s*([A-Z]+)\s+(.*?)$/
|
189
161
|
end
|
190
162
|
end
|
191
163
|
|
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
164
|
def remark(file)
|
260
|
-
@remark[File.extname(file)] ||=
|
261
|
-
|
262
|
-
|
263
|
-
|
165
|
+
@remark[File.extname(file)] ||=
|
166
|
+
begin
|
167
|
+
mark = guess_marker(file)
|
168
|
+
Regexp.escape(mark)
|
169
|
+
end
|
264
170
|
end
|
265
171
|
|
266
172
|
# Guess marker based on file extension. Fallsback to '#'
|
@@ -269,52 +175,19 @@ module DNote
|
|
269
175
|
# TODO: Continue to add comment types.
|
270
176
|
def guess_marker(file)
|
271
177
|
return @marker if @marker # forced marker
|
178
|
+
|
272
179
|
case File.extname(file)
|
273
|
-
when
|
274
|
-
|
275
|
-
when
|
180
|
+
when ".js", ".c", "cpp", ".css"
|
181
|
+
"//"
|
182
|
+
when ".bas"
|
276
183
|
"'"
|
277
|
-
when
|
278
|
-
|
279
|
-
when
|
280
|
-
|
184
|
+
when ".sql", ".ada"
|
185
|
+
"--"
|
186
|
+
when ".asm"
|
187
|
+
";"
|
281
188
|
else
|
282
|
-
|
189
|
+
"#"
|
283
190
|
end
|
284
191
|
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
192
|
end
|
318
|
-
|
319
193
|
end
|
320
|
-
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DNote
|
4
|
+
# = Notes Collection
|
5
|
+
#
|
6
|
+
# This class contains a collection of Note objects and can group them in
|
7
|
+
# several ways.
|
8
|
+
#
|
9
|
+
class NotesCollection
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
def initialize(notes)
|
13
|
+
@notes = notes
|
14
|
+
end
|
15
|
+
|
16
|
+
# Array of notes.
|
17
|
+
attr_reader :notes
|
18
|
+
|
19
|
+
# Notes counts by label.
|
20
|
+
def counts
|
21
|
+
@counts ||=
|
22
|
+
begin
|
23
|
+
h = {}
|
24
|
+
by_label.each do |label, notes|
|
25
|
+
h[label] = notes.size
|
26
|
+
end
|
27
|
+
h
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Iterate through notes.
|
32
|
+
def each(&block)
|
33
|
+
notes.each(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# No notes?
|
37
|
+
def empty?
|
38
|
+
notes.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Organize notes into a hash with labels for keys.
|
42
|
+
def by_label
|
43
|
+
@by_label ||= notes.group_by(&:label)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Organize notes into a hash with filename for keys.
|
47
|
+
def by_file
|
48
|
+
@by_file ||= notes.group_by(&:file)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Organize notes into a hash with labels for keys, followed
|
52
|
+
# by a hash with filename for keys.
|
53
|
+
def by_label_file
|
54
|
+
@by_label_file ||= by_label.transform_values { |notes| notes.group_by(&:file) }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Organize notes into a hash with filenames for keys, followed
|
58
|
+
# by a hash with labels for keys.
|
59
|
+
def by_file_label
|
60
|
+
@by_file_label ||= by_file.transform_values { |notes| notes.group_by(&:label) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Convert to an array of hashes.
|
64
|
+
def to_a
|
65
|
+
notes.map(&:to_h)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Same as #by_label.
|
69
|
+
def to_h
|
70
|
+
by_label
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dnote/core_ext"
|
4
|
+
require "dnote/notes"
|
5
|
+
require "dnote/format"
|
6
|
+
require "optparse"
|
7
|
+
|
8
|
+
module DNote
|
9
|
+
# Option parser
|
10
|
+
class Options
|
11
|
+
def self.parse(*argv)
|
12
|
+
new(argv).parse
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(argv)
|
16
|
+
@argv = argv
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse
|
20
|
+
session = Session.new
|
21
|
+
|
22
|
+
opts = OptionParser.new do |opt|
|
23
|
+
opt.banner = "DNote v#{DNote::VERSION}"
|
24
|
+
|
25
|
+
opt.separator(" ")
|
26
|
+
opt.separator("USAGE:\n dnote [OPTIONS] path1 [path2 ...]")
|
27
|
+
|
28
|
+
add_output_format_options(session, opt)
|
29
|
+
add_other_options(session, opt)
|
30
|
+
add_command_options(session, opt)
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.parse!(@argv)
|
34
|
+
session.paths.replace(@argv)
|
35
|
+
session
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_command_options(session, opt)
|
39
|
+
opt.separator(" ")
|
40
|
+
opt.separator("COMMAND OPTIONS:")
|
41
|
+
|
42
|
+
opt.on_tail("--templates", "-T", "list available format templates") do
|
43
|
+
session.list_templates
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
opt.on_tail("--help", "-h", "show this help information") do
|
48
|
+
puts opt
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_output_format_options(session, opt)
|
54
|
+
opt.separator(" ")
|
55
|
+
opt.separator("OUTPUT FORMAT: (choose one)")
|
56
|
+
|
57
|
+
opt.on("--format", "-f NAME", "select a format [text]") do |format|
|
58
|
+
session.format = format
|
59
|
+
end
|
60
|
+
|
61
|
+
opt.on("--custom", "-C FILE", "use a custom ERB template") do |file|
|
62
|
+
session.format = "custom"
|
63
|
+
session.template = file
|
64
|
+
end
|
65
|
+
|
66
|
+
opt.on("--file", "shortcut for text/file format") do
|
67
|
+
session.format = "text/file"
|
68
|
+
end
|
69
|
+
|
70
|
+
opt.on("--list", "shortcut for text/list format") do
|
71
|
+
session.format = "text/list"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_other_options(session, opt)
|
76
|
+
opt.separator(" ")
|
77
|
+
opt.separator("OTHER OPTIONS:")
|
78
|
+
|
79
|
+
opt.on("--label", "-l LABEL", "labels to collect") do |lbl|
|
80
|
+
session.labels.concat(lbl.split(":"))
|
81
|
+
end
|
82
|
+
|
83
|
+
opt.on(:colon, "--[no-]colon", "match labels with/without colon suffix") do |val|
|
84
|
+
session.colon = val
|
85
|
+
end
|
86
|
+
|
87
|
+
opt.on("--marker", "-m MARK", "alternative remark marker") do |mark|
|
88
|
+
session.marker = mark
|
89
|
+
end
|
90
|
+
|
91
|
+
opt.on("--url", "-u TEMPLATE", "url template for line entries (for HTML)") do |url|
|
92
|
+
session.url = url
|
93
|
+
end
|
94
|
+
|
95
|
+
opt.on("--context", "-c INTEGER", "number of lines of context to display") do |int|
|
96
|
+
session.context = int.to_i
|
97
|
+
end
|
98
|
+
|
99
|
+
opt.on("--exclude", "-x PATH", "exclude file or directory") do |path|
|
100
|
+
session.exclude << path
|
101
|
+
end
|
102
|
+
|
103
|
+
opt.on("--ignore", "-i NAME", "ignore file based on any part of pathname") do |name|
|
104
|
+
session.ignore << name
|
105
|
+
end
|
106
|
+
|
107
|
+
opt.on("--title", "-t TITLE", "title to use in header") do |title|
|
108
|
+
session.title = title
|
109
|
+
end
|
110
|
+
|
111
|
+
opt.on("--output", "-o PATH", "save to file or directory") do |path|
|
112
|
+
session.output = path
|
113
|
+
end
|
114
|
+
|
115
|
+
opt.on("--dryrun", "-n", "do not actually write to disk") do
|
116
|
+
session.dryrun = true
|
117
|
+
end
|
118
|
+
|
119
|
+
opt.on("--debug", "debug mode") do
|
120
|
+
$DEBUG = true
|
121
|
+
$VERBOSE = true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|