jsduck 4.0.1 → 4.1.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.
- data/Rakefile +14 -0
- data/esprima/esprima.js +210 -85
- data/jsduck.gemspec +3 -3
- data/lib/jsduck/accessors.rb +10 -8
- data/lib/jsduck/aggregator.rb +7 -7
- data/lib/jsduck/app.rb +24 -164
- data/lib/jsduck/app_data.rb +2 -4
- data/lib/jsduck/assets.rb +5 -2
- data/lib/jsduck/ast.rb +9 -4
- data/lib/jsduck/batch_formatter.rb +54 -0
- data/lib/jsduck/batch_parser.rb +106 -0
- data/lib/jsduck/categories.rb +5 -6
- data/lib/jsduck/class.rb +77 -239
- data/lib/jsduck/class_doc_expander.rb +0 -5
- data/lib/jsduck/class_formatter.rb +14 -10
- data/lib/jsduck/class_name.rb +23 -0
- data/lib/jsduck/class_writer.rb +9 -8
- data/lib/jsduck/doc_ast.rb +5 -6
- data/lib/jsduck/doc_formatter.rb +61 -272
- data/lib/jsduck/enum.rb +4 -4
- data/lib/jsduck/esprima.rb +10 -4
- data/lib/jsduck/examples.rb +5 -5
- data/lib/jsduck/export_writer.rb +62 -0
- data/lib/jsduck/exporter/app.rb +62 -0
- data/lib/jsduck/exporter/examples.rb +58 -0
- data/lib/jsduck/exporter/full.rb +60 -0
- data/lib/jsduck/file_categories.rb +7 -4
- data/lib/jsduck/function_ast.rb +99 -0
- data/lib/jsduck/grouped_asset.rb +27 -16
- data/lib/jsduck/guide_writer.rb +8 -7
- data/lib/jsduck/guides.rb +31 -29
- data/lib/jsduck/icons.rb +12 -1
- data/lib/jsduck/images.rb +3 -3
- data/lib/jsduck/importer.rb +7 -7
- data/lib/jsduck/index_html.rb +20 -6
- data/lib/jsduck/inherit_doc.rb +9 -12
- data/lib/jsduck/inline/example.rb +42 -0
- data/lib/jsduck/inline/img.rb +55 -0
- data/lib/jsduck/inline/link.rb +227 -0
- data/lib/jsduck/inline/video.rb +67 -0
- data/lib/jsduck/inline_examples.rb +7 -7
- data/lib/jsduck/js_parser.rb +5 -4
- data/lib/jsduck/lint.rb +14 -2
- data/lib/jsduck/logger.rb +5 -6
- data/lib/jsduck/members_index.rb +132 -0
- data/lib/jsduck/merger.rb +3 -9
- data/lib/jsduck/options.rb +39 -41
- data/lib/jsduck/override.rb +3 -7
- data/lib/jsduck/relations.rb +9 -9
- data/lib/jsduck/renderer.rb +3 -3
- data/lib/jsduck/return_values.rb +72 -0
- data/lib/jsduck/search_data.rb +16 -20
- data/lib/jsduck/shortener.rb +58 -0
- data/lib/jsduck/signature_renderer.rb +1 -2
- data/lib/jsduck/source/file.rb +98 -0
- data/lib/jsduck/source/file_parser.rb +72 -0
- data/lib/jsduck/source/writer.rb +89 -0
- data/lib/jsduck/tag/aside.rb +1 -1
- data/lib/jsduck/tag/since.rb +1 -1
- data/lib/jsduck/template_dir.rb +2 -2
- data/lib/jsduck/util/html.rb +27 -0
- data/lib/jsduck/util/io.rb +32 -0
- data/lib/jsduck/util/json.rb +60 -0
- data/lib/jsduck/util/null_object.rb +23 -0
- data/lib/jsduck/util/os.rb +14 -0
- data/lib/jsduck/util/parallel.rb +34 -0
- data/lib/jsduck/util/singleton.rb +35 -0
- data/lib/jsduck/util/stdout.rb +33 -0
- data/lib/jsduck/videos.rb +5 -5
- data/lib/jsduck/web_writer.rb +79 -0
- data/lib/jsduck/welcome.rb +6 -6
- data/spec/class_factory.rb +20 -0
- metadata +32 -20
- data/lib/jsduck/api_exporter.rb +0 -48
- data/lib/jsduck/app_exporter.rb +0 -60
- data/lib/jsduck/examples_exporter.rb +0 -56
- data/lib/jsduck/full_exporter.rb +0 -35
- data/lib/jsduck/html.rb +0 -25
- data/lib/jsduck/inline_img.rb +0 -53
- data/lib/jsduck/inline_video.rb +0 -58
- data/lib/jsduck/io.rb +0 -30
- data/lib/jsduck/json_duck.rb +0 -52
- data/lib/jsduck/lexer.rb +0 -251
- data/lib/jsduck/null_object.rb +0 -19
- data/lib/jsduck/os.rb +0 -11
- data/lib/jsduck/parallel_wrap.rb +0 -32
- data/lib/jsduck/source_file.rb +0 -97
- data/lib/jsduck/source_file_parser.rb +0 -70
- data/lib/jsduck/source_writer.rb +0 -87
- data/lib/jsduck/stats.rb +0 -103
- data/lib/jsduck/stdout.rb +0 -31
@@ -107,14 +107,9 @@ module JsDuck
|
|
107
107
|
results = []
|
108
108
|
|
109
109
|
if docset[:code]
|
110
|
-
|
111
110
|
(docset[:code][:members] || []).each do |m|
|
112
111
|
results << code_to_docset(m) unless @constructor_found && m[:name] == "constructor"
|
113
112
|
end
|
114
|
-
|
115
|
-
(docset[:code][:statics] || []).each do |m|
|
116
|
-
results << code_to_docset(m)
|
117
|
-
end
|
118
113
|
end
|
119
114
|
|
120
115
|
results
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'jsduck/type_parser'
|
2
2
|
require 'jsduck/logger'
|
3
3
|
require 'jsduck/meta_tag_registry'
|
4
|
+
require 'jsduck/shortener'
|
4
5
|
|
5
6
|
module JsDuck
|
6
7
|
|
@@ -25,21 +26,24 @@ module JsDuck
|
|
25
26
|
@formatter.class_context = cls[:name]
|
26
27
|
@formatter.doc_context = cls[:files][0]
|
27
28
|
cls[:doc] = @formatter.format(cls[:doc]) if cls[:doc]
|
28
|
-
|
29
|
-
|
30
|
-
# format all members (except hidden ones)
|
31
|
-
cls[group][type] = members.map {|m| m[:meta][:hide] ? m : format_member(m) }
|
32
|
-
end
|
33
|
-
end
|
29
|
+
# format all members (except hidden ones)
|
30
|
+
cls[:members] = cls[:members].map {|m| m[:meta][:hide] ? m : format_member(m) }
|
34
31
|
cls[:html_meta] = format_meta_data(cls)
|
35
32
|
cls
|
36
33
|
end
|
37
34
|
|
35
|
+
# Returns the images detected by doc-formatter
|
36
|
+
def images
|
37
|
+
@formatter.images
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
38
42
|
def format_member(m)
|
39
43
|
@formatter.doc_context = m[:files][0]
|
40
44
|
m[:doc] = @formatter.format(m[:doc]) if m[:doc]
|
41
|
-
if expandable?(m) ||
|
42
|
-
m[:shortDoc] =
|
45
|
+
if expandable?(m) || Shortener.too_long?(m[:doc])
|
46
|
+
m[:shortDoc] = Shortener.shorten(m[:doc])
|
43
47
|
end
|
44
48
|
|
45
49
|
# We don't validate and format CSS var and mixin type definitions
|
@@ -72,9 +76,9 @@ module JsDuck
|
|
72
76
|
else
|
73
77
|
context = @formatter.doc_context
|
74
78
|
if tp.error == :syntax
|
75
|
-
Logger.
|
79
|
+
Logger.warn(:type_syntax, "Incorrect type syntax #{type}", context[:filename], context[:linenr])
|
76
80
|
else
|
77
|
-
Logger.
|
81
|
+
Logger.warn(:type_name, "Unknown type #{type}", context[:filename], context[:linenr])
|
78
82
|
end
|
79
83
|
type
|
80
84
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module JsDuck
|
2
|
+
|
3
|
+
# Common routines for manipulating class names.
|
4
|
+
class ClassName
|
5
|
+
|
6
|
+
# Given a full class name extracts the "class"-part of the name.
|
7
|
+
#
|
8
|
+
# ClassName.short("My.package.Foo") --> "Foo"
|
9
|
+
#
|
10
|
+
# Because we try to emulate ext-doc, it's not as simple as just
|
11
|
+
# taking the last part. See class_spec.rb for details.
|
12
|
+
def self.short(name)
|
13
|
+
parts = name.split(/\./)
|
14
|
+
short = parts.pop
|
15
|
+
while parts.length > 1 && parts.last =~ /^[A-Z]/
|
16
|
+
short = parts.pop + "." + short
|
17
|
+
end
|
18
|
+
short
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/jsduck/class_writer.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require 'jsduck/
|
1
|
+
require 'jsduck/util/parallel'
|
2
2
|
require 'jsduck/logger'
|
3
|
-
require 'jsduck/
|
3
|
+
require 'jsduck/util/json'
|
4
|
+
require 'jsduck/util/stdout'
|
4
5
|
require 'fileutils'
|
5
6
|
|
6
7
|
module JsDuck
|
@@ -23,22 +24,22 @@ module JsDuck
|
|
23
24
|
private
|
24
25
|
|
25
26
|
def write_stdout
|
26
|
-
json =
|
27
|
-
Stdout.
|
27
|
+
json = Util::Parallel.map(@relations.classes) {|cls| @exporter.export(cls) }.compact
|
28
|
+
Util::Stdout.add(json)
|
28
29
|
end
|
29
30
|
|
30
31
|
def write_dir(dir, extension)
|
31
32
|
FileUtils.mkdir(dir)
|
32
|
-
|
33
|
+
Util::Parallel.each(@relations.classes) do |cls|
|
33
34
|
filename = dir + "/" + cls[:name] + extension
|
34
|
-
Logger.
|
35
|
+
Logger.log("Writing docs", filename)
|
35
36
|
json = @exporter.export(cls)
|
36
37
|
# skip file if exporter returned nil
|
37
38
|
if json
|
38
39
|
if extension == ".json"
|
39
|
-
|
40
|
+
Util::Json.write_json(filename, json)
|
40
41
|
elsif extension == ".js"
|
41
|
-
|
42
|
+
Util::Json.write_jsonp(filename, cls[:name].gsub(/\./, "_"), json)
|
42
43
|
else
|
43
44
|
throw "Unexpected file extension: #{extension}"
|
44
45
|
end
|
data/lib/jsduck/doc_ast.rb
CHANGED
@@ -58,14 +58,13 @@ module JsDuck
|
|
58
58
|
|
59
59
|
def create_method(docs)
|
60
60
|
doc_map = build_doc_map(docs)
|
61
|
-
name = detect_name(:method, doc_map)
|
62
61
|
return add_shared({
|
63
62
|
:tagname => :method,
|
64
|
-
:name =>
|
63
|
+
:name => detect_name(:method, doc_map),
|
65
64
|
:owner => detect_owner(doc_map),
|
66
65
|
:doc => detect_doc(docs),
|
67
66
|
:params => detect_params(doc_map),
|
68
|
-
:return => detect_return(doc_map
|
67
|
+
:return => detect_return(doc_map),
|
69
68
|
:throws => detect_throws(doc_map),
|
70
69
|
}, doc_map)
|
71
70
|
end
|
@@ -248,7 +247,7 @@ module JsDuck
|
|
248
247
|
parent[:properties] = [] unless parent[:properties]
|
249
248
|
parent[:properties] << it
|
250
249
|
else
|
251
|
-
Logger.
|
250
|
+
Logger.warn(:subproperty, "Ignoring subproperty #{$1}.#{$2}, no parent found with name '#{$1}'.", @filename, @linenr)
|
252
251
|
end
|
253
252
|
else
|
254
253
|
items << it
|
@@ -257,10 +256,10 @@ module JsDuck
|
|
257
256
|
items
|
258
257
|
end
|
259
258
|
|
260
|
-
def detect_return(doc_map
|
259
|
+
def detect_return(doc_map)
|
261
260
|
ret = extract(doc_map, :return) || {}
|
262
261
|
return {
|
263
|
-
:type => ret[:type] ||
|
262
|
+
:type => ret[:type] || "undefined",
|
264
263
|
:name => ret[:name] || "return",
|
265
264
|
:doc => ret[:doc] || "",
|
266
265
|
:properties => doc_map[:return] ? detect_subproperties(:return, doc_map[:return]) : []
|
data/lib/jsduck/doc_formatter.rb
CHANGED
@@ -1,71 +1,80 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
1
|
require 'rubygems'
|
3
2
|
require 'strscan'
|
4
3
|
require 'rdiscount'
|
5
|
-
require 'jsduck/
|
6
|
-
require 'jsduck/
|
7
|
-
require 'jsduck/
|
8
|
-
require 'jsduck/
|
4
|
+
require 'jsduck/inline/link'
|
5
|
+
require 'jsduck/inline/img'
|
6
|
+
require 'jsduck/inline/video'
|
7
|
+
require 'jsduck/inline/example'
|
9
8
|
|
10
9
|
module JsDuck
|
11
10
|
|
12
11
|
# Formats doc-comments
|
13
12
|
class DocFormatter
|
14
|
-
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
13
|
+
|
14
|
+
# Creates a formatter configured with options originating from
|
15
|
+
# command line. For the actual effect of the options see
|
16
|
+
# Inline::* classes.
|
17
|
+
def initialize(opts={})
|
18
|
+
@inline_link = Inline::Link.new(opts)
|
19
|
+
@inline_img = Inline::Img.new(opts)
|
20
|
+
@inline_video = Inline::Video.new(opts)
|
21
|
+
@inline_example = Inline::Example.new(opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets base path to prefix images from {@img} tags.
|
25
|
+
def img_path=(path)
|
26
|
+
@inline_img.base_path = path
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns list of all image paths gathered from {@img} tags.
|
30
|
+
def images
|
31
|
+
@inline_img.images
|
32
|
+
end
|
25
33
|
|
26
34
|
# Sets up instance to work in context of particular class, so
|
27
35
|
# that when {@link #blah} is encountered it knows that
|
28
36
|
# Context#blah is meant.
|
29
|
-
|
37
|
+
def class_context=(cls)
|
38
|
+
@inline_link.class_context = cls
|
39
|
+
end
|
30
40
|
|
31
41
|
# Sets up instance to work in context of particular doc object.
|
32
42
|
# Used for error reporting.
|
33
|
-
|
43
|
+
def doc_context=(doc)
|
44
|
+
@inline_video.doc_context = doc
|
45
|
+
@inline_link.doc_context = doc
|
46
|
+
end
|
34
47
|
|
35
|
-
#
|
36
|
-
|
48
|
+
# Returns the current documentation context
|
49
|
+
def doc_context
|
50
|
+
@inline_link.doc_context
|
51
|
+
end
|
37
52
|
|
38
53
|
# JsDuck::Relations for looking up class names.
|
39
54
|
#
|
40
55
|
# When auto-creating class links from CamelCased names found from
|
41
56
|
# text, we check the relations object to see if a class with that
|
42
57
|
# name actually exists.
|
43
|
-
|
44
|
-
|
45
|
-
def initialize(relations={}, opts={})
|
46
|
-
@class_context = ""
|
47
|
-
@doc_context = {}
|
48
|
-
@max_length = 120
|
49
|
-
@relations = relations
|
50
|
-
@images = []
|
51
|
-
|
52
|
-
@inline_img = InlineImg.new(opts)
|
53
|
-
@inline_video = InlineVideo.new(opts)
|
54
|
-
|
55
|
-
@link_tpl = opts[:link_tpl] || '<a href="%c%#%m">%a</a>'
|
56
|
-
@link_re = /\{@link\s+(\S*?)(?:\s+(.+?))?\}/m
|
57
|
-
|
58
|
-
@example_annotation_re = /<pre><code>\s*@example( +[^\n]*)?\s+/m
|
58
|
+
def relations=(relations)
|
59
|
+
@inline_link.relations = relations
|
59
60
|
end
|
60
61
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
# Formats doc-comment for placement into HTML.
|
63
|
+
# Renders it with Markdown-formatter and replaces @link-s.
|
64
|
+
def format(input)
|
65
|
+
# In ExtJS source "<pre>" is often at the end of paragraph, not
|
66
|
+
# on its own line. But in that case RDiscount doesn't recognize
|
67
|
+
# it as the beginning of <pre>-block and goes on parsing it as
|
68
|
+
# normal Markdown, which often causes nested <pre>-blocks.
|
69
|
+
#
|
70
|
+
# To prevent this, we always add extra newline before <pre>.
|
71
|
+
input.gsub!(/([^\n])<pre>/, "\\1\n<pre>")
|
65
72
|
|
66
|
-
|
67
|
-
|
68
|
-
|
73
|
+
# But we remove trailing newline after <pre> to prevent
|
74
|
+
# code-blocks beginning with empty line.
|
75
|
+
input.gsub!(/<pre>(<code>)?\n?/, "<pre>\\1")
|
76
|
+
|
77
|
+
replace(RDiscount.new(input).to_html)
|
69
78
|
end
|
70
79
|
|
71
80
|
# Replaces {@link} and {@img} tags, auto-generates links for
|
@@ -91,21 +100,17 @@ module JsDuck
|
|
91
100
|
open_a_tags = 0
|
92
101
|
|
93
102
|
while !s.eos? do
|
94
|
-
if
|
95
|
-
out +=
|
103
|
+
if substitute = @inline_link.replace(s)
|
104
|
+
out += substitute
|
96
105
|
elsif substitute = @inline_img.replace(s)
|
97
106
|
out += substitute
|
98
|
-
elsif substitute = @inline_video.replace(s
|
107
|
+
elsif substitute = @inline_video.replace(s)
|
99
108
|
out += substitute
|
100
109
|
elsif s.check(/[{]/)
|
101
110
|
# There might still be "{" that doesn't begin {@link} or {@img} - ignore it
|
102
111
|
out += s.scan(/[{]/)
|
103
|
-
elsif
|
104
|
-
|
105
|
-
# as CSS classes inside <pre> element.
|
106
|
-
s.scan(@example_annotation_re) =~ @example_annotation_re
|
107
|
-
css_classes = ($1 || "").strip
|
108
|
-
out += "<pre class='inline-example #{css_classes}'><code>"
|
112
|
+
elsif substitute = @inline_example.replace(s)
|
113
|
+
out += substitute
|
109
114
|
elsif s.check(/<a\b/)
|
110
115
|
# Increment number of open <a> tags.
|
111
116
|
open_a_tags += 1
|
@@ -121,232 +126,16 @@ module JsDuck
|
|
121
126
|
# Replace class names in the following text up to next "<" or "{"
|
122
127
|
# but only when we're not inside <a>...</a>
|
123
128
|
text = s.scan(/[^{<]+/)
|
124
|
-
out += open_a_tags > 0 ? text : create_magic_links(text)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
out
|
128
|
-
end
|
129
|
-
|
130
|
-
def replace_link_tag(input)
|
131
|
-
input.sub(@link_re) do
|
132
|
-
target = $1
|
133
|
-
text = $2
|
134
|
-
if target =~ /^(.*)#(static-)?(?:(cfg|property|method|event|css_var|css_mixin)-)?(.*)$/
|
135
|
-
cls = $1.empty? ? @class_context : $1
|
136
|
-
static = $2 ? true : nil
|
137
|
-
type = $3 ? $3.intern : nil
|
138
|
-
member = $4
|
139
|
-
else
|
140
|
-
cls = target
|
141
|
-
static = nil
|
142
|
-
type = false
|
143
|
-
member = false
|
144
|
-
end
|
145
|
-
|
146
|
-
# Construct link text
|
147
|
-
if text
|
148
|
-
text = text
|
149
|
-
elsif member
|
150
|
-
text = (cls == @class_context) ? member : (cls + "." + member)
|
151
|
-
else
|
152
|
-
text = cls
|
153
|
-
end
|
154
|
-
|
155
|
-
file = @doc_context[:filename]
|
156
|
-
line = @doc_context[:linenr]
|
157
|
-
if !@relations[cls]
|
158
|
-
Logger.instance.warn(:link, "#{input} links to non-existing class", file, line)
|
159
|
-
return text
|
160
|
-
elsif member
|
161
|
-
ms = get_members(cls, member, type, static)
|
162
|
-
if ms.length == 0
|
163
|
-
Logger.instance.warn(:link, "#{input} links to non-existing member", file, line)
|
164
|
-
return text
|
165
|
-
end
|
166
|
-
|
167
|
-
if ms.length > 1
|
168
|
-
# When multiple public members, see if there remains just
|
169
|
-
# one when we ignore the static members. If there's more,
|
170
|
-
# report ambiguity. If there's only static members, also
|
171
|
-
# report ambiguity.
|
172
|
-
instance_ms = ms.find_all {|m| !m[:meta][:static] }
|
173
|
-
if instance_ms.length > 1
|
174
|
-
alternatives = instance_ms.map {|m| m[:tagname].to_s }.join(", ")
|
175
|
-
Logger.instance.warn(:link_ambiguous, "#{input} is ambiguous: "+alternatives, file, line)
|
176
|
-
elsif instance_ms.length == 0
|
177
|
-
static_ms = ms.find_all {|m| m[:meta][:static] }
|
178
|
-
alternatives = static_ms.map {|m| "static " + m[:tagname].to_s }.join(", ")
|
179
|
-
Logger.instance.warn(:link_ambiguous, "#{input} is ambiguous: "+alternatives, file, line)
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
return link(cls, member, text, type, static)
|
184
|
-
else
|
185
|
-
return link(cls, false, text)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
# Looks input text for patterns like:
|
191
|
-
#
|
192
|
-
# My.ClassName
|
193
|
-
# MyClass#method
|
194
|
-
# #someProperty
|
195
|
-
#
|
196
|
-
# and converts them to links, as if they were surrounded with
|
197
|
-
# {@link} tag. One notable exception is that Foo is not created to
|
198
|
-
# link, even when Foo class exists, but Foo.Bar is. This is to
|
199
|
-
# avoid turning normal words into links. For example:
|
200
|
-
#
|
201
|
-
# Math involves a lot of numbers. Ext JS is a JavaScript framework.
|
202
|
-
#
|
203
|
-
# In these sentences we don't want to link "Math" and "Ext" to the
|
204
|
-
# corresponding JS classes. And that's why we auto-link only
|
205
|
-
# class names containing a dot "."
|
206
|
-
#
|
207
|
-
def create_magic_links(input)
|
208
|
-
cls_re = "([A-Z][A-Za-z0-9.]*[A-Za-z0-9])"
|
209
|
-
member_re = "(?:#([A-Za-z0-9]+))"
|
210
|
-
|
211
|
-
input.gsub(/\b#{cls_re}#{member_re}?\b|#{member_re}\b/m) do
|
212
|
-
replace_magic_link($1, $2 || $3)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def replace_magic_link(cls, member)
|
217
|
-
if cls && member
|
218
|
-
if @relations[cls] && get_matching_member(cls, member)
|
219
|
-
return link(cls, member, cls+"."+member)
|
220
|
-
else
|
221
|
-
warn_magic_link("#{cls}##{member} links to non-existing " + (@relations[cls] ? "member" : "class"))
|
222
|
-
end
|
223
|
-
elsif cls && cls =~ /\./
|
224
|
-
if @relations[cls]
|
225
|
-
return link(cls, nil, cls)
|
226
|
-
else
|
227
|
-
cls2, member2 = split_to_cls_and_member(cls)
|
228
|
-
if @relations[cls2] && get_matching_member(cls2, member2)
|
229
|
-
return link(cls2, member2, cls2+"."+member2)
|
230
|
-
elsif cls =~ /\.(js|css|html|php)\Z/
|
231
|
-
# Ignore common filenames
|
232
|
-
else
|
233
|
-
warn_magic_link("#{cls} links to non-existing class")
|
234
|
-
end
|
235
|
-
end
|
236
|
-
elsif !cls && member
|
237
|
-
if get_matching_member(@class_context, member)
|
238
|
-
return link(@class_context, member, member)
|
239
|
-
elsif member =~ /\A([A-F0-9]{3}|[A-F0-9]{6})\Z/i || member =~ /\A[0-9]/
|
240
|
-
# Ignore HEX color codes and
|
241
|
-
# member names beginning with number
|
242
|
-
else
|
243
|
-
warn_magic_link("##{member} links to non-existing member")
|
129
|
+
out += open_a_tags > 0 ? text : @inline_link.create_magic_links(text)
|
244
130
|
end
|
245
131
|
end
|
246
132
|
|
247
|
-
|
248
|
-
end
|
249
|
-
|
250
|
-
def split_to_cls_and_member(str)
|
251
|
-
parts = str.split(/\./)
|
252
|
-
return [parts.slice(0, parts.length-1).join("."), parts.last]
|
253
|
-
end
|
254
|
-
|
255
|
-
def warn_magic_link(msg)
|
256
|
-
Logger.instance.warn(:link_auto, msg, @doc_context[:filename], @doc_context[:linenr])
|
133
|
+
out
|
257
134
|
end
|
258
135
|
|
259
|
-
#
|
136
|
+
# Creates a link based on the link template.
|
260
137
|
def link(cls, member, anchor_text, type=nil, static=nil)
|
261
|
-
|
262
|
-
cls = @relations[cls].full_name
|
263
|
-
# prepend type name to member name
|
264
|
-
member = member && get_matching_member(cls, member, type, static)
|
265
|
-
|
266
|
-
@link_tpl.gsub(/(%[\w#-])/) do
|
267
|
-
case $1
|
268
|
-
when '%c'
|
269
|
-
cls
|
270
|
-
when '%m'
|
271
|
-
member ? member[:id] : ""
|
272
|
-
when '%#'
|
273
|
-
member ? "#" : ""
|
274
|
-
when '%-'
|
275
|
-
member ? "-" : ""
|
276
|
-
when '%a'
|
277
|
-
HTML.escape(anchor_text||"")
|
278
|
-
else
|
279
|
-
$1
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
def get_matching_member(cls, member, type=nil, static=nil)
|
285
|
-
ms = get_members(cls, member, type, static).find_all {|m| !m[:private] }
|
286
|
-
if ms.length > 1
|
287
|
-
instance_ms = ms.find_all {|m| !m[:meta][:static] }
|
288
|
-
instance_ms.length > 0 ? instance_ms[0] : ms.find_all {|m| m[:meta][:static] }[0]
|
289
|
-
else
|
290
|
-
ms[0]
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
def get_members(cls, member, type=nil, static=nil)
|
295
|
-
@relations[cls] ? @relations[cls].get_members(member, type, static) : []
|
296
|
-
end
|
297
|
-
|
298
|
-
# Formats doc-comment for placement into HTML.
|
299
|
-
# Renders it with Markdown-formatter and replaces @link-s.
|
300
|
-
def format(input)
|
301
|
-
# In ExtJS source "<pre>" is often at the end of paragraph, not
|
302
|
-
# on its own line. But in that case RDiscount doesn't recognize
|
303
|
-
# it as the beginning of <pre>-block and goes on parsing it as
|
304
|
-
# normal Markdown, which often causes nested <pre>-blocks.
|
305
|
-
#
|
306
|
-
# To prevent this, we always add extra newline before <pre>.
|
307
|
-
input.gsub!(/([^\n])<pre>/, "\\1\n<pre>")
|
308
|
-
|
309
|
-
# But we remove trailing newline after <pre> to prevent
|
310
|
-
# code-blocks beginning with empty line.
|
311
|
-
input.gsub!(/<pre>(<code>)?\n?/, "<pre>\\1")
|
312
|
-
|
313
|
-
replace(RDiscount.new(input).to_html)
|
314
|
-
end
|
315
|
-
|
316
|
-
# Shortens text
|
317
|
-
#
|
318
|
-
# 116 chars is also where ext-doc makes its cut, but unlike
|
319
|
-
# ext-doc we only make the cut when there's more than 120 chars.
|
320
|
-
#
|
321
|
-
# This way we don't get stupid expansions like:
|
322
|
-
#
|
323
|
-
# Blah blah blah some text...
|
324
|
-
#
|
325
|
-
# expanding to:
|
326
|
-
#
|
327
|
-
# Blah blah blah some text.
|
328
|
-
#
|
329
|
-
def shorten(input)
|
330
|
-
sent = first_sentence(HTML.strip_tags(input).strip)
|
331
|
-
# Use u-modifier to correctly count multi-byte characters
|
332
|
-
chars = sent.scan(/./mu)
|
333
|
-
if chars.length > @max_length
|
334
|
-
chars[0..(@max_length-4)].join + "..."
|
335
|
-
else
|
336
|
-
sent + " ..."
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def first_sentence(str)
|
341
|
-
str.sub(/\A(.+?(\.|。))\s.*\Z/mu, "\\1")
|
342
|
-
end
|
343
|
-
|
344
|
-
# Returns true when input should get shortened.
|
345
|
-
def too_long?(input)
|
346
|
-
stripped = HTML.strip_tags(input).strip
|
347
|
-
# for sentence v/s full - compare byte length
|
348
|
-
# for full v/s max - compare char length
|
349
|
-
first_sentence(stripped).length < stripped.length || stripped.scan(/./mu).length > @max_length
|
138
|
+
@inline_link.link(cls, member, anchor_text, type, static)
|
350
139
|
end
|
351
140
|
|
352
141
|
end
|