docjs 0.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 (109) hide show
  1. data/CONCEPT.md +80 -0
  2. data/DOCUMENTATION.md +41 -0
  3. data/LICENSE.md +19 -0
  4. data/README.md +19 -0
  5. data/RENDERING.md +8 -0
  6. data/bin/docjs +190 -0
  7. data/docjs.gemspec +32 -0
  8. data/lib/boot.rb +34 -0
  9. data/lib/code_object/base.rb +48 -0
  10. data/lib/code_object/converter.rb +48 -0
  11. data/lib/code_object/exceptions.rb +5 -0
  12. data/lib/code_object/function.rb +84 -0
  13. data/lib/code_object/object.rb +18 -0
  14. data/lib/code_object/type.rb +43 -0
  15. data/lib/configs.rb +53 -0
  16. data/lib/document/document.rb +25 -0
  17. data/lib/dom/dom.rb +188 -0
  18. data/lib/dom/exceptions.rb +12 -0
  19. data/lib/dom/no_doc.rb +26 -0
  20. data/lib/dom/node.rb +415 -0
  21. data/lib/helper/helper.rb +120 -0
  22. data/lib/helper/linker.rb +130 -0
  23. data/lib/logger.rb +49 -0
  24. data/lib/parser/comment.rb +69 -0
  25. data/lib/parser/comment_parser.rb +90 -0
  26. data/lib/parser/exceptions.rb +6 -0
  27. data/lib/parser/meta_container.rb +20 -0
  28. data/lib/parser/parser.rb +269 -0
  29. data/lib/processor.rb +123 -0
  30. data/lib/renderer.rb +108 -0
  31. data/lib/tasks/render_task.rb +112 -0
  32. data/lib/thor.rb +27 -0
  33. data/lib/token/container.rb +84 -0
  34. data/lib/token/exceptions.rb +6 -0
  35. data/lib/token/handler.rb +242 -0
  36. data/lib/token/token.rb +46 -0
  37. data/templates/application.rb +14 -0
  38. data/templates/helpers/template.rb +66 -0
  39. data/templates/resources/css/.sass-cache/98c121fba905284c2c8ca6220fe3c590e5c9ec19/application.scssc +0 -0
  40. data/templates/resources/css/application.css +836 -0
  41. data/templates/resources/img/arrow_down.png +0 -0
  42. data/templates/resources/img/arrow_right.png +0 -0
  43. data/templates/resources/img/arrow_up.png +0 -0
  44. data/templates/resources/img/bullet_toggle_minus.png +0 -0
  45. data/templates/resources/img/bullet_toggle_plus.png +0 -0
  46. data/templates/resources/img/constructor.png +0 -0
  47. data/templates/resources/img/function.png +0 -0
  48. data/templates/resources/img/object.png +0 -0
  49. data/templates/resources/img/page.png +0 -0
  50. data/templates/resources/img/prototype.png +0 -0
  51. data/templates/resources/img/tag.png +0 -0
  52. data/templates/resources/js/application.js +318 -0
  53. data/templates/resources/js/jcore.js +129 -0
  54. data/templates/resources/js/jquery.cookie.js +92 -0
  55. data/templates/resources/js/jquery.js +16 -0
  56. data/templates/resources/js/jquery.tooltip.js +77 -0
  57. data/templates/resources/js/jquery.treeview.js +238 -0
  58. data/templates/resources/scss/_footer.scss +10 -0
  59. data/templates/resources/scss/_header.scss +184 -0
  60. data/templates/resources/scss/_helpers.scss +91 -0
  61. data/templates/resources/scss/_print.scss +20 -0
  62. data/templates/resources/scss/_resets.scss +132 -0
  63. data/templates/resources/scss/_tooltip.scss +26 -0
  64. data/templates/resources/scss/application.scss +442 -0
  65. data/templates/tasks/api_index_task.rb +26 -0
  66. data/templates/tasks/docs_task.rb +33 -0
  67. data/templates/tasks/json_data_task.rb +55 -0
  68. data/templates/tasks/typed_task.rb +54 -0
  69. data/templates/tokens/tokens.rb +22 -0
  70. data/templates/types/prototype.rb +20 -0
  71. data/templates/views/api_index.html.erb +21 -0
  72. data/templates/views/doc_page.html.erb +11 -0
  73. data/templates/views/function/_detail.html.erb +8 -0
  74. data/templates/views/function/index.html.erb +53 -0
  75. data/templates/views/index.html.erb +0 -0
  76. data/templates/views/layout/application.html.erb +73 -0
  77. data/templates/views/layout/json.html.erb +3 -0
  78. data/templates/views/object/index.html.erb +63 -0
  79. data/templates/views/tokens/_default.html.erb +11 -0
  80. data/templates/views/tokens/_default_token.html.erb +19 -0
  81. data/templates/views/tokens/_example.html.erb +2 -0
  82. data/templates/views/tokens/_examples.html.erb +1 -0
  83. data/test/code_object/converter.rb +78 -0
  84. data/test/code_object/prototype.rb +70 -0
  85. data/test/configs.rb +65 -0
  86. data/test/docs/README.CONCEPT.md +83 -0
  87. data/test/docs/README.md +14 -0
  88. data/test/dom/dom.absolute_nodes.rb +40 -0
  89. data/test/dom/dom.rb +72 -0
  90. data/test/dom/node.rb +53 -0
  91. data/test/integration/converter.rb +72 -0
  92. data/test/integration/parser_factory.rb +28 -0
  93. data/test/interactive.rb +7 -0
  94. data/test/js-files/absolute.js +11 -0
  95. data/test/js-files/comments_in_strings.js +31 -0
  96. data/test/js-files/core-doc-relative.js +77 -0
  97. data/test/js-files/core-doc.js +145 -0
  98. data/test/js-files/nested.js +34 -0
  99. data/test/js-files/nested_with_strings.js +35 -0
  100. data/test/js-files/prototype.js +33 -0
  101. data/test/js-files/simple.js +17 -0
  102. data/test/js-files/tokens.js +32 -0
  103. data/test/parser/comments_in_strings.rb +51 -0
  104. data/test/parser/intelligent_skip_until.rb +110 -0
  105. data/test/parser/parser.rb +273 -0
  106. data/test/rspec_helper.rb +23 -0
  107. data/test/token/handler.rb +136 -0
  108. data/test/token/tokens.rb +52 -0
  109. metadata +184 -0
