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