yard-sketchup 1.1.4 → 1.1.5
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.
- checksums.yaml +4 -4
- data/Gemfile +15 -15
- data/lib/yard-sketchup.rb +36 -36
- data/lib/yard-sketchup/patches/c_base_handler.rb +52 -52
- data/lib/yard-sketchup/stubs/autoload.rb +134 -134
- data/lib/yard-sketchup/templates/changelog/fulldoc/text/setup.rb +44 -44
- data/lib/yard-sketchup/templates/coverage/fulldoc/text/setup.rb +41 -41
- data/lib/yard-sketchup/templates/default/fulldoc/html/css/rubyapi.css +10 -10
- data/lib/yard-sketchup/templates/default/fulldoc/html/css/sketchup.css +120 -120
- data/lib/yard-sketchup/templates/default/fulldoc/html/full_list_object_types.erb +1 -1
- data/lib/yard-sketchup/templates/default/fulldoc/html/js/sketchup.js +37 -37
- data/lib/yard-sketchup/templates/default/fulldoc/html/setup.rb +66 -66
- data/lib/yard-sketchup/templates/default/layout/html/embed_meta.erb +78 -78
- data/lib/yard-sketchup/templates/default/layout/html/footer.erb +4 -4
- data/lib/yard-sketchup/templates/default/layout/html/headers.erb +15 -15
- data/lib/yard-sketchup/templates/default/layout/html/layout.erb +55 -55
- data/lib/yard-sketchup/templates/default/layout/html/navbar.erb +36 -36
- data/lib/yard-sketchup/templates/default/layout/html/setup.rb +19 -19
- data/lib/yard-sketchup/templates/default/method_details/html/method_signature.erb +25 -25
- data/lib/yard-sketchup/templates/default/method_details/setup.rb +11 -11
- data/lib/yard-sketchup/templates/default/module/html/box_info.erb +37 -37
- data/lib/yard-sketchup/templates/default/module/html/constant_summary.erb +17 -17
- data/lib/yard-sketchup/templates/default/module/html/method_summary.erb +18 -18
- data/lib/yard-sketchup/templates/stubs/fulldoc/text/setup.rb +361 -357
- data/lib/yard-sketchup/templates/versions/fulldoc/text/setup.rb +25 -25
- data/lib/yard-sketchup/version.rb +3 -3
- data/yard-sketchup.gemspec +25 -25
- metadata +2 -2
@@ -1,19 +1,19 @@
|
|
1
|
-
# Consider if this should simply replace the Classes list.
|
2
|
-
|
3
|
-
=begin
|
4
|
-
TODO(thomthom): Temporarily disabled until we have time to fully implement this.
|
5
|
-
def menu_lists
|
6
|
-
# Load the existing menus
|
7
|
-
super + [ { :type => 'object_types', :title => 'Object Reference', :search_title => 'Object Reference List' } ]
|
8
|
-
end
|
9
|
-
=end
|
10
|
-
|
11
|
-
def javascripts
|
12
|
-
# Using the latest 1.x jQuery from CDN broke the YARD js. For now we must
|
13
|
-
# continue to use the version shipping with the YARD template we use as base.
|
14
|
-
%w(js/jquery.js js/app.js)
|
15
|
-
end
|
16
|
-
|
17
|
-
def stylesheets
|
18
|
-
%w(css/style.css css/sketchup.css css/rubyapi.css)
|
19
|
-
end
|
1
|
+
# Consider if this should simply replace the Classes list.
|
2
|
+
|
3
|
+
=begin
|
4
|
+
TODO(thomthom): Temporarily disabled until we have time to fully implement this.
|
5
|
+
def menu_lists
|
6
|
+
# Load the existing menus
|
7
|
+
super + [ { :type => 'object_types', :title => 'Object Reference', :search_title => 'Object Reference List' } ]
|
8
|
+
end
|
9
|
+
=end
|
10
|
+
|
11
|
+
def javascripts
|
12
|
+
# Using the latest 1.x jQuery from CDN broke the YARD js. For now we must
|
13
|
+
# continue to use the version shipping with the YARD template we use as base.
|
14
|
+
%w(js/jquery.js js/app.js)
|
15
|
+
end
|
16
|
+
|
17
|
+
def stylesheets
|
18
|
+
%w(css/style.css css/sketchup.css css/rubyapi.css)
|
19
|
+
end
|
@@ -1,26 +1,26 @@
|
|
1
|
-
<h3 class="signature <%= 'first' if @index == 0 %>" id="<%= anchor_for(object) %>">
|
2
|
-
<a style="float: right;" href="#header" title="Return to Top">↑</a>
|
3
|
-
<% if object.tags(:overload).size == 1 %>
|
4
|
-
<%= signature(object.tag(:overload), false) %>
|
5
|
-
<% elsif object.tags(:overload).size > 1 %>
|
6
|
-
<% object.tags(:overload).each do |overload| %>
|
7
|
-
<span class="overload"><%= signature(overload, false) %></span>
|
8
|
-
<% end %>
|
9
|
-
<% else %>
|
10
|
-
<%= signature(object, false) %>
|
11
|
-
<% end %>
|
12
|
-
|
13
|
-
<% if object.aliases.size > 0 %>
|
14
|
-
<span class="aliases">Also known as:
|
15
|
-
<span class="names"><%= object.aliases.map {|o|
|
16
|
-
"<span id='#{anchor_for(o)}'>" + h(o.name.to_s) + "</span>" }.join(", ") %></span>
|
17
|
-
</span>
|
18
|
-
<% end %>
|
19
|
-
|
20
|
-
<% if owner != object.namespace %>
|
21
|
-
<span class="not_defined_here">
|
22
|
-
Originally defined in <%= object.namespace.type %>
|
23
|
-
<%= linkify object, owner.relative_path(object.namespace) %>
|
24
|
-
</span>
|
25
|
-
<% end %>
|
1
|
+
<h3 class="signature <%= 'first' if @index == 0 %>" id="<%= anchor_for(object) %>">
|
2
|
+
<a style="float: right;" href="#header" title="Return to Top">↑</a>
|
3
|
+
<% if object.tags(:overload).size == 1 %>
|
4
|
+
<%= signature(object.tag(:overload), false) %>
|
5
|
+
<% elsif object.tags(:overload).size > 1 %>
|
6
|
+
<% object.tags(:overload).each do |overload| %>
|
7
|
+
<span class="overload"><%= signature(overload, false) %></span>
|
8
|
+
<% end %>
|
9
|
+
<% else %>
|
10
|
+
<%= signature(object, false) %>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if object.aliases.size > 0 %>
|
14
|
+
<span class="aliases">Also known as:
|
15
|
+
<span class="names"><%= object.aliases.map {|o|
|
16
|
+
"<span id='#{anchor_for(o)}'>" + h(o.name.to_s) + "</span>" }.join(", ") %></span>
|
17
|
+
</span>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if owner != object.namespace %>
|
21
|
+
<span class="not_defined_here">
|
22
|
+
Originally defined in <%= object.namespace.type %>
|
23
|
+
<%= linkify object, owner.relative_path(object.namespace) %>
|
24
|
+
</span>
|
25
|
+
<% end %>
|
26
26
|
</h3>
|
@@ -1,12 +1,12 @@
|
|
1
|
-
# We don't want to expose our source code in the API docs.
|
2
|
-
|
3
|
-
# https://groups.google.com/forum/#!topic/yardoc/-HH48h-aifs
|
4
|
-
def source
|
5
|
-
return
|
6
|
-
end
|
7
|
-
|
8
|
-
# http://stackoverflow.com/a/10345917/486990
|
9
|
-
#def init
|
10
|
-
# super
|
11
|
-
# sections.first.delete(:source)
|
1
|
+
# We don't want to expose our source code in the API docs.
|
2
|
+
|
3
|
+
# https://groups.google.com/forum/#!topic/yardoc/-HH48h-aifs
|
4
|
+
def source
|
5
|
+
return
|
6
|
+
end
|
7
|
+
|
8
|
+
# http://stackoverflow.com/a/10345917/486990
|
9
|
+
#def init
|
10
|
+
# super
|
11
|
+
# sections.first.delete(:source)
|
12
12
|
#end
|
@@ -1,37 +1,37 @@
|
|
1
|
-
<div class="box_info">
|
2
|
-
<% if CodeObjects::ClassObject === object && object.superclass %>
|
3
|
-
<dl>
|
4
|
-
<dt>Inherits:</dt>
|
5
|
-
<dd>
|
6
|
-
<span class="inheritName"><%= linkify object.superclass %></span>
|
7
|
-
<% if object.superclass.name != :BasicObject %>
|
8
|
-
<ul class="fullTree">
|
9
|
-
<li><%= linkify P(:Object) %></li>
|
10
|
-
<% object.inheritance_tree.reverse.each_with_index do |obj, i| %>
|
11
|
-
<li class="next"><%= obj == object ? obj.path : linkify(obj) %></li>
|
12
|
-
<% end %>
|
13
|
-
</ul>
|
14
|
-
<a href="#" class="inheritanceTree">show all</a>
|
15
|
-
<% end %>
|
16
|
-
</dd>
|
17
|
-
</dl>
|
18
|
-
<% end %>
|
19
|
-
|
20
|
-
<% [[:class, "Extended by"], [:instance, "Includes"]].each do |scope, name| %>
|
21
|
-
<% if (mix = run_verifier(object.mixins(scope))).size > 0 %>
|
22
|
-
<dl>
|
23
|
-
<dt><%= name %>:</dt>
|
24
|
-
<dd><%= mix.sort_by {|o| o.path }.map {|o| linkify(o) }.join(", ") %></dd>
|
25
|
-
</dl>
|
26
|
-
<% end %>
|
27
|
-
<% end %>
|
28
|
-
|
29
|
-
<% if (mixed_into = mixed_into(object)).size > 0 %>
|
30
|
-
<dl>
|
31
|
-
<dt>Included in:</dt>
|
32
|
-
<dd><%= mixed_into.sort_by {|o| o.path }.map {|o| linkify(o) }.join(", ") %></dd>
|
33
|
-
</dl>
|
34
|
-
<% end %>
|
35
|
-
|
36
|
-
</div>
|
37
|
-
|
1
|
+
<div class="box_info">
|
2
|
+
<% if CodeObjects::ClassObject === object && object.superclass %>
|
3
|
+
<dl>
|
4
|
+
<dt>Inherits:</dt>
|
5
|
+
<dd>
|
6
|
+
<span class="inheritName"><%= linkify object.superclass %></span>
|
7
|
+
<% if object.superclass.name != :BasicObject %>
|
8
|
+
<ul class="fullTree">
|
9
|
+
<li><%= linkify P(:Object) %></li>
|
10
|
+
<% object.inheritance_tree.reverse.each_with_index do |obj, i| %>
|
11
|
+
<li class="next"><%= obj == object ? obj.path : linkify(obj) %></li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
<a href="#" class="inheritanceTree">show all</a>
|
15
|
+
<% end %>
|
16
|
+
</dd>
|
17
|
+
</dl>
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% [[:class, "Extended by"], [:instance, "Includes"]].each do |scope, name| %>
|
21
|
+
<% if (mix = run_verifier(object.mixins(scope))).size > 0 %>
|
22
|
+
<dl>
|
23
|
+
<dt><%= name %>:</dt>
|
24
|
+
<dd><%= mix.sort_by {|o| o.path }.map {|o| linkify(o) }.join(", ") %></dd>
|
25
|
+
</dl>
|
26
|
+
<% end %>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<% if (mixed_into = mixed_into(object)).size > 0 %>
|
30
|
+
<dl>
|
31
|
+
<dt>Included in:</dt>
|
32
|
+
<dd><%= mixed_into.sort_by {|o| o.path }.map {|o| linkify(o) }.join(", ") %></dd>
|
33
|
+
</dl>
|
34
|
+
<% end %>
|
35
|
+
|
36
|
+
</div>
|
37
|
+
|
@@ -1,17 +1,17 @@
|
|
1
|
-
<% if constant_listing.size > 0 || object.constants.size > 0 %>
|
2
|
-
<h2>
|
3
|
-
Constant Summary
|
4
|
-
<a
|
5
|
-
id="constant_summary"
|
6
|
-
href="#constant_summary"
|
7
|
-
title="Permalink">#</a>
|
8
|
-
</h2>
|
9
|
-
<% if constant_listing.size > 0 %>
|
10
|
-
<dl class="constants">
|
11
|
-
<% constant_listing.each do |cnst| %>
|
12
|
-
<dt id="<%= anchor_for(cnst) %>" class="<%= cnst.has_tag?(:deprecated) ? 'deprecated' : '' %>"><%= cnst.namespace.to_s.empty? ? '' : "#{cnst.namespace}::" %><%= cnst.name %>
|
13
|
-
</dt>
|
14
|
-
<% end %>
|
15
|
-
</dl>
|
16
|
-
<% end %>
|
17
|
-
<% end %>
|
1
|
+
<% if constant_listing.size > 0 || object.constants.size > 0 %>
|
2
|
+
<h2>
|
3
|
+
Constant Summary
|
4
|
+
<a
|
5
|
+
id="constant_summary"
|
6
|
+
href="#constant_summary"
|
7
|
+
title="Permalink">#</a>
|
8
|
+
</h2>
|
9
|
+
<% if constant_listing.size > 0 %>
|
10
|
+
<dl class="constants">
|
11
|
+
<% constant_listing.each do |cnst| %>
|
12
|
+
<dt id="<%= anchor_for(cnst) %>" class="<%= cnst.has_tag?(:deprecated) ? 'deprecated' : '' %>"><%= cnst.namespace.to_s.empty? ? '' : "#{cnst.namespace}::" %><%= cnst.name %>
|
13
|
+
</dt>
|
14
|
+
<% end %>
|
15
|
+
</dl>
|
16
|
+
<% end %>
|
17
|
+
<% end %>
|
@@ -1,18 +1,18 @@
|
|
1
|
-
<% if method_listing.size > 0 %>
|
2
|
-
<% groups(method_listing) do |list, name| %>
|
3
|
-
<h2>
|
4
|
-
<%= name %>
|
5
|
-
<a
|
6
|
-
id="<%= name.gsub(' ', '_').downcase %>"
|
7
|
-
href="#<%= name.gsub(' ', '_').downcase %>"
|
8
|
-
title="Permalink">#</a>
|
9
|
-
<small><a href="#" class="summary_toggle">collapse</a></small>
|
10
|
-
</h2>
|
11
|
-
|
12
|
-
<ul class="summary">
|
13
|
-
<% list.each do |meth| %>
|
14
|
-
<%= yieldall :item => meth %>
|
15
|
-
<% end %>
|
16
|
-
</ul>
|
17
|
-
<% end %>
|
18
|
-
<% end %>
|
1
|
+
<% if method_listing.size > 0 %>
|
2
|
+
<% groups(method_listing) do |list, name| %>
|
3
|
+
<h2>
|
4
|
+
<%= name %>
|
5
|
+
<a
|
6
|
+
id="<%= name.gsub(' ', '_').downcase %>"
|
7
|
+
href="#<%= name.gsub(' ', '_').downcase %>"
|
8
|
+
title="Permalink">#</a>
|
9
|
+
<small><a href="#" class="summary_toggle">collapse</a></small>
|
10
|
+
</h2>
|
11
|
+
|
12
|
+
<ul class="summary">
|
13
|
+
<% list.each do |meth| %>
|
14
|
+
<%= yieldall :item => meth %>
|
15
|
+
<% end %>
|
16
|
+
</ul>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
@@ -1,357 +1,361 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'pathname'
|
3
|
-
require 'set'
|
4
|
-
require 'stringio'
|
5
|
-
|
6
|
-
include Helpers::ModuleHelper
|
7
|
-
|
8
|
-
def init
|
9
|
-
generate_stubs
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
# NOTE: Remember to run objects outputted through `run_verifier` first in order
|
14
|
-
# to filter out items that should be excluded by command line arguments.
|
15
|
-
|
16
|
-
def namespace_objects
|
17
|
-
run_verifier(Registry.all(:class, :module))
|
18
|
-
end
|
19
|
-
|
20
|
-
def generate_stubs
|
21
|
-
puts "Generating stubs..."
|
22
|
-
generate_module_stubs(Registry.root)
|
23
|
-
namespace_objects.each do |object|
|
24
|
-
generate_module_stubs(object)
|
25
|
-
end
|
26
|
-
generate_autoloader(namespace_objects)
|
27
|
-
end
|
28
|
-
|
29
|
-
def generate_autoloader(namespace_objects)
|
30
|
-
generator = SketchUpYARD::Stubs::AutoLoadGenerator.new
|
31
|
-
autoload_file = File.join(stubs_gem_path, 'sketchup.rb')
|
32
|
-
File.open(autoload_file, 'w') do |file|
|
33
|
-
generator.generate(namespace_objects, file)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def print_section(io, title, content)
|
38
|
-
return if content.strip.empty?
|
39
|
-
io.puts
|
40
|
-
io.puts " # #{title}"
|
41
|
-
io.puts
|
42
|
-
io.puts content
|
43
|
-
end
|
44
|
-
|
45
|
-
def generate_module_stubs(object)
|
46
|
-
filename = stub_filename(object)
|
47
|
-
ensure_exist(File.dirname(filename))
|
48
|
-
StubFile.open(filename, 'w') { |file|
|
49
|
-
file.puts file_header(object)
|
50
|
-
file.puts
|
51
|
-
file.puts namespace_definition(object)
|
52
|
-
print_section(file, 'Extends', generate_mixins(object, :class))
|
53
|
-
print_section(file, 'Includes', generate_mixins(object, :instance))
|
54
|
-
print_section(file, 'Constants', generate_constants_grouped(object))
|
55
|
-
print_section(file, 'Class Methods', generate_class_methods(object))
|
56
|
-
print_section(file, 'Instance Methods', generate_instance_methods(object))
|
57
|
-
file.puts
|
58
|
-
file.puts file_footer(object)
|
59
|
-
}
|
60
|
-
#trim_trailing_white_space(filename)
|
61
|
-
end
|
62
|
-
|
63
|
-
def file_header(object)
|
64
|
-
header = StringIO.new
|
65
|
-
header.puts "# Copyright:: Copyright #{Time.now.year} Trimble Inc."
|
66
|
-
header.puts "# License:: The MIT License (MIT)"
|
67
|
-
#header.puts "# Generated:: #{Time.now.strftime('%F %R')}"
|
68
|
-
header.string
|
69
|
-
end
|
70
|
-
|
71
|
-
def file_footer(object)
|
72
|
-
return if object.root?
|
73
|
-
footer = StringIO.new
|
74
|
-
footer.puts 'end'
|
75
|
-
footer.string
|
76
|
-
end
|
77
|
-
|
78
|
-
def namespace_definition(object)
|
79
|
-
return if object.root?
|
80
|
-
definition = "#{object.type} #{object.path}"
|
81
|
-
if object.type == :class && object.superclass.name != :Object
|
82
|
-
definition << " < #{object.superclass.path}"
|
83
|
-
end
|
84
|
-
output = StringIO.new
|
85
|
-
output.puts generate_docstring(object)
|
86
|
-
output.puts definition
|
87
|
-
output.string
|
88
|
-
end
|
89
|
-
|
90
|
-
def output_path
|
91
|
-
options.serializer.options[:basepath] || File.join(Dir.pwd, 'stubs')
|
92
|
-
end
|
93
|
-
|
94
|
-
def stubs_root_path
|
95
|
-
ensure_exist(output_path)
|
96
|
-
end
|
97
|
-
|
98
|
-
def stubs_lib_path
|
99
|
-
ensure_exist(File.join(stubs_root_path, 'lib'))
|
100
|
-
end
|
101
|
-
|
102
|
-
def stubs_gem_path
|
103
|
-
ensure_exist(File.join(stubs_lib_path, 'sketchup-api-stubs'))
|
104
|
-
end
|
105
|
-
|
106
|
-
def stubs_path
|
107
|
-
ensure_exist(File.join(stubs_gem_path, 'stubs'))
|
108
|
-
end
|
109
|
-
|
110
|
-
def stub_filename(object)
|
111
|
-
basename = object.path.gsub('::', '/')
|
112
|
-
basename = '_top_level' if basename.empty?
|
113
|
-
File.join(stubs_path, "#{basename}.rb")
|
114
|
-
end
|
115
|
-
|
116
|
-
# A stable sort_by method.
|
117
|
-
#
|
118
|
-
# @param [Enumerable]
|
119
|
-
# @return [Array]
|
120
|
-
def stable_sort_by(list)
|
121
|
-
list.each_with_index.sort_by { |item, i| [yield(item), i] }.map(&:first)
|
122
|
-
end
|
123
|
-
|
124
|
-
CAMELCASE_CONSTANT = /^([A-Z]+[a-z]+)/
|
125
|
-
|
126
|
-
def group_constant(constant)
|
127
|
-
constant_name = constant.name.to_s
|
128
|
-
MANUAL_CONSTANT_GROUPS.each { |rule|
|
129
|
-
if rule[:constants]
|
130
|
-
return rule[:group] if rule[:constants].include?(constant_name)
|
131
|
-
else
|
132
|
-
return rule[:group] if rule[:regex].match(constant_name)
|
133
|
-
end
|
134
|
-
}
|
135
|
-
if constant_name.include?('_')
|
136
|
-
constant_name.split('_').first
|
137
|
-
else
|
138
|
-
constant_name[CAMELCASE_CONSTANT] || constant_name
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# Sorts and groups constants for easier reading.
|
143
|
-
def generate_constants_grouped(object)
|
144
|
-
constants = run_verifier(object.constants(object_options))
|
145
|
-
# The constants are not sorted before chunking. This is because `chunk` groups
|
146
|
-
# consecutive items - and we want to chunk them based their relationship
|
147
|
-
# with each other. This ensure that constants that doesn't follow the normal
|
148
|
-
# pattern of PREFIX_SOME_NAME will still be grouped next to each other.
|
149
|
-
groups = constants.chunk { |constant|
|
150
|
-
group_constant(constant)
|
151
|
-
}
|
152
|
-
grouped_output = groups.map { |group, group_constants|
|
153
|
-
output = StringIO.new
|
154
|
-
# Each group itself is sorted in order to more easily scan the list.
|
155
|
-
sorted = stable_sort_by(group_constants, &:name)
|
156
|
-
sorted.each { |constant|
|
157
|
-
output.puts " #{constant.name} = nil # Stub value."
|
158
|
-
}
|
159
|
-
output.string
|
160
|
-
}
|
161
|
-
# Finally each group is also sorted, again to ease scanning for a particular
|
162
|
-
# name. We simply use the first character of each group.
|
163
|
-
stable_sort_by(grouped_output) { |item| item.lstrip[0] }.join("\n")
|
164
|
-
end
|
165
|
-
|
166
|
-
# Sort constants without grouping.
|
167
|
-
def generate_constants(object)
|
168
|
-
output = StringIO.new
|
169
|
-
constants = run_verifier(object.constants(object_options))
|
170
|
-
constants = stable_sort_by(constants, &:name)
|
171
|
-
constants.each { |constant|
|
172
|
-
output.puts " #{constant.name} = nil # Stub value."
|
173
|
-
}
|
174
|
-
output.string
|
175
|
-
end
|
176
|
-
|
177
|
-
def generate_mixins(object, scope)
|
178
|
-
output = StringIO.new
|
179
|
-
mixin_type = (scope == :class) ? 'extend' : 'include'
|
180
|
-
mixins = run_verifier(object.mixins(scope))
|
181
|
-
mixins = stable_sort_by(mixins, &:path)
|
182
|
-
mixins.each { |mixin|
|
183
|
-
output.puts " #{mixin_type} #{mixin.path}"
|
184
|
-
}
|
185
|
-
output.string
|
186
|
-
end
|
187
|
-
|
188
|
-
def generate_class_methods(object)
|
189
|
-
generate_methods(object, :class, 'self.')
|
190
|
-
end
|
191
|
-
|
192
|
-
def generate_instance_methods(object)
|
193
|
-
generate_methods(object, :instance)
|
194
|
-
end
|
195
|
-
|
196
|
-
def generate_methods(object, scope, prefix = '')
|
197
|
-
methods = sort_methods(object, scope)
|
198
|
-
signatures = methods.map { |method|
|
199
|
-
output = StringIO.new
|
200
|
-
# Cannot use `methods.signature` here as it would return the C/C++ function
|
201
|
-
# signature. Must generate one from the YARD data.
|
202
|
-
signature = generate_method_signature(method)
|
203
|
-
# NOTE: We must call `generate_docstring` after `generate_method_signature`
|
204
|
-
# because `generate_method_signature` will also clean up docstrings with
|
205
|
-
# a single @overload tag.
|
206
|
-
output.puts generate_docstring(method, 1)
|
207
|
-
output.puts " def #{prefix}#{signature}"
|
208
|
-
output.puts " end"
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
signature
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
#
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
require 'set'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
include Helpers::ModuleHelper
|
7
|
+
|
8
|
+
def init
|
9
|
+
generate_stubs
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
# NOTE: Remember to run objects outputted through `run_verifier` first in order
|
14
|
+
# to filter out items that should be excluded by command line arguments.
|
15
|
+
|
16
|
+
def namespace_objects
|
17
|
+
run_verifier(Registry.all(:class, :module))
|
18
|
+
end
|
19
|
+
|
20
|
+
def generate_stubs
|
21
|
+
puts "Generating stubs..."
|
22
|
+
generate_module_stubs(Registry.root)
|
23
|
+
namespace_objects.each do |object|
|
24
|
+
generate_module_stubs(object)
|
25
|
+
end
|
26
|
+
generate_autoloader(namespace_objects)
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_autoloader(namespace_objects)
|
30
|
+
generator = SketchUpYARD::Stubs::AutoLoadGenerator.new
|
31
|
+
autoload_file = File.join(stubs_gem_path, 'sketchup.rb')
|
32
|
+
File.open(autoload_file, 'w') do |file|
|
33
|
+
generator.generate(namespace_objects, file)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def print_section(io, title, content)
|
38
|
+
return if content.strip.empty?
|
39
|
+
io.puts
|
40
|
+
io.puts " # #{title}"
|
41
|
+
io.puts
|
42
|
+
io.puts content
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_module_stubs(object)
|
46
|
+
filename = stub_filename(object)
|
47
|
+
ensure_exist(File.dirname(filename))
|
48
|
+
StubFile.open(filename, 'w') { |file|
|
49
|
+
file.puts file_header(object)
|
50
|
+
file.puts
|
51
|
+
file.puts namespace_definition(object)
|
52
|
+
print_section(file, 'Extends', generate_mixins(object, :class))
|
53
|
+
print_section(file, 'Includes', generate_mixins(object, :instance))
|
54
|
+
print_section(file, 'Constants', generate_constants_grouped(object))
|
55
|
+
print_section(file, 'Class Methods', generate_class_methods(object))
|
56
|
+
print_section(file, 'Instance Methods', generate_instance_methods(object))
|
57
|
+
file.puts
|
58
|
+
file.puts file_footer(object)
|
59
|
+
}
|
60
|
+
#trim_trailing_white_space(filename)
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_header(object)
|
64
|
+
header = StringIO.new
|
65
|
+
header.puts "# Copyright:: Copyright #{Time.now.year} Trimble Inc."
|
66
|
+
header.puts "# License:: The MIT License (MIT)"
|
67
|
+
#header.puts "# Generated:: #{Time.now.strftime('%F %R')}"
|
68
|
+
header.string
|
69
|
+
end
|
70
|
+
|
71
|
+
def file_footer(object)
|
72
|
+
return if object.root?
|
73
|
+
footer = StringIO.new
|
74
|
+
footer.puts 'end'
|
75
|
+
footer.string
|
76
|
+
end
|
77
|
+
|
78
|
+
def namespace_definition(object)
|
79
|
+
return if object.root?
|
80
|
+
definition = "#{object.type} #{object.path}"
|
81
|
+
if object.type == :class && object.superclass.name != :Object
|
82
|
+
definition << " < #{object.superclass.path}"
|
83
|
+
end
|
84
|
+
output = StringIO.new
|
85
|
+
output.puts generate_docstring(object)
|
86
|
+
output.puts definition
|
87
|
+
output.string
|
88
|
+
end
|
89
|
+
|
90
|
+
def output_path
|
91
|
+
options.serializer.options[:basepath] || File.join(Dir.pwd, 'stubs')
|
92
|
+
end
|
93
|
+
|
94
|
+
def stubs_root_path
|
95
|
+
ensure_exist(output_path)
|
96
|
+
end
|
97
|
+
|
98
|
+
def stubs_lib_path
|
99
|
+
ensure_exist(File.join(stubs_root_path, 'lib'))
|
100
|
+
end
|
101
|
+
|
102
|
+
def stubs_gem_path
|
103
|
+
ensure_exist(File.join(stubs_lib_path, 'sketchup-api-stubs'))
|
104
|
+
end
|
105
|
+
|
106
|
+
def stubs_path
|
107
|
+
ensure_exist(File.join(stubs_gem_path, 'stubs'))
|
108
|
+
end
|
109
|
+
|
110
|
+
def stub_filename(object)
|
111
|
+
basename = object.path.gsub('::', '/')
|
112
|
+
basename = '_top_level' if basename.empty?
|
113
|
+
File.join(stubs_path, "#{basename}.rb")
|
114
|
+
end
|
115
|
+
|
116
|
+
# A stable sort_by method.
|
117
|
+
#
|
118
|
+
# @param [Enumerable]
|
119
|
+
# @return [Array]
|
120
|
+
def stable_sort_by(list)
|
121
|
+
list.each_with_index.sort_by { |item, i| [yield(item), i] }.map(&:first)
|
122
|
+
end
|
123
|
+
|
124
|
+
CAMELCASE_CONSTANT = /^([A-Z]+[a-z]+)/
|
125
|
+
|
126
|
+
def group_constant(constant)
|
127
|
+
constant_name = constant.name.to_s
|
128
|
+
MANUAL_CONSTANT_GROUPS.each { |rule|
|
129
|
+
if rule[:constants]
|
130
|
+
return rule[:group] if rule[:constants].include?(constant_name)
|
131
|
+
else
|
132
|
+
return rule[:group] if rule[:regex].match(constant_name)
|
133
|
+
end
|
134
|
+
}
|
135
|
+
if constant_name.include?('_')
|
136
|
+
constant_name.split('_').first
|
137
|
+
else
|
138
|
+
constant_name[CAMELCASE_CONSTANT] || constant_name
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Sorts and groups constants for easier reading.
|
143
|
+
def generate_constants_grouped(object)
|
144
|
+
constants = run_verifier(object.constants(object_options))
|
145
|
+
# The constants are not sorted before chunking. This is because `chunk` groups
|
146
|
+
# consecutive items - and we want to chunk them based their relationship
|
147
|
+
# with each other. This ensure that constants that doesn't follow the normal
|
148
|
+
# pattern of PREFIX_SOME_NAME will still be grouped next to each other.
|
149
|
+
groups = constants.chunk { |constant|
|
150
|
+
group_constant(constant)
|
151
|
+
}
|
152
|
+
grouped_output = groups.map { |group, group_constants|
|
153
|
+
output = StringIO.new
|
154
|
+
# Each group itself is sorted in order to more easily scan the list.
|
155
|
+
sorted = stable_sort_by(group_constants, &:name)
|
156
|
+
sorted.each { |constant|
|
157
|
+
output.puts " #{constant.name} = nil # Stub value."
|
158
|
+
}
|
159
|
+
output.string
|
160
|
+
}
|
161
|
+
# Finally each group is also sorted, again to ease scanning for a particular
|
162
|
+
# name. We simply use the first character of each group.
|
163
|
+
stable_sort_by(grouped_output) { |item| item.lstrip[0] }.join("\n")
|
164
|
+
end
|
165
|
+
|
166
|
+
# Sort constants without grouping.
|
167
|
+
def generate_constants(object)
|
168
|
+
output = StringIO.new
|
169
|
+
constants = run_verifier(object.constants(object_options))
|
170
|
+
constants = stable_sort_by(constants, &:name)
|
171
|
+
constants.each { |constant|
|
172
|
+
output.puts " #{constant.name} = nil # Stub value."
|
173
|
+
}
|
174
|
+
output.string
|
175
|
+
end
|
176
|
+
|
177
|
+
def generate_mixins(object, scope)
|
178
|
+
output = StringIO.new
|
179
|
+
mixin_type = (scope == :class) ? 'extend' : 'include'
|
180
|
+
mixins = run_verifier(object.mixins(scope))
|
181
|
+
mixins = stable_sort_by(mixins, &:path)
|
182
|
+
mixins.each { |mixin|
|
183
|
+
output.puts " #{mixin_type} #{mixin.path}"
|
184
|
+
}
|
185
|
+
output.string
|
186
|
+
end
|
187
|
+
|
188
|
+
def generate_class_methods(object)
|
189
|
+
generate_methods(object, :class, 'self.')
|
190
|
+
end
|
191
|
+
|
192
|
+
def generate_instance_methods(object)
|
193
|
+
generate_methods(object, :instance)
|
194
|
+
end
|
195
|
+
|
196
|
+
def generate_methods(object, scope, prefix = '')
|
197
|
+
methods = sort_methods(object, scope)
|
198
|
+
signatures = methods.map { |method|
|
199
|
+
output = StringIO.new
|
200
|
+
# Cannot use `methods.signature` here as it would return the C/C++ function
|
201
|
+
# signature. Must generate one from the YARD data.
|
202
|
+
signature = generate_method_signature(method)
|
203
|
+
# NOTE: We must call `generate_docstring` after `generate_method_signature`
|
204
|
+
# because `generate_method_signature` will also clean up docstrings with
|
205
|
+
# a single @overload tag.
|
206
|
+
output.puts generate_docstring(method, 1)
|
207
|
+
output.puts " def #{prefix}#{signature}"
|
208
|
+
output.puts " end"
|
209
|
+
# Include aliases.
|
210
|
+
method.aliases.each { |method_alias|
|
211
|
+
output.puts " alias_method :#{method_alias.name}, :#{method.name}"
|
212
|
+
}
|
213
|
+
output.string
|
214
|
+
}
|
215
|
+
signatures.join("\n")
|
216
|
+
end
|
217
|
+
|
218
|
+
# NOTE: This may modify the docstring of the object.
|
219
|
+
def generate_method_signature(object)
|
220
|
+
signature = "#{object.name}"
|
221
|
+
# If there is a single overload then use that as the parameter list. Many of
|
222
|
+
# the SketchUp Ruby API methods will have this as it was safer to add an
|
223
|
+
# @overload tag instead of renaming the function argument names.
|
224
|
+
overloads = object.docstring.tags(:overload)
|
225
|
+
if overloads.size == 1
|
226
|
+
overload = overloads.first
|
227
|
+
parameters = overload.parameters
|
228
|
+
# Move the tags from the @overload tag to the root of the docstring. No need
|
229
|
+
# for a single overload tag - it's unexpected when reading the source.
|
230
|
+
object.docstring.add_tag(*overload.tags)
|
231
|
+
object.docstring.delete_tags(:overload)
|
232
|
+
else
|
233
|
+
parameters = object.parameters
|
234
|
+
end
|
235
|
+
# Compile the signature for the arguments and default values.
|
236
|
+
params = parameters.map { |param|
|
237
|
+
param.last.nil? ? param.first : param.join(' = ')
|
238
|
+
}.join(', ')
|
239
|
+
signature << "(#{params})" unless params.empty?
|
240
|
+
signature
|
241
|
+
end
|
242
|
+
|
243
|
+
def generate_docstring(object, indent_step = 0)
|
244
|
+
output = StringIO.new
|
245
|
+
indent = ' ' * indent_step
|
246
|
+
docstring = object.docstring
|
247
|
+
docstring.delete_tags(:par) # Remove obsolete @par tags.
|
248
|
+
docstring.to_raw.lines.each { |line|
|
249
|
+
# Naive check for tags with no indent - if it is we insert an extra line
|
250
|
+
# in order to get some space for easier reader. Doing it this way in order
|
251
|
+
# to avoid hacking YARD too much.
|
252
|
+
output.puts "#{indent}#" if line.start_with?('@')
|
253
|
+
# This is the original docstring line.
|
254
|
+
output.puts "#{indent}# #{line}"
|
255
|
+
}
|
256
|
+
output.string
|
257
|
+
end
|
258
|
+
|
259
|
+
def sort_methods(object, scope)
|
260
|
+
methods = run_verifier(object.meths(object_options))
|
261
|
+
objects = methods.select { |method|
|
262
|
+
!method.is_alias? && method.scope == scope
|
263
|
+
}
|
264
|
+
stable_sort_by(objects, &:name)
|
265
|
+
end
|
266
|
+
|
267
|
+
def object_options
|
268
|
+
{
|
269
|
+
:inherited => false,
|
270
|
+
:included => false
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
def ensure_exist(path)
|
276
|
+
unless File.directory?(path)
|
277
|
+
FileUtils.mkdir_p(path)
|
278
|
+
end
|
279
|
+
path
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
class StubFile < File
|
284
|
+
|
285
|
+
def puts(*args)
|
286
|
+
case args.size
|
287
|
+
when 0
|
288
|
+
super
|
289
|
+
when 1
|
290
|
+
super trim_trailing_white_space(args[0].to_s)
|
291
|
+
else
|
292
|
+
raise NotImplementedError
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
private
|
297
|
+
|
298
|
+
TRAILING_WHITE_SPACE = /([\t ]+)$/
|
299
|
+
def trim_trailing_white_space(string)
|
300
|
+
string.gsub(TRAILING_WHITE_SPACE, '')
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
|
306
|
+
MANUAL_CONSTANT_GROUPS = [
|
307
|
+
# UI.messagebox return values.
|
308
|
+
{
|
309
|
+
constants: %w{IDABORT IDCANCEL IDIGNORE IDNO IDOK IDRETRY IDYES},
|
310
|
+
group: 'ID_MESSAGEBOX'
|
311
|
+
},
|
312
|
+
# Axes
|
313
|
+
{
|
314
|
+
constants: %w{X_AXIS Y_AXIS Z_AXIS},
|
315
|
+
group: 'AXES'
|
316
|
+
},
|
317
|
+
# Axes 2D
|
318
|
+
{
|
319
|
+
constants: %w{X_AXIS_2D Y_AXIS_2D},
|
320
|
+
group: 'AXES2D'
|
321
|
+
},
|
322
|
+
# Transformation
|
323
|
+
{
|
324
|
+
constants: %w{IDENTITY IDENTITY_2D},
|
325
|
+
group: 'IDENTITY'
|
326
|
+
},
|
327
|
+
# Geom::PolygonMesh
|
328
|
+
{
|
329
|
+
constants: %w{
|
330
|
+
AUTO_SOFTEN HIDE_BASED_ON_INDEX NO_SMOOTH_OR_HIDE SMOOTH_SOFT_EDGES
|
331
|
+
SOFTEN_BASED_ON_INDEX},
|
332
|
+
group: 'SOFTEN'
|
333
|
+
},
|
334
|
+
# Sketchup::Importer
|
335
|
+
# The other constants start with Import, this was odd one out.
|
336
|
+
{
|
337
|
+
constants: %w{ImporterNotFound},
|
338
|
+
group: 'Import'
|
339
|
+
},
|
340
|
+
# Sketchup::Http
|
341
|
+
{
|
342
|
+
constants: %w{DELETE GET HEAD OPTIONS PATCH POST PUT},
|
343
|
+
group: 'HTTP'
|
344
|
+
},
|
345
|
+
# Sketchup::Licensing
|
346
|
+
{
|
347
|
+
constants: %w{EXPIRED LICENSED NOT_LICENSED TRIAL TRIAL_EXPIRED},
|
348
|
+
group: 'EX_LICENSE'
|
349
|
+
},
|
350
|
+
# Sketchup::Model
|
351
|
+
{
|
352
|
+
constants: %w{Make MakeTrial ProLicensed ProTrial},
|
353
|
+
group: 'SU_LICENSE'
|
354
|
+
},
|
355
|
+
# Sketchup::RenderingOptions
|
356
|
+
# Most ROP constants start with ROPSet, with a handful of exceptions.
|
357
|
+
{
|
358
|
+
regex: /^ROP/,
|
359
|
+
group: 'ROP'
|
360
|
+
},
|
361
|
+
]
|