mvz-dnote 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
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
+