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.
data/lib/dnote/notes.rb CHANGED
@@ -1,8 +1,10 @@
1
- require 'pathname'
2
- require 'dnote/note'
1
+ # frozen_string_literal: true
3
2
 
4
- module DNote
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 = ["**/*.rb"]
23
+ DEFAULT_PATHS = ["**/*.rb"].freeze
24
24
 
25
25
  # Default note labels to look for in source code. (NOT CURRENTLY USED!)
26
- DEFAULT_LABELS = ['TODO', 'FIXME', 'OPTIMIZE', 'THINK', 'DEPRECATE']
26
+ DEFAULT_LABELS = %w[TODO FIXME OPTIMIZE THINK DEPRECATE].freeze
27
27
 
28
28
  # Files to search for notes.
29
- attr_accessor :files
29
+ attr_reader :files
30
30
 
31
31
  # Labels to document. Defaults are: +TODO+, +FIXME+, +OPTIMIZE+ and +DEPRECATE+.
32
- attr_accessor :labels
32
+ attr_reader :labels
33
33
 
34
34
  # Require label colon? Default is +true+.
35
- attr_accessor :colon
35
+ attr_reader :colon
36
36
 
37
37
  # Specific remark marker (+nil+ for auto).
38
- attr_accessor :marker
38
+ attr_reader :marker
39
39
 
40
40
  # Link template.
41
- attr_accessor :url
41
+ attr_reader :url
42
42
 
43
43
  # Number of lines of context to show.
44
- attr_accessor :context
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 = [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]
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
- # 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?
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
- 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
70
+ records += parse_file(fname)
131
71
  end
132
72
 
133
- @notes = records.sort
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*#{Regexp.escape(label)}[:]\s+(.*?)$/
136
+ /#{mark}\s*#{label}:\s+(.*?)$/
165
137
  else
166
- /#{mark}\s*#{Regexp.escape(label)}[:]?\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, text = md[1], md[2]
175
- #rec = {'label'=>label,'file'=>file,'line'=>lineno,'note'=>text}
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
- return rec
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]+)[:]\s+(.*?)$/
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
- mark = guess_marker(file)
262
- Regexp.escape(mark)
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 '.js', '.c', 'cpp', '.css'
274
- '//'
275
- when '.bas'
180
+ when ".js", ".c", "cpp", ".css"
181
+ "//"
182
+ when ".bas"
276
183
  "'"
277
- when '.sql', '.ada'
278
- '--'
279
- when '.asm'
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