docjs 0.1.2 → 0.1.3

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 (39) hide show
  1. data/README.md +7 -5
  2. data/bin/docjs +33 -8
  3. data/bin/docjs.rb +239 -0
  4. data/docjs.gemspec +2 -2
  5. data/lib/boot.rb +5 -3
  6. data/lib/code_object/base.rb +1 -0
  7. data/lib/code_object/function.rb +21 -37
  8. data/lib/code_object/object.rb +1 -1
  9. data/lib/helper/helper.rb +16 -3
  10. data/lib/helper/linker.rb +5 -2
  11. data/lib/parser/parser.rb +20 -6
  12. data/lib/token/container.rb +0 -1
  13. data/lib/token/handler.rb +46 -6
  14. data/lib/token/token.rb +3 -2
  15. data/templates/helpers/template.rb +15 -5
  16. data/templates/resources/css/application.css +65 -14
  17. data/templates/resources/js/application.js +33 -11
  18. data/templates/resources/js/regexpx.js +652 -0
  19. data/templates/resources/js/shBrushJScript.js +55 -0
  20. data/templates/resources/js/shCore.js +1596 -0
  21. data/templates/resources/scss/_header.scss +2 -1
  22. data/templates/resources/scss/_helpers.scss +6 -6
  23. data/templates/resources/scss/_highlighter.scss +70 -0
  24. data/templates/resources/scss/application.scss +8 -8
  25. data/templates/tokens/tokens.rb +55 -4
  26. data/templates/views/function/_detail.html.erb +1 -1
  27. data/templates/views/function/index.html.erb +16 -4
  28. data/templates/views/layout/application.html.erb +5 -5
  29. data/templates/views/layout/json.html.erb +3 -3
  30. data/templates/views/object/index.html.erb +3 -3
  31. data/templates/views/tokens/_default.html.erb +4 -2
  32. data/templates/views/tokens/_default_token.html.erb +2 -1
  33. data/templates/views/tokens/_example.html.erb +1 -1
  34. data/templates/views/tokens/_overload.html.erb +12 -0
  35. data/test/interactive.rb +5 -2
  36. data/test/js-files/core-doc.js +20 -12
  37. data/test/parser/intelligent_skip_until.rb +1 -1
  38. data/test/parser/parser.rb +3 -6
  39. metadata +8 -2
@@ -15,4 +15,4 @@ end
15
15
  CodeObject::Type.register :object, CodeObject::Object
16
16
  Token::Handler.register :object, :handler => :noop, :area => :none
17
17
 
18
- Token::Handler.register :prop, :handler => :typed_with_name
18
+ Token::Handler.register :prop, :handler => :typed_with_name
@@ -1,6 +1,7 @@
1
1
  # ../data.img#1800236:1
2
2
  require 'pathname'
3
3
  require 'rdiscount'
4
+ require 'cgi'
4
5
 
5
6
  require_relative 'linker'
6
7
 
@@ -56,12 +57,17 @@ module Helper
56
57
  return html
57
58
  end
58
59
 
59
- def code(source)
60
+ def code(source, opts = {})
61
+
62
+ # defaults
63
+ opts[:firstline] ||= 1
64
+ opts[:class] ||= "block"
65
+
60
66
  # find minimal intendation
61
67
  intendation = source.lines.map {|line| line.match(/(^\s+)/) && line.match(/(^\s+)/).captures.first.size || 0 }.min
62
68
 
63
69
  # @todo there has to be a better way for that
64
- tag :code, source.lines.map { |line| line[intendation .. line.size] }.join(""), :class => 'block'
70
+ tag :code, h(source.lines.map { |line| line[intendation .. line.size] }.join("")), :class => "#{opts[:class]} brush:js first-line:#{opts[:firstline]}"
65
71
  end
66
72
 
67
73
  def to_html(markdown_text, *markdown_opts)
