sdoc_local_editor 0.3.17

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 (59) hide show
  1. data/.gitignore +3 -0
  2. data/.rake_tasks~ +20 -0
  3. data/LICENSE +110 -0
  4. data/README.rdoc +37 -0
  5. data/Rakefile +12 -0
  6. data/bin/sdoc_local_editor +27 -0
  7. data/bin/sdoc_local_editor_merge +24 -0
  8. data/lib/rdoc/discover.rb +5 -0
  9. data/lib/rdoc/generator/template/merge/index.rhtml +14 -0
  10. data/lib/rdoc/generator/template/rails/_context.rhtml +210 -0
  11. data/lib/rdoc/generator/template/rails/_head.rhtml +7 -0
  12. data/lib/rdoc/generator/template/rails/class.rhtml +39 -0
  13. data/lib/rdoc/generator/template/rails/file.rhtml +37 -0
  14. data/lib/rdoc/generator/template/rails/index.rhtml +13 -0
  15. data/lib/rdoc/generator/template/rails/resources/apple-touch-icon.png +0 -0
  16. data/lib/rdoc/generator/template/rails/resources/css/github.css +129 -0
  17. data/lib/rdoc/generator/template/rails/resources/css/main.css +346 -0
  18. data/lib/rdoc/generator/template/rails/resources/css/panel.css +389 -0
  19. data/lib/rdoc/generator/template/rails/resources/css/reset.css +48 -0
  20. data/lib/rdoc/generator/template/rails/resources/favicon.ico +0 -0
  21. data/lib/rdoc/generator/template/rails/resources/i/arrows.png +0 -0
  22. data/lib/rdoc/generator/template/rails/resources/i/results_bg.png +0 -0
  23. data/lib/rdoc/generator/template/rails/resources/i/tree_bg.png +0 -0
  24. data/lib/rdoc/generator/template/rails/resources/js/highlight.pack.js +1 -0
  25. data/lib/rdoc/generator/template/rails/resources/js/jquery-1.3.2.min.js +19 -0
  26. data/lib/rdoc/generator/template/rails/resources/js/jquery-effect.js +593 -0
  27. data/lib/rdoc/generator/template/rails/resources/js/main.js +20 -0
  28. data/lib/rdoc/generator/template/rails/resources/js/searchdoc.js +441 -0
  29. data/lib/rdoc/generator/template/rails/resources/panel/index.html +73 -0
  30. data/lib/rdoc/generator/template/rails/se_index.rhtml +8 -0
  31. data/lib/rdoc/generator/template/sdoc/_context.rhtml +221 -0
  32. data/lib/rdoc/generator/template/sdoc/_head.rhtml +7 -0
  33. data/lib/rdoc/generator/template/sdoc/class.rhtml +39 -0
  34. data/lib/rdoc/generator/template/sdoc/file.rhtml +29 -0
  35. data/lib/rdoc/generator/template/sdoc/index.rhtml +13 -0
  36. data/lib/rdoc/generator/template/sdoc/resources/apple-touch-icon.png +0 -0
  37. data/lib/rdoc/generator/template/sdoc/resources/css/github.css +129 -0
  38. data/lib/rdoc/generator/template/sdoc/resources/css/main.css +333 -0
  39. data/lib/rdoc/generator/template/sdoc/resources/css/panel.css +384 -0
  40. data/lib/rdoc/generator/template/sdoc/resources/css/reset.css +48 -0
  41. data/lib/rdoc/generator/template/sdoc/resources/favicon.ico +0 -0
  42. data/lib/rdoc/generator/template/sdoc/resources/i/arrows.png +0 -0
  43. data/lib/rdoc/generator/template/sdoc/resources/i/results_bg.png +0 -0
  44. data/lib/rdoc/generator/template/sdoc/resources/i/tree_bg.png +0 -0
  45. data/lib/rdoc/generator/template/sdoc/resources/js/highlight.pack.js +1 -0
  46. data/lib/rdoc/generator/template/sdoc/resources/js/jquery-1.3.2.min.js +19 -0
  47. data/lib/rdoc/generator/template/sdoc/resources/js/jquery-effect.js +593 -0
  48. data/lib/rdoc/generator/template/sdoc/resources/js/main.js +24 -0
  49. data/lib/rdoc/generator/template/sdoc/resources/js/searchdoc.js +442 -0
  50. data/lib/rdoc/generator/template/sdoc/resources/panel/index.html +73 -0
  51. data/lib/rdoc/generator/template/sdoc/se_index.rhtml +8 -0
  52. data/lib/sdoc_local_editor.rb +9 -0
  53. data/lib/sdoc_local_editor/generator.rb +418 -0
  54. data/lib/sdoc_local_editor/github.rb +61 -0
  55. data/lib/sdoc_local_editor/helpers.rb +26 -0
  56. data/lib/sdoc_local_editor/merge.rb +223 -0
  57. data/lib/sdoc_local_editor/templatable.rb +60 -0
  58. data/sdoc_local_editor.gemspec +31 -0
  59. metadata +139 -0
