mvz-dnote 1.7.2 → 1.10.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.
- 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
|