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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING.rdoc +31 -0
  3. data/HISTORY.rdoc +171 -0
  4. data/README.rdoc +113 -0
  5. data/bin/dnote +4 -0
  6. data/lib/dnote.rb +4 -0
  7. data/lib/dnote.yml +1 -0
  8. data/lib/dnote/core_ext.rb +92 -0
  9. data/lib/dnote/format.rb +173 -0
  10. data/lib/dnote/note.rb +150 -0
  11. data/lib/dnote/notes.rb +320 -0
  12. data/lib/dnote/rake/dnotetask.rb +116 -0
  13. data/lib/dnote/session.rb +269 -0
  14. data/lib/dnote/templates/html.erb +49 -0
  15. data/lib/dnote/templates/html/file.erb +48 -0
  16. data/lib/dnote/templates/html/label.erb +49 -0
  17. data/lib/dnote/templates/html/list.erb +42 -0
  18. data/lib/dnote/templates/json.erb +8 -0
  19. data/lib/dnote/templates/json/file.erb +8 -0
  20. data/lib/dnote/templates/json/label.erb +8 -0
  21. data/lib/dnote/templates/json/list.erb +8 -0
  22. data/lib/dnote/templates/md.erb +19 -0
  23. data/lib/dnote/templates/md/file.erb +14 -0
  24. data/lib/dnote/templates/md/label.erb +19 -0
  25. data/lib/dnote/templates/md/list.erb +10 -0
  26. data/lib/dnote/templates/rdoc.erb +12 -0
  27. data/lib/dnote/templates/rdoc/file.erb +10 -0
  28. data/lib/dnote/templates/rdoc/label.erb +12 -0
  29. data/lib/dnote/templates/rdoc/list.erb +7 -0
  30. data/lib/dnote/templates/soap.erb +4 -0
  31. data/lib/dnote/templates/soap/file.erb +4 -0
  32. data/lib/dnote/templates/soap/label.erb +4 -0
  33. data/lib/dnote/templates/soap/list.erb +4 -0
  34. data/lib/dnote/templates/text.erb +12 -0
  35. data/lib/dnote/templates/text/file.erb +11 -0
  36. data/lib/dnote/templates/text/label.erb +11 -0
  37. data/lib/dnote/templates/text/list.erb +8 -0
  38. data/lib/dnote/templates/xml.erb +23 -0
  39. data/lib/dnote/templates/xml/file.erb +23 -0
  40. data/lib/dnote/templates/xml/label.erb +23 -0
  41. data/lib/dnote/templates/xml/list.erb +14 -0
  42. data/lib/dnote/templates/xoxo.erb +4 -0
  43. data/lib/dnote/templates/xoxo/file.erb +4 -0
  44. data/lib/dnote/templates/xoxo/label.erb +4 -0
  45. data/lib/dnote/templates/xoxo/list.erb +6 -0
  46. data/lib/dnote/templates/yaml.erb +1 -0
  47. data/lib/dnote/templates/yaml/file.erb +1 -0
  48. data/lib/dnote/templates/yaml/label.erb +1 -0
  49. data/lib/dnote/templates/yaml/list.erb +1 -0
  50. data/lib/dnote/version.rb +17 -0
  51. data/test/notes_case.rb +59 -0
  52. data/try/sample.bas +7 -0
  53. data/try/sample.js +11 -0
  54. data/try/sample.rb +11 -0
  55. metadata +131 -0
@@ -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
@@ -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
+