docjs 0.1.2 → 0.1.3

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