@@ -72,6 +78,10 @@ module Helper
72
78
  RDiscount.new(markdown_text, :generate_toc).toc_content
73
79
  end
74
80
 
81
+ def h(to_escape)
82
+ CGI.escapeHTML(to_escape)
83
+ end
84
+
75
85
  def to_relative(path)
76
86
 
77
87
  path = Pathname.new(path)
@@ -92,10 +102,13 @@ module Helper
92
102
 
93
103
  code_object = opts[:of] or raise Exception.new("Parameter :of (CodeObject) required")
94
104
  area = opts[:in] or raise Exception.new("Parameter :in (Area) required")
105
+ exclude = opts[:without] || []
95
106
 
96
107
  rendered = ""
97
108
 
98
- token_groups = code_object.tokens.values.each do |tokens|
109
+ tokens = code_object.tokens.reject {|token, v| exclude.include? token }
110
+
111
+ token_groups = tokens.values.each do |tokens|
99
112
 
100
113
  # tokens is an array of Token::Token
101
114
  if not tokens.empty? and tokens.first.area == area
@@ -75,15 +75,18 @@ module Helper
75
75
 
76
76
  # Returns the relative path (from dom) to this node
77
77
  # The Node can be either a {CodeObject::Base CodeObject} or a {Document::Document Document}.
78
+ #
79
+ # @note this method can be overwritten in every included Helper to fit your custom API-Layout
78
80
  #
79
81
  # @param [CodeObject::Base, Document::Document] object
80
82
  #
81
83
  # @example
82
- # Dom[:Foo][:bar].file_path #=> Foo/bar
84
+ # Dom[:Foo][:bar].file_path #=> Foo/bar.html
85
+ # Dom['Foo.bar'].file_path :format => :json #=> Foo/bar.json
83
86
  #
84
87
  def path_to(object, args = {})
85
88
 
86
- return "" if object.nil?
89
+ return "" if object.nil?
87
90
  format = args[:format] || :html
88
91
  path = object.parents.push(object).map{|p| p.name}.join('/') + ".#{format.to_s}"
89
92
 
@@ -73,8 +73,9 @@ module Parser
73
73
 
74
74
 
75
75
  # clean input and convert windows linebreaks to normal ones
76
- @to_parse = input.gsub(/\r\n/, "\n")
76
+ @to_parse = input.force_encoding("UTF-8").gsub(/\r\n/, "\n")
77
77
  @scanner = StringScanner.new @to_parse
78
+
78
79
  @comments = []
79
80
  end
80
81
 
@@ -117,19 +118,32 @@ module Parser
117
118
  protected
118
119
 
119
120
  def parse_comment_until(ending)
120
- content = @scanner.scan_until_ahead ending
121
+ content = @scanner.scan_until_ahead ending # this consumes ending, but don't includes it
121
122
  comment = CommentParser.new(content).parse unless content.nil?
122
123
 
123
124
  # only proceed, if it is a tokenized comment
124
- return parse unless comment.has_tokens?
125
+ return parse unless comment and comment.has_tokens?
126
+
127
+
128
+ # First skip some white spaces, that may occure after comment
129
+ @scanner.skip /#{NO_BR}+/
125
130
 
126
131
  # search scope for that comment
127
132
  @scanner.skip /\n/
133
+
128
134
  scope = @scanner.save_scanned { find_scope }
129
-
135
+
136
+
137
+ # FIX: UTF-8 characters destroyed the string-slicing, because scanner is working with
138
+ # byte-positions only
139
+ @to_parse.force_encoding "ISO-8859-1"
140
+
130
141
  code_line = @to_parse.line_of(scope.min) + @offset + 1
131
142
  source = @to_parse[scope]
132
143
 
144
+ # Switch back to UTF-8
145
+ @to_parse.force_encoding "UTF-8"
146
+
133
147
  # Add Metadata
134
148
  comment.add_meta_data @filepath, source, code_line
135
149
 
