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.
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