sdoc_local_editor 0.3.17

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