@@ -147,7 +161,7 @@ module Parser
147
161
  end
148
162
 
149
163
  # adding |$ only if we don't ignore line_ends (which is most of the time)
150
- @scanner.intelligent_skip_until /\{|\(|\}|\)#{'|$' unless ignore_line_end}/
164
+ @scanner.intelligent_skip_until /\{|\(|\}|\)#{"|$" unless ignore_line_end}/
151
165
 
152
166
  match = @scanner.matched
153
167
 
@@ -236,7 +250,7 @@ class StringScanner
236
250
  end
237
251
 
238
252
  def save_scanned
239
- pos_start = self.pos #- 1 # fixes missing first char
253
+ pos_start = self.pos
240
254
  yield
241
255
  pos_end = self.pos
242
256
  Range.new(pos_start, pos_end)
@@ -1,6 +1,5 @@
1
1
  # ../data.img#1811394:1
2
2
  require_relative 'exceptions'
3
- require_relative 'handler'
4
3
 
5
4
  module Token
6
5
 
@@ -67,23 +67,25 @@ module Token
67
67
  (?<content>#{ALL}*)
68
68
  /x
69
69
 
70
+
71
+ # @note It would be nice, if those defaults could be used without self.add_token
70
72
  @@defaults = {
71
73
  :text_only => ->(tokenklass, content) {
72
- self.add_token tokenklass.new(:content => content)
74
+ tokenklass.new(:content => content)
73
75
  },
74
76
 
75
77
  :typed => ->(tokenklass, content) {
76
78
  typestring, content = TOKEN_W_TYPE.match(content).captures
77
79
  types = typestring.split /,\s*/
78
80
 
79
- self.add_token tokenklass.new(:types => types, :content => content)
81
+ tokenklass.new(:types => types, :content => content)
80
82
  },
81
83
 
82
84
  :typed_with_name => ->(tokenklass, content) {
83
85
  typestring, name, content = TOKEN_W_TYPE_NAME.match(content).captures
84
86
  types = typestring.split /,\s*/
85
87
 
86
- self.add_token tokenklass.new(:name => name, :types => types, :content => content)
88
+ tokenklass.new(:name => name, :types => types, :content => content)
87
89
  },
88
90
 
89
91
  :named_multiline => ->(tokenklass, content) {
@@ -93,7 +95,28 @@ module Token
93
95
  name = rows.shift.strip
94
96
  content = rows.join("\n")
95
97
 
96
- self.add_token tokenklass.new(:name => name, :content => content)
98
+ tokenklass.new(:name => name, :content => content)
99
+ },
100
+
101
+ :named_nested_shorthand => ->(tokenklass, content) {
102
+
103
+ # First remove linebreaks with 2-times intendation
104
+ lines = content.gsub(/\n((?!\n)\s){2}/, ' ').split(/\n/)
105
+ name = lines.shift.strip
106
+ documentation = []
107
+ children = []
108
+
109
+ lines.each do |line|
110
+ if TOKEN_W_TYPE_NAME.match(line)
111
+ # apply default-handler :typed_with_name to each child-line
112
+ # @todo maybe we need a special way to select Children's Class?
113
+ children << Handler.apply(:typed_with_name, tokenklass, line)
114
+ else
115
+ documentation << line
116
+ end
117
+ end
118
+
119
+ tokenklass.new(:name => name, :types => [], :children => children, :content => documentation.join("\n"))
97
120
  },
98
121
 
99
122
  :noop => ->(tokenklass, content) {}
@@ -107,6 +130,11 @@ module Token
107
130
  @@handlers
108
131
  end
109
132
 
133
+ # Use a default handler
134
+ def self.apply(default_handler, *args)
135
+ @@defaults[default_handler].call(*args)
136
+ end
137
+
110
138
  # Registering a new Tokenhandler
111
139
  # ==============================
112
140
  # It is possible to register your own Tokenhandlers and therefore extend the
@@ -203,11 +231,11 @@ module Token
203
231
  if block_given?
204
232
  # handler is already defined
205
233
  elsif options[:handler] and @@defaults.include?(options[:handler])
206
- handler = @@defaults[options[:handler]]
234
+ handler = self.build_handler(options[:handler])
207
235
  elsif options[:handler]
208
236
  raise Exception, "#{type} has no registered Tokenhandler"
209
237
  else
210
- handler = @@defaults[:text_only]
238
+ handler = self.build_handler(:text_only)
211
239
  end
212
240
 
213
241
  # Dynamically create Class named TokennameToken
@@ -237,6 +265,18 @@ module Token
237
265
  def self.add_default_handler(name, &block)
238
266
  @@defaults[name] = block;
239
267
  end
268
+
269
+ protected
270
+
271
+ def self.build_handler(type)
272
+
273
+ handler = @@defaults[type]
274
+
275
+ ->(tokenklass, content) {
276
+ token = instance_exec(tokenklass, content, &handler)
277
+ self.add_token token unless token.nil? # can be NOOP
278
+ }
279
+ end
240
280
 
241
281
  end
242
282
  end
@@ -15,7 +15,8 @@ module Token
15
15
  def self.process_options(options = {})
16
16
 
17
17
  # pushing defaults
18
- options[:template] ||= :default
18
+ options[:template] ||= :default
19
+ options[:description] ||= ""
19
20
 
20
21
  options[:html] = {
21
22
  :class => options[:token].to_s
@@ -23,7 +24,7 @@ module Token
23
24
 
24
25
  options[:area] ||= :body
25
26
 
26
- %w(handler token template html area).each do |opt|
27
+ %w(handler token template html area description).each do |opt|
27
28
  self.class_variable_set("@@#{opt}".to_sym, options[opt.to_sym])
28
29
 
29
30
  class_eval "
@@ -4,15 +4,25 @@ module Helper
4
4
  # need them anymore.
5
5
  module Template
6
6
 
7
- def signature(method)
8
- params = method.params.map { |p|
7
+ def signature(method_or_token)
8
+
9
+ if method_or_token.is_a? Token::Token
10
+ params = method_or_token.children.select {|t| t.token == :param }
11
+ returns = method_or_token.children.select {|t| t.token == :return }
12
+ else
13
+ params = method_or_token.params
14
+ returns = method_or_token.returns
15
+ end
16
+
17
+
18
+ params = params.map { |p|
9
19
  "<span class=\"param\">#{p.name}</span>" +
10
20
  "<span class=\"tooltip\">(<span class=\"types\">#{p.types.map{|t| link_to(t) }.join(', ')}</span>) " +
11
21
  "#{replace_links p.content}</span>"
12
- }.join(', ') unless method.params.nil?
22
+ }.join(', ') unless params.nil?
13
23
 
14
- return_types = method.returns.first.types.map{|type| link_to(type) }.join(', ') unless method.returns.nil? or method.returns.first.nil?
15
- "(#{return_types || 'Void'}) <span class='name'>#{method.name}</span>(<span class='params'>#{params}</span>)"
24
+ return_types = returns.first.types.map{|type| link_to(type) }.join(', ') unless returns.nil? or returns.first.nil?
25
+ "(#{return_types || 'Void'}) <span class='name'>#{method_or_token.name}</span>(<span class='params'>#{params}</span>)"
16
26
  end
17
27
 
18
28
  def subsection(id, opts = {})
@@ -364,7 +364,8 @@ input:invalid, textarea:invalid {
364
364
  border-radius: 10px;
365
365
  padding: 0;
366
366
  margin: 0;
367
- overflow-y: scroll; }
367
+ overflow: hidden;
368
+ /*overflow-y: scroll;*/ }
368
369
  #header .col50 section > ul::-webkit-scrollbar {
369
370
  width: 7px;
370
371
  height: 9px; }
@@ -498,6 +499,62 @@ input:invalid, textarea:invalid {
498
499
  font-family: "Consolas", "monospace";
499
500
  padding: 0 0.2em; }
500
501
 
502
+ code.source, .syntaxhighlighter, code.example {
503
+ box-shadow: inset 0px 0px 0px 1px white;
504
+ border: 1px solid #d9d9d9;
505
+ font-family: "Consolas", "monospace";
506
+ display: block;
507
+ background: #f3f3f3;
508
+ font-size: 9pt;
509
+ line-height: 1.4em;
510
+ padding: 0.5em;
511
+ margin-bottom: 1.33em; }
512
+
513
+ /* js-replaced version */
514
+ .syntaxhighlighter {
515
+ /* revert no-js settings */
516
+ background: #fff;
517
+ padding: 0;
518
+ /* syntaxhighlighting settings */ }
519
+ .syntaxhighlighter table {
520
+ width: 100%; }
521
+ .syntaxhighlighter table td {
522
+ border: 1px solid #fff;
523
+ /* rebuild double-border effect */ }
524
+ .syntaxhighlighter .gutter {
525
+ background-color: #f3f3f3;
526
+ color: silver;
527
+ padding: 0.5em;
528
+ border-right: 1px solid #d9d9d9;
529
+ text-align: right; }
530
+ .syntaxhighlighter .gutter .line {
531
+ font-size: 9pt;
532
+ line-height: 1.4em; }
533
+ .syntaxhighlighter .code {
534
+ padding: 0.5em 0;
535
+ width: 100%; }
536
+ .syntaxhighlighter .code .line {
537
+ padding: 0 0.5em; }
538
+ .syntaxhighlighter .code .line:hover {
539
+ background: #f5f5f5; }
540
+ .syntaxhighlighter.example {
541
+ background: #f3f3f3; }
542
+ .syntaxhighlighter.example .line {
543
+ padding-left: 1em; }
544
+ .syntaxhighlighter.example .line:hover {
545
+ background: #efefef; }
546
+ .syntaxhighlighter .keyword {
547
+ font-weight: bold; }
548
+ .syntaxhighlighter .string {
549
+ color: #d84b0f; }
550
+ .syntaxhighlighter .comments {
551
+ color: #998;
552
+ font-style: italic; }
553
+ .syntaxhighlighter .comments a {
554
+ color: #998; }
555
+ .syntaxhighlighter .constants {
556
+ color: #4183c2; }
557
+
501
558
  body, select, input, textarea {
502
559
  font-family: Arial, sans-serif;
503
560
  font-size: 10pt;
@@ -539,13 +596,13 @@ a {
539
596
  .icon.prototype {
540
597
  background: url(../img/prototype.png) no-repeat left center;
541
598
  padding-left: 24px; }
542
- .icon.arrow_right, div#main .body .source.collapsed {
599
+ .icon.arrow_right, div#main .body h3.source.collapsed {
543
600
  background: url(../img/arrow_right.png) no-repeat left center;
544
601
  padding-left: 24px; }
545
602
  .icon.arrow_up {
546
603
  background: url(../img/arrow_up.png) no-repeat left center;
547
604
  padding-left: 24px; }
548
- .icon.arrow_down, div#main .body .source {
605
+ .icon.arrow_down, div#main .body h3.source {
549
606
  background: url(../img/arrow_down.png) no-repeat left center;
550
607
  padding-left: 24px; }
551
608
 
@@ -616,6 +673,7 @@ div#main nav.sidebar {
616
673
  box-shadow: inset 0px 0px 0px 1px white;
617
674
  border: 1px solid #d9d9d9;
618
675
  font-family: "Consolas", "monospace";
676
+ display: block;
619
677
  background: #f3f3f3;
620
678
  margin: 40px 0 12px 12px;
621
679
  padding: 0;
@@ -679,15 +737,6 @@ div#main .body .summary {
679
737
  div#main .body .summary a:hover {
680
738
  text-decoration: none;
681
739
  color: #3976b1; }
682
- div#main .body code.block {
683
- box-shadow: inset 0px 0px 0px 1px white;
684
- border: 1px solid #d9d9d9;
685
- font-family: "Consolas", "monospace";
686
- background: #f3f3f3;
687
- display: block;
688
- padding: 0.5em;
689
- color: #555;
690
- margin-bottom: 1.33em; }
691
740
  div#main .body .section {
692
741
  padding-bottom: 12px; }
693
742
  div#main .body .subsection > ul {
@@ -707,9 +756,9 @@ div#main .body .subsection h4 {
707
756
  div#main .body .subsection .types {
708
757
  font-family: "Consolas", "monospace";
709
758
  padding: 0 0.2em; }
710
- div#main .body .source {
759
+ div#main .body h3.source {
711
760
  cursor: pointer; }
712
- div#main .body .source.collapsed {
761
+ div#main .body h3.source.collapsed {
713
762
  border-bottom: 1px solid #d9d9d9;
714
763
  margin-bottom: 1.33em; }
715
764
  div#main .body .signature {
@@ -740,6 +789,8 @@ div#main .body .signature {
740
789
  padding: 0 0.2em; }
741
790
  div#main .body .signature .params .param {
742
791
  cursor: help; }