@@ -0,0 +1,73 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
3
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
6
+ <head>
7
+ <title>search index</title>
8
+ <link rel="stylesheet" href="../css/reset.css" type="text/css" media="screen" charset="utf-8" />
9
+ <link rel="stylesheet" href="../css/panel.css" type="text/css" media="screen" charset="utf-8" />
10
+ <script src="../js/search_index.js" type="text/javascript" charset="utf-8"></script>
11
+ <script src="../js/searcher.js" type="text/javascript" charset="utf-8"></script>
12
+ <script src="tree.js" type="text/javascript" charset="utf-8"></script>
13
+ <script src="../js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
14
+ <script src="../js/searchdoc.js" type="text/javascript" charset="utf-8"></script>
15
+ <script type="text/javascript" charset="utf-8">
16
+ function placeholder() {
17
+ if ($('<input type="text">')[0].placeholder !== undefined) return;
18
+
19
+ $('#search-label').click(function() {
20
+ $('#search').focus();
21
+ $('#search-label').hide();
22
+ });
23
+
24
+ $('#search').focus(function() {
25
+ $('#search-label').hide();
26
+ });
27
+ $('#search').blur(function() {
28
+ this.value == '' && $('#search-label').show()
29
+ });
30
+
31
+ $('#search')[0].value == '' && $('#search-label').show();
32
+ }
33
+ $(function() {
34
+ placeholder();
35
+ $('#links').hide();
36
+ var panel = new Searchdoc.Panel($('#panel'), search_data, tree, top.frames[1]);
37
+ $('#search').focus();
38
+
39
+ var s = window.parent.location.search.match(/\?q=([^&]+)/);
40
+ if (s) {
41
+ s = decodeURIComponent(s[1]).replace(/\+/g, ' ');
42
+ if (s.length > 0)
43
+ {
44
+ $('#search').val(s);
45
+ panel.search(s, true);
46
+ }
47
+ }
48
+ })
49
+ </script>
50
+ </head>
51
+ <body>
52
+ <div class="panel panel_tree" id="panel">
53
+ <div class="header">
54
+ <div>
55
+ <label for="search" id="search-label" style="display: none">Search</label>
56
+ <table>
57
+ <tr><td>
58
+ <input type="Search" placeholder="Search" autosave="searchdoc" results="10" id="search" autocomplete="off"/>
59
+ </td></tr>
60
+ </table></div>
61
+ </div>
62
+ <div class="tree">
63
+ <ul>
64
+ </ul>
65
+ </div>
66
+ <div class="result">
67
+ <ul>
68
+ </ul>
69
+ </div>
70
+ </div>
71
+ <a href="links.html" id="links">index</a>
72
+ </body>
73
+ </html>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>File index</head>
3
+ <body>
4
+ <% @files.each do |file| %>
5
+ <a href="../<%= file.path %>"><%= file.relative_name %></a>
6
+ <% end %>
7
+ </body>
8
+ </html>
@@ -0,0 +1,9 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ SOURCE_DIR = Dir.pwd
3
+
4
+ require "rubygems"
5
+ gem 'rdoc', '~> 3'
6
+
7
+ module SDoc end
8
+
9
+ require 'sdoc_local_editor/generator'
@@ -0,0 +1,418 @@
1
+ require 'rubygems'
2
+ require 'erb'
3
+ require 'pathname'
4
+ require 'fileutils'
5
+ if Gem::Specification.respond_to?(:find_by_name) ? Gem::Specification::find_by_name("json") : Gem.available?("json")
6
+ gem "json", ">= 1.1.3"
7
+ else
8
+ gem "json_pure", ">= 1.1.3"
9
+ end
10
+ require 'json'
11
+
12
+ require 'sdoc_local_editor/github'
13
+ require 'sdoc_local_editor/templatable'
14
+ require 'sdoc_local_editor/helpers'
15
+ require 'rdoc'
16
+
17
+ class RDoc::ClassModule
18
+ def with_documentation?
19
+ document_self_or_methods || classes_and_modules.any?{ |c| c.with_documentation? }
20
+ end
21
+ end
22
+
23
+ class RDoc::Options
24
+ attr_accessor :github, :se_index, :local_editor
25
+ end
26
+
27
+ class RDoc::AnyMethod
28
+
29
+ TITLE_AFTER = %w(def class module)
30
+
31
+ ##
32
+ # Turns the method's token stream into HTML.
33
+ #
34
+ # Prepends line numbers if +add_line_numbers+ is true.
35
+
36
+ def sdoc_markup_code
37
+ return '' unless @token_stream
38
+
39
+ src = ""
40
+ starting_title = false
41
+
42
+ @token_stream.each do |t|
43
+ next unless t
44
+
45
+ style = case t
46
+ when RDoc::RubyToken::TkFLOAT then 'ruby-number'
47
+ when RDoc::RubyToken::TkINTEGER then 'ruby-number'
48
+ when RDoc::RubyToken::TkCONSTANT then 'ruby-constant'
49
+ when RDoc::RubyToken::TkKW then 'ruby-keyword'
50
+ when RDoc::RubyToken::TkIVAR then 'ruby-ivar'
51
+ when RDoc::RubyToken::TkOp then 'ruby-operator'
52
+ when RDoc::RubyToken::TkId then 'ruby-identifier'
53
+ when RDoc::RubyToken::TkNode then 'ruby-node'
54
+ when RDoc::RubyToken::TkCOMMENT then 'ruby-comment'
55
+ when RDoc::RubyToken::TkREGEXP then 'ruby-regexp'
56
+ when RDoc::RubyToken::TkSTRING then 'ruby-string'
57
+ when RDoc::RubyToken::TkVal then 'ruby-value'
58
+ end
59
+
60
+ if RDoc::RubyToken::TkId === t && starting_title
61
+ starting_title = false
62
+ style = 'ruby-keyword ruby-title'
63
+ end
64
+
65
+ if RDoc::RubyToken::TkKW === t && TITLE_AFTER.include?(t.text)
66
+ starting_title = true
67
+ end
68
+
69
+ text = CGI.escapeHTML t.text
70
+
71
+ if style then
72
+ src << "<span class=\"#{style}\">#{text}</span>"
73
+ else
74
+ src << text
75
+ end
76
+ end
77
+
78
+ # dedent the source
79
+ indent = src.length
80
+ lines = src.lines.to_a
81
+ lines.shift if src =~ /\A.*#\ *File/i # remove '# File' comment
82
+ lines.each do |line|
83
+ if line =~ /^ *(?=\S)/
84
+ n = $&.length
85
+ indent = n if n < indent
86
+ break if n == 0
87
+ end
88
+ end
89
+ src.gsub!(/^#{' ' * indent}/, '') if indent > 0
90
+
91
+ add_line_numbers(src) if self.class.add_line_numbers
92
+
93
+ src
94
+ end
95
+
96
+ end
97
+
98
+ class RDoc::Generator::SDoc
99
+ RDoc::RDoc.add_generator self
100
+
101
+ DESCRIPTION = 'Searchable HTML documentation'
102
+
103
+ include ERB::Util
104
+ include SDoc::GitHub
105
+ include SDoc::Templatable
106
+ include SDoc::Helpers
107
+
108
+ GENERATOR_DIRS = [File.join('sdoc_local_editor', 'generator')]
109
+
110
+ TREE_FILE = File.join 'panel', 'tree.js'
111
+ SEARCH_INDEX_FILE = File.join 'js', 'search_index.js'
112
+
113
+ FILE_DIR = 'files'
114
+ CLASS_DIR = 'classes'
115
+
116
+ RESOURCES_DIR = File.join('resources', '.')
117
+
118
+ attr_reader :base_dir
119
+
120
+ attr_reader :options
121
+
122
+ def self.setup_options(options)
123
+ @github = false
124
+ options.se_index = true
125
+ options.local_editor = true
126
+
127
+ opt = options.option_parser
128
+ opt.separator nil
129
+ opt.separator "SDoc generator options:"
130
+ opt.separator nil
131
+ opt.on("--github", "-g",
132
+ "Generate links to github.") do |value|
133
+ options.github = true
134
+ end
135
+ opt.separator nil
136
+
137
+ opt.on("--no-se-index", "-ns",
138
+ "Do not generated index file for search engines.",
139
+ "SDoc uses javascript to refrence individual documentation pages.",
140
+ "Search engine crawlers are not smart enough to find all the",
141
+ "referenced pages.",
142
+ "To help them SDoc generates a static file with links to every",
143
+ "documentation page. This file is not shown to the user."
144
+ ) do |value|
145
+ options.se_index = false
146
+ end
147
+ opt.separator nil
148
+
149
+ opt.on("--local-editor", "-L",
150
+ "Generate links to open file in local editor.",
151
+ "The server serving this documentation should respond to",
152
+ "a POST request to /open_editor/. The request will contain",
153
+ "the following query parameters:",
154
+ " * file -- the file path, relative to your code directory",
155
+ " * line -- the line number in the file."
156
+ ) do |value|
157
+ options.local_editor = true
158
+ end
159
+ opt.separator nil
160
+
161
+ end
162
+
163
+ def initialize(options)
164
+ @options = options
165
+ if @options.respond_to?('diagram=')
166
+ @options.diagram = false
167
+ end
168
+ @github_url_cache = {}
169
+
170
+ @template_dir = Pathname.new(options.template_dir)
171
+ @base_dir = Pathname.pwd.expand_path
172
+
173
+ @json_index = RDoc::Generator::JsonIndex.new self, options
174
+ end
175
+
176
+ def generate(top_levels)
177
+ @outputdir = Pathname.new(@options.op_dir).expand_path(@base_dir)
178
+ @files = top_levels.sort
179
+ @classes = RDoc::TopLevel.all_classes_and_modules.sort
180
+
181
+ # Now actually write the output
182
+ copy_resources
183
+ generate_class_tree
184
+ @json_index.generate top_levels
185
+ generate_file_files
186
+ generate_class_files
187
+ generate_index_file
188
+ generate_se_index if @options.se_index
189
+ end
190
+
191
+ def class_dir
192
+ CLASS_DIR
193
+ end
194
+
195
+ def file_dir
196
+ FILE_DIR
197
+ end
198
+
199
+ protected
200
+ ### Output progress information if debugging is enabled
201
+ def debug_msg( *msg )
202
+ return unless $DEBUG_RDOC
203
+ $stderr.puts( *msg )
204
+ end
205
+
206
+ ### Create class tree structure and write it as json
207
+ def generate_class_tree
208
+ debug_msg "Generating class tree"
209
+ topclasses = @classes.select {|klass| !(RDoc::ClassModule === klass.parent) }
210
+ tree = generate_file_tree + generate_class_tree_level(topclasses)
211
+ debug_msg " writing class tree to %s" % TREE_FILE
212
+ File.open(TREE_FILE, "w", 0644) do |f|
213
+ f.write('var tree = '); f.write(tree.to_json(:max_nesting => 0))
214
+ end unless $dryrun
215
+ end
216
+
217
+ ### Recursivly build class tree structure
218
+ def generate_class_tree_level(classes, visited = {})
219
+ tree = []
220
+ classes.select do |klass|
221
+ !visited[klass] && klass.with_documentation?
222
+ end.sort.each do |klass|
223
+ visited[klass] = true
224
+ item = [
225
+ klass.name,
226
+ klass.document_self_or_methods ? klass.path : '',
227
+ klass.module? ? '' : (klass.superclass ? " < #{String === klass.superclass ? klass.superclass : klass.superclass.full_name}" : ''),
228
+ generate_class_tree_level(klass.classes_and_modules, visited)
229
+ ]
230
+ tree << item
231
+ end
232
+ tree
233
+ end
234
+
235
+ ### Add files to search +index+ array
236
+ def add_file_search_index(index)
237
+ debug_msg " generating file search index"
238
+
239
+ @files.select { |file|
240
+ file.document_self
241
+ }.sort.each do |file|
242
+ debug_msg " #{file.path}"
243
+ index[:searchIndex].push( search_string(file.name) )
244
+ index[:longSearchIndex].push( search_string(file.path) )
245
+ index[:info].push([
246
+ file.name,
247
+ file.path,
248
+ file.path,
249
+ '',
250
+ snippet(file.comment),
251
+ TYPE_FILE
252
+ ])
253
+ end
254
+ end
255
+
256
+ ### Add classes to search +index+ array
257
+ def add_class_search_index(index)
258
+ debug_msg " generating class search index"
259
+ @classes.uniq.select { |klass|
260
+ klass.document_self_or_methods
261
+ }.sort.each do |klass|
262
+ modulename = klass.module? ? '' : (klass.superclass ? (String === klass.superclass ? klass.superclass : klass.superclass.full_name) : '')
263
+ debug_msg " #{klass.parent.full_name}::#{klass.name}"
264
+ index[:searchIndex].push( search_string(klass.name) )
265
+ index[:longSearchIndex].push( search_string(klass.parent.full_name) )
266
+ files = klass.in_files.map{ |file| file.absolute_name }
267
+ index[:info].push([
268
+ klass.name,
269
+ files.include?(klass.parent.full_name) ? files.first : klass.parent.full_name,
270
+ klass.path,
271
+ modulename ? " < #{modulename}" : '',
272
+ snippet(klass.comment),
273
+ TYPE_CLASS
274
+ ])
275
+ end
276
+ end
277
+
278
+ ### Add methods to search +index+ array
279
+ def add_method_search_index(index)
280
+ debug_msg " generating method search index"
281
+
282
+ list = @classes.uniq.map do |klass|
283
+ klass.method_list
284
+ end.flatten.sort do |a, b|
285
+ a.name == b.name ?
286
+ a.parent.full_name <=> b.parent.full_name :
287
+ a.name <=> b.name
288
+ end.select do |method|
289
+ method.document_self
290
+ end.find_all do |m|
291
+ m.visibility == :public || m.visibility == :protected ||
292
+ m.force_documentation
293
+ end
294
+
295
+ list.each do |method|
296
+ debug_msg " #{method.full_name}"
297
+ index[:searchIndex].push( search_string(method.name) + '()' )
298
+ index[:longSearchIndex].push( search_string(method.parent.full_name) )
299
+ index[:info].push([
300
+ method.name,
301
+ method.parent.full_name,
302
+ method.path,
303
+ method.params,
304
+ snippet(method.comment),
305
+ TYPE_METHOD
306
+ ])
307
+ end
308
+ end
309
+
310
+ ### Generate a documentation file for each class
311
+ def generate_class_files
312
+ debug_msg "Generating class documentation in #@outputdir"
313
+ templatefile = @template_dir + 'class.rhtml'
314
+
315
+ @classes.each do |klass|
316
+ debug_msg " working on %s (%s)" % [ klass.full_name, klass.path ]
317
+ outfile = @outputdir + klass.path
318
+ rel_prefix = @outputdir.relative_path_from( outfile.dirname )
319
+
320
+ debug_msg " rendering #{outfile}"
321
+ self.render_template( templatefile, binding(), outfile )
322
+ end
323
+ end
324
+
325
+ ### Generate a documentation file for each file
326
+ def generate_file_files
327
+ debug_msg "Generating file documentation in #@outputdir"
328
+ templatefile = @template_dir + 'file.rhtml'
329
+
330
+ @files.each do |file|
331
+ outfile = @outputdir + file.path
332
+ debug_msg " working on %s (%s)" % [ file.full_name, outfile ]
333
+ rel_prefix = @outputdir.relative_path_from( outfile.dirname )
334
+
335
+ debug_msg " rendering #{outfile}"
336
+ self.render_template( templatefile, binding(), outfile )
337
+ end
338
+ end
339
+
340
+ ### Determines index path based on @options.main_page (or lack thereof)
341
+ def index_path
342
+ # Break early to avoid a big if block when no main page is specified
343
+ default = @files.first.path
344
+ return default unless @options.main_page
345
+
346
+ # Transform class name to file path
347
+ if @options.main_page.include?("::")
348
+ slashed = @options.main_page.sub(/^::/, "").gsub("::", "/")
349
+ "%s/%s.html" % [ class_dir, slashed ]
350
+ elsif file = @files.find { |f| f.full_name == @options.main_page }
351
+ file.path
352
+ else
353
+ default
354
+ end
355
+ end
356
+
357
+ ### Create index.html with frameset
358
+ def generate_index_file
359
+ debug_msg "Generating index file in #@outputdir"
360
+ templatefile = @template_dir + 'index.rhtml'
361
+ outfile = @outputdir + 'index.html'
362
+
363
+ self.render_template( templatefile, binding(), outfile )
364
+ end
365
+
366
+ ### Generate file with links for the search engine
367
+ def generate_se_index
368
+ debug_msg "Generating search engine index in #@outputdir"
369
+ templatefile = @template_dir + 'se_index.rhtml'
370
+ outfile = @outputdir + 'panel/links.html'
371
+
372
+ self.render_template( templatefile, binding(), outfile )
373
+ end
374
+
375
+ ### Copy all the resource files to output dir
376
+ def copy_resources
377
+ resoureces_path = @template_dir + RESOURCES_DIR
378
+ debug_msg "Copying #{resoureces_path}/** to #{@outputdir}/**"
379
+ FileUtils.cp_r resoureces_path.to_s, @outputdir.to_s, :preserve => true unless $dryrun
380
+ end
381
+
382
+ class FilesTree
383
+ attr_reader :children
384
+ def add(path, url)
385
+ path = path.split(File::SEPARATOR) unless Array === path
386
+ @children ||= {}
387
+ if path.length == 1
388
+ @children[path.first] = url
389
+ else
390
+ @children[path.first] ||= FilesTree.new
391
+ @children[path.first].add(path[1, path.length], url)
392
+ end
393
+ end
394
+ end
395
+
396
+ def generate_file_tree
397
+ if @files.length > 1
398
+ @files_tree = FilesTree.new
399
+ @files.each do |file|
400
+ @files_tree.add(file.relative_name, file.path)
401
+ end
402
+ [['', '', 'files', generate_file_tree_level(@files_tree)]]
403
+ else
404
+ []
405
+ end
406
+ end
407
+
408
+ def generate_file_tree_level(tree)
409
+ tree.children.keys.sort.map do |name|
410
+ child = tree.children[name]
411
+ if String === child
412
+ [name, child, '', []]
413
+ else
414
+ ['', '', name, generate_file_tree_level(child)]
415
+ end
416
+ end
417
+ end
418
+ end