@@ -0,0 +1,6 @@
1
+ module Parser
2
+
3
+ class NotValidTokenline < Exception
4
+ end
5
+
6
+ end
@@ -0,0 +1,20 @@
1
+ # ../data.img#1772248:1
2
+ module Parser
3
+
4
+ # is included by {CodeObject::Base} and {Parser::Comment}
5
+ module MetaContainer
6
+
7
+ attr_reader :filepath, :source, :line_start
8
+
9
+ def add_meta_data(filepath, source, line_start)
10
+ @filepath, @source, @line_start = filepath, source, line_start+1 # counting from 1
11
+ end
12
+
13
+ def clone_meta(other)
14
+ @filepath, @source, @line_start = other.filepath, other.source, other.line_start
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
@@ -0,0 +1,269 @@
1
+ # ../data.img#1869414:2
2
+ require 'strscan'
3
+ require_relative 'comment_parser'
4
+
5
+ #
6
+ # ![Parser Overview](../uml/Parser.svg)
7
+ #
8
+ #
9
+ module Parser
10
+
11
+ NO_BR = /((?!\n)\s)/
12
+ ALL = /./m
13
+
14
+ # Multiline Comments
15
+ M_START = /\/\*+/
16
+
17
+ # End of multiline comment with all leading whitespaces (no breaks)
18
+ M_END = /#{NO_BR}*\*+\//
19
+
20
+ # Singleline Comments
21
+ S_START = /\/\//
22
+ S_END = /\n/
23
+
24
+
25
+ LINE_START = /(#{NO_BR})*\*+#{NO_BR}?/
26
+ LINE_END = /\n/
27
+ EMPTY_LINE = /^\s*$/
28
+
29
+ # CAUTION: \s can contain breaks "\n"
30
+ TOKENLINE_START = /\s*@/
31
+ TOKENLINE = /
32
+ #{TOKENLINE_START}
33
+ (?<name>\w+)
34
+ (?:#{NO_BR}?(?<content>#{ALL}+)|$)
35
+ /x
36
+
37
+ # String delimiter
38
+ S_STRING = /'/
39
+ D_STRING = /"/
40
+
41
+ REGEXP_START = /\/[^\/]/
42
+ REGEXP_END = /\//
43
+
44
+ NON_COMMENT_PATTERNS = {
45
+ S_STRING => S_STRING,
46
+ D_STRING => D_STRING,
47
+ REGEXP_START => REGEXP_END
48
+ }
49
+
50
+ NON_CODE_PATTERNS = {
51
+ M_START => M_END,
52
+ S_START => S_END,
53
+ S_STRING => S_STRING,
54
+ D_STRING => D_STRING,
55
+ REGEXP_START => REGEXP_END
56
+ }
57
+
58
+ class Parser
59
+
60
+ attr_reader :filepath, :offset
61
+
62
+ # A new StringScanner instance will be used to {#parse parse} the given
63
+ # `input`.
64
+ #
65
+ # @param [String] input
66
+ def initialize(input, args = {})
67
+
68
+ raise Exception, "Expected input to be a String, got #{input.class}" unless input.is_a? String
69
+
70
+ # Default Values
71
+ @filepath = args[:filepath] || "No File specified"
72
+ @offset = args[:offset] || -1 # we are adding 1 later
73
+
74
+
75
+ # clean input and convert windows linebreaks to normal ones
76
+ @to_parse = input.gsub(/\r\n/, "\n")
77
+ @scanner = StringScanner.new @to_parse
78
+ @comments = []
79
+ end
80
+
81
+
82
+ # Recursivly parses the {#initialize given input} and thereby ignores
83
+ # strings.
84
+ # @todo Rewrite to use skip_intelligent_until
85
+ # @return [Array<Parser::Comment>] the parsed comment-stream
86
+ def parse()
87
+ @scanner.skip /\s/
88
+ @scanner.skip_until /#{M_START}|#{S_START}|#{NON_COMMENT_PATTERNS.keys.join('|')}|$/
89
+
90
+ found = @scanner.matched
91
+
92
+ if found.match M_START
93
+ parse_comment_until(M_END)
94
+
95
+ elsif found.match S_START
96
+ parse_comment_until(S_END)
97
+
98
+ else
99
+ matched_pattern = NON_COMMENT_PATTERNS.detect do |start_pattern, end_pattern|
100
+ found.match start_pattern
101
+ end
102
+ @scanner.skip_escaping_until matched_pattern.last unless matched_pattern.nil?
103
+ end
104
+
105
+ if @scanner.eos?
106
+ return @comments
107
+ else
108
+ parse
109
+ end
110
+ end
111
+
112
+ def self.parse_file(path)
113
+ stream = File.read path
114
+ Parser.new(stream, :filepath => path).parse
115
+ end
116
+
117
+ protected
118
+
119
+ def parse_comment_until(ending)
120
+ content = @scanner.scan_until_ahead ending
121
+ comment = CommentParser.new(content).parse unless content.nil?
122
+
123
+ # only proceed, if it is a tokenized comment
124
+ return parse unless comment.has_tokens?
125
+
126
+ # search scope for that comment
127
+ @scanner.skip /\n/
128
+ scope = @scanner.save_scanned { find_scope }
129
+
130
+ code_line = @to_parse.line_of(scope.min) + @offset + 1
131
+ source = @to_parse[scope]
132
+
133
+ # Add Metadata
134
+ comment.add_meta_data @filepath, source, code_line
135
+
136
+ # Save Comment
137
+ @comments << comment
138
+
139
+ comment.add_children Parser.new(source, :filepath => @filepath, :offset => code_line-1).parse
140
+ end
141
+
142
+ def find_scope(scope_stack = [], ignore_line_end = false)
143
+
144
+ if ignore_line_end
145
+ return if scope_stack.empty?
146
+ @scanner.skip /\s/
147
+ end
148
+
149
+ # adding |$ only if we don't ignore line_ends (which is most of the time)
150
+ @scanner.intelligent_skip_until /\{|\(|\}|\)#{'|$' unless ignore_line_end}/
151
+
152
+ match = @scanner.matched
153
+
154
+ case match
155
+ when '{'
156
+ find_scope(scope_stack << '}', ignore_line_end)
157
+
158
+ when '('
159
+ find_scope(scope_stack << ')', ignore_line_end)
160
+
161
+ when '}', ')'
162
+ if(scope_stack.last == match)
163
+ scope_stack.pop
164
+ find_scope(scope_stack, ignore_line_end)
165
+ else
166
+ # currently just ignore non matching closing pairs
167
+ puts "I'm ignoring #{match} at #{@scanner.pos} of #{@filepath}"
168
+ end
169
+ else
170
+
171
+ if ignore_line_end
172
+ if not @scanner.eos?
173
+ find_scope(scope_stack, ignore_line_end)
174
+
175
+ elsif not scope_stack.empty?
176
+ raise "Unmatched Scope-Stack: #{scope_stack}"
177
+ end
178
+
179
+ else
180
+ find_scope(scope_stack, true) unless scope_stack.empty?
181
+ end
182
+ end
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+
189
+ # We have to extend StringScanner a little bit to fit our needs.
190
+ #
191
+ # @see Parser::Parser
192
+ # @see Parser::CommentParser
193
+ class StringScanner
194
+
195
+ # returns the string until `pattern` matches, then consums `pattern`
196
+ #
197
+ # @example
198
+ # scanner = StringScanner.new("hello world")
199
+ # scanner.scan_until_ahead(/\s+/) #=> "hello"
200
+ # scanner.pos #=> 5
201
+ #
202
+ # @param [Regexp] pattern the pattern to scan until
203
+ # @return [String] the String before `pattern`
204
+ def scan_until_ahead(pattern)
205
+ content = self.scan_until /(?=(#{pattern}))/
206
+ self.skip pattern
207
+ return content
208
+ end
209
+
210
+ # will stop to scan at the specified pattern or at eos and returns the
211
+ # consumed string.
212
+ #
213
+ # @param [Regexp] pattern the pattern to scan for
214
+ # @return [String] the String before `pattern`
215
+ def scan_until_or_end(pattern)
216
+ self.scan_until(pattern) or self.scan_until(/$/)
217
+ end
218
+
219
+ # skips content within comments, strings and regularexpressions
220
+ def intelligent_skip_until(pattern)
221
+
222
+ self.skip_escaping_until(/#{pattern}|#{Parser::NON_CODE_PATTERNS.keys.join('|')}/)
223
+
224
+ found = self.matched
225
+
226
+ raise end_of_string_error(pattern) if self.matched.nil?
227
+
228
+ return if found.match pattern
229
+
230
+ Parser::NON_CODE_PATTERNS.each do |start_pattern, end_pattern|
231
+ if found.match start_pattern
232
+ self.skip_escaping_until end_pattern
233
+ return self.intelligent_skip_until pattern
234
+ end
235
+ end
236
+ end
237
+
238
+ def save_scanned
239
+ pos_start = self.pos #- 1 # fixes missing first char
240
+ yield
241
+ pos_end = self.pos
242
+ Range.new(pos_start, pos_end)
243
+ end
244
+
245
+ def skip_escaping_until(pattern)
246
+
247
+ self.skip_until(/\\|#{pattern}/)
248
+
249
+ raise end_of_string_error(pattern) if self.matched.nil?
250
+
251
+ if self.matched.match /\\/
252
+ self.getch
253
+ skip_escaping_until(pattern)
254
+ end
255
+ end
256
+
257
+ protected
258
+
259
+ def end_of_string_error(pattern)
260
+ Error.new "Unexpected end of String, expected: #{pattern.inspect} in \"#{self.string}\" at pos:#{self.pos}"
261
+ end
262
+
263
+ end
264
+
265
+ class String
266
+ def line_of(pos)
267
+ self[0..pos].count "\n"
268
+ end
269
+ end
@@ -0,0 +1,123 @@
1
+ require 'rdiscount'
2
+ require_relative 'dom/dom'
3
+ require_relative 'tasks/render_task'
4
+ require_relative 'document/document'
5
+
6
+ module Processor
7
+
8
+ RenderTask = Struct.new :name, :description, :block
9
+ @@render_tasks = {}
10
+
11
+ # Accessor Method for RenderTasks
12
+ def self.render_tasks
13
+ @@render_tasks
14
+ end
15
+
16
+ # @group Combined Stages
17
+
18
+ def self.process_and_render
19
+ process_files_to_dom
20
+ perform_all_tasks
21
+ end
22
+
23
+ def self.process_files_to_dom(files = nil)
24
+ process_comments parse_files(files)
25
+ end
26
+
27
+ # @group Stage #1 - FileProcessor
28
+
29
+ # Parsing Files and creating comment stream
30
+ def self.parse_files(files = nil)
31
+ files ||= Configs.files
32
+
33
+ return if files.nil?
34
+
35
+ files = [files] unless files.is_a? Array
36
+ comments = []
37
+
38
+ files.each do |file|
39
+ Logger.info "Processing file #{file}"
40
+ comments += Parser::Parser.parse_file(file)
41
+ end
42
+
43
+ return comments
44
+ end
45
+
46
+ # @group Stage #2 - CommentProcessor
47
+
48
+ # Processing comment-stream and convert to {CodeObject CodeObjects}
49
+ # This stage also adds the CodeObjects to Dom.
50
+ def self.process_comments(comments)
51
+
52
+ comments = [comments] unless comments.is_a? Array
53
+
54
+ comments.each do |comment|
55
+ code_object = comment.to_code_object # convert to code_object
56
+ Logger.debug "Adding to Dom: #{code_object}"
57
+ Dom.add_node(code_object.path, code_object) # add to dom
58
+ end
59
+ end
60
+
61
+
62
+ # @group Stage #3 - TemplateProcessor
63
+
64
+ # just some notes
65
+
66
+ # command line:
67
+ # $~ jsdoc render_tasks
68
+ # Registered Rendertasks:
69
+ # - typed: renders objects type-dependant
70
+ # - overview: renders an overview
71
+ # - files: converts specified markdown files and renders them
72
+ #
73
+ def self.perform_all_tasks
74
+ perform_tasks @@render_tasks.keys
75
+ end
76
+
77
+ def self.perform_tasks(tasks)
78
+
79
+ tasks = [tasks] unless tasks.is_a? Array
80
+
81
+ tasks.each do |task|
82
+ task = task.to_sym
83
+ raise Exception, "No render-task registered with name '#{task}'" unless @@render_tasks.has_key? task
84
+
85
+ Logger.debug "Rendering task '#{task}'"
86
+ @@render_tasks[task].new.perform
87
+ end
88
+ end
89
+
90
+ # @group Stage #4 - Document Processor
91
+
92
+ def self.prepare_documents
93
+ # underscores will be replaced with whitespaces as title
94
+ Configs.docs.each do |doc|
95
+
96
+ doc_path = File.expand_path(doc, Configs.wdir)
97
+ Logger.debug "Working with Document #{doc_path}"
98
+
99
+ contents = File.read(doc_path)
100
+
101
+ # Those documents get registered in a special {Dom::Node} Dom.docs
102
+ document = Document::Document.new(doc_path, contents)
103
+ Dom.docs.add_node(document.path, document)
104
+
105
+ # The docs can be accessed via Dom later on
106
+ end
107
+ end
108
+
109
+
110
+
111
+ # @group RenderTask-Setup
112
+
113
+ def self.register_render_task(name, klass)
114
+ @@render_tasks[name.to_sym] = klass
115
+ end
116
+
117
+ def self.unregister_render_task(name)
118
+ @@render_tasks.delete(name.to_sym)
119
+ end
120
+
121
+
122
+
123
+ end
@@ -0,0 +1,108 @@
1
+ # ../data.img#1783836:1
2
+ require 'erb'
3
+ require 'fileutils'
4
+
5
+ class Renderer
6
+
7
+ def initialize(default_path, layout)
8
+ @_path = default_path
9
+ @_layout = layout
10
+ end
11
+
12
+ # Teilweise aus rails...
13
+ # @todo better error-handling with partial and layout context information!!!
14
+ def render(opt = nil, extra_options = {})
15
+
16
+ if opt.nil?
17
+ opt = { :layout => @_layout }
18
+
19
+ elsif opt.is_a?(String) || opt.is_a?(Symbol)
20
+ extra_options[:template] = opt
21
+ extra_options[:layout] ||= @_layout
22
+ opt = extra_options
23
+
24
+ elsif !opt.is_a?(Hash)
25
+ extra_options[:partial] = opt
26
+ opt = extra_options
27
+ end
28
+
29
+ if opt[:partial]
30
+
31
+ # add underscore to last element of foo/bar/baz
32
+ parts = opt[:partial].split('/')
33
+ parts[-1] = "_"+parts.last
34
+
35
+ begin
36
+ template_file = File.read path_to_template(parts.join('/'))
37
+ rescue Exception
38
+ raise "Could not find Partial '#{opt[:partial]}'"
39
+ end
40
+ if opt[:collection]
41
+
42
+ partial_name = opt[:partial].split('/').last
43
+
44
+ opt[:collection].map { |item|
45
+ define_singleton_method(partial_name) { item }
46
+ ERB.new(template_file).result(binding)
47
+ }.join "\n"
48
+ else
49
+
50
+ # If there are locals we have to save our instance binding, otherwise we will store our
51
+ # newly created local-variables in the blockcontext of each_pair
52
+ # values has to be defined explicitly to be overridden by the block and still available inside of eval
53
+ if opt[:locals]
54
+ value = nil
55
+ instance_context = binding
56
+ opt[:locals].each_pair do |local, value|
57
+ Logger.warn("Please change your partial-name or local binding, because #{local} is already set in this context.") if respond_to? local
58
+ define_singleton_method(local) { value }
59
+ end
60
+ end
61
+
62
+ ERB.new(template_file).result(binding)
63
+ end
64
+ else
65
+ # bind @current_path correctly to use in helpers and views
66
+ if opt[:to_file]
67
+ # Make absolute
68
+ opt[:to_file] = File.expand_path(opt[:to_file], Configs.output)
69
+ @current_path = File.dirname opt[:to_file]
70
+ else
71
+ @current_path ||= Configs.output
72
+ end
73
+
74
+ # render 'view_name', :option1 => 1, :option2 => 2
75
+ view = ERB.new(File.read path_to_template opt[:template]).result(binding)
76
+
77
+ # then render with layout
78
+ if opt[:layout]
79
+ layout = File.read path_to_template opt[:layout]
80
+ view = render_in_layout(layout) { view }
81
+ end
82
+
83
+ # Render to file, if desired
84
+ if opt[:to_file]
85
+ # create directories recursive
86
+ FileUtils.mkpath File.dirname opt[:to_file]
87
+
88
+ File.open(opt[:to_file], "w+") do |f|
89
+ f.write view
90
+ end
91
+ else
92
+ return view
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ protected
99
+
100
+ def render_in_layout(layout, &view)
101
+ ERB.new(layout).result(binding)
102
+ end
103
+
104
+ def path_to_template(file)
105
+ File.expand_path "#{file}.html.erb", @_path
106
+ end
107
+
108
+ end