792
+ div#main .body .overload {
793
+ margin-bottom: 3em; }
743
794
 
744
795
  div#main .notification {
745
796
  display: table; }
@@ -131,7 +131,7 @@ Module('Header', function(my) {
131
131
  // @section Filling private variables
132
132
 
133
133
  my.apisearch = {
134
- data: JSDOC.data.apisearch
134
+ data: J.data.apisearch
135
135
  };
136
136
 
137
137
  my.settings.apisearch = {
@@ -156,7 +156,7 @@ Module('Header', function(my) {
156
156
  .hide()
157
157
  .data(data)
158
158
  .append($('<a>', {
159
- href: JSDOC.root + data.path,
159
+ href: J.root + data.path,
160
160
  html: data.name
161
161
  }));
162
162
 
@@ -291,20 +291,42 @@ Module('Body', function(my) {
291
291
  plugins: {
292
292
 
293
293
  source_code: function(my, reveal) {
294
-
294
+
295
+ // apply syntax highlighting
296
+ SyntaxHighlighter.config.tagName = "code";
297
+ SyntaxHighlighter.defaults.toolbar = false;
298
+
299
+ // replace all sources
295
300
  my.dom.find('h3.source').each(function(i, el) {
296
301
 
297
302
  var header = $(el).addClass('collapsed'),
298
- code = header.next('code').hide();
303
+ code = header.next('code');
304
+
305
+ SyntaxHighlighter.highlight(code.get(), {}, function(el) {
306
+
307
+ var code = $(el).hide();
299
308
 
300
- header.toggle(function(){
309
+ header.toggle(function(){
301
310
  header.removeClass('collapsed');
302
- code.slideDown();
303
- }, function() {
304
- code.slideUp(function() { header.addClass('collapsed'); });
311
+ code.slideDown();
312
+ }, function() {
313
+ code.slideUp(function() { header.addClass('collapsed'); });
314
+ });
315
+
305
316
  });
306
- });
307
- }
317
+ });
318
+
319
+
320
+ // replace all code-examples
321
+ my.dom.find('code.example').each(function(i, el) {
322
+ SyntaxHighlighter.highlight([el], {
323
+ gutter: false
324
+ }, function(el) {
325
+ $(el).find('.syntaxhighlighter').addClass('example');
326
+ });
327
+ });
328
+
329
+ }
308
330
  }
309
331
  });
310
332
 
@@ -315,4 +337,4 @@ $(function() {
315
337
  if(J.modules.hasOwnProperty(key))
316
338
  J.modules[key].init();
317
339
  }
318
- });
340
+ });