rubocop-sketchup 0.4.1 → 0.5.0
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 +7 -1
- data/assets/logo.png +0 -0
- data/assets/output.html.erb +301 -0
- data/config/default.yml +141 -36
- data/lib/rubocop/sketchup/config.rb +28 -8
- data/lib/rubocop/sketchup/cop/deprecations/add_separator_to_menu.rb +4 -1
- data/lib/rubocop/sketchup/cop/deprecations/operation_next_transparent.rb +11 -2
- data/lib/rubocop/sketchup/cop/deprecations/require_all.rb +7 -2
- data/lib/rubocop/sketchup/cop/deprecations/set_texture_projection.rb +6 -2
- data/lib/rubocop/sketchup/cop/deprecations/show_ruby_panel.rb +4 -1
- data/lib/rubocop/sketchup/cop/deprecations/sketchup_set.rb +8 -0
- data/lib/rubocop/sketchup/cop/performance/openssl.rb +5 -3
- data/lib/rubocop/sketchup/cop/performance/operation_disable_ui.rb +10 -2
- data/lib/rubocop/sketchup/cop/performance/selection_bulk.rb +11 -4
- data/lib/rubocop/sketchup/cop/performance/type_check.rb +63 -0
- data/lib/rubocop/sketchup/cop/performance/typename.rb +6 -1
- data/lib/rubocop/sketchup/cop/requirements/api_namespace.rb +8 -2
- data/lib/rubocop/sketchup/cop/requirements/exit.rb +7 -3
- data/lib/rubocop/sketchup/cop/requirements/extension_namespace.rb +32 -2
- data/lib/rubocop/sketchup/cop/requirements/file_structure.rb +22 -10
- data/lib/rubocop/sketchup/cop/requirements/gem_install.rb +45 -0
- data/lib/rubocop/sketchup/cop/requirements/get_extension_license.rb +95 -0
- data/lib/rubocop/sketchup/cop/requirements/global_constants.rb +10 -1
- data/lib/rubocop/sketchup/cop/requirements/global_include.rb +9 -2
- data/lib/rubocop/sketchup/cop/requirements/global_methods.rb +10 -1
- data/lib/rubocop/sketchup/cop/requirements/global_variables.rb +13 -4
- data/lib/rubocop/sketchup/cop/requirements/language_handler_globals.rb +6 -4
- data/lib/rubocop/sketchup/cop/requirements/load_path.rb +9 -6
- data/lib/rubocop/sketchup/cop/requirements/minimal_registration.rb +26 -2
- data/lib/rubocop/sketchup/cop/requirements/observers_start_operation.rb +28 -2
- data/lib/rubocop/sketchup/cop/requirements/register_extension.rb +12 -2
- data/lib/rubocop/sketchup/cop/requirements/ruby_core_namespace.rb +14 -8
- data/lib/rubocop/sketchup/cop/requirements/ruby_stdlib_namespace.rb +594 -588
- data/lib/rubocop/sketchup/cop/requirements/shipped_extensions_namespace.rb +6 -6
- data/lib/rubocop/sketchup/cop/requirements/sketchup_extension.rb +28 -9
- data/lib/rubocop/sketchup/cop/requirements/sketchup_require.rb +163 -0
- data/lib/rubocop/sketchup/cop/suggestions/add_group.rb +49 -0
- data/lib/rubocop/sketchup/cop/suggestions/compatibility.rb +35 -6
- data/lib/rubocop/sketchup/cop/suggestions/dc_internals.rb +6 -3
- data/lib/rubocop/sketchup/cop/suggestions/file_encoding.rb +3 -0
- data/lib/rubocop/sketchup/cop/suggestions/model_entities.rb +9 -2
- data/lib/rubocop/sketchup/cop/suggestions/monkey_patched_api.rb +5 -1
- data/lib/rubocop/sketchup/cop/suggestions/operation_name.rb +20 -9
- data/lib/rubocop/sketchup/cop/suggestions/sketchup_find_support_file.rb +13 -2
- data/lib/rubocop/sketchup/cop/suggestions/tool_drawing_bounds.rb +44 -0
- data/lib/rubocop/sketchup/cop/suggestions/tool_invalidate.rb +66 -0
- data/lib/rubocop/sketchup/cop/suggestions/tool_user_input.rb +41 -0
- data/lib/rubocop/sketchup/cop/suggestions/toolbar_timer.rb +65 -0
- data/lib/rubocop/sketchup/cop.rb +38 -18
- data/lib/rubocop/sketchup/dc_globals.rb +1 -1
- data/lib/rubocop/sketchup/dc_methods.rb +27 -27
- data/lib/rubocop/sketchup/extension_project.rb +19 -2
- data/lib/rubocop/sketchup/formatter/extension_review.rb +35 -15
- data/lib/rubocop/sketchup/inject.rb +1 -1
- data/lib/rubocop/sketchup/namespace.rb +1 -0
- data/lib/rubocop/sketchup/namespace_checker.rb +4 -1
- data/lib/rubocop/sketchup/no_comment_disable.rb +1 -1
- data/lib/rubocop/sketchup/range_help.rb +52 -0
- data/lib/rubocop/sketchup/sketchup_version.rb +4 -2
- data/lib/rubocop/sketchup/tool_checker.rb +43 -0
- data/lib/rubocop/sketchup/version.rb +1 -1
- data/lib/rubocop/sketchup.rb +1 -1
- data/lib/rubocop-sketchup.rb +9 -0
- data/rubocop-sketchup.gemspec +8 -11
- metadata +18 -6
- data/lib/rubocop/sketchup/cop/suggestions/sketchup_require.rb +0 -67
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
# We check only against the top level namespaces. The core define more
|
15
15
|
# objects, but they are under one of the top level namespaces listed.
|
16
16
|
|
17
|
-
NAMESPACES_ADVANCED_CAMERA_TOOLS = %w
|
17
|
+
NAMESPACES_ADVANCED_CAMERA_TOOLS = %w[
|
18
18
|
ACTUtil
|
19
19
|
CameraAppObserver
|
20
20
|
CameraEntityObserver
|
@@ -28,9 +28,9 @@ module RuboCop
|
|
28
28
|
FSGeomUtils
|
29
29
|
FSValidate
|
30
30
|
PageNameChangeObserver
|
31
|
-
|
31
|
+
].freeze
|
32
32
|
|
33
|
-
NAMESPACES_DYNAMIC_COMPONENTS = %w
|
33
|
+
NAMESPACES_DYNAMIC_COMPONENTS = %w[
|
34
34
|
DCConverter
|
35
35
|
DCDownloader
|
36
36
|
DCFunctionsV1
|
@@ -39,11 +39,11 @@ module RuboCop
|
|
39
39
|
DCProgressBar
|
40
40
|
DynamicComponents
|
41
41
|
DynamicComponentsV1
|
42
|
-
|
42
|
+
].freeze
|
43
43
|
|
44
|
-
NAMESPACES_TRIMBLE_CONNECT = %w
|
44
|
+
NAMESPACES_TRIMBLE_CONNECT = %w[
|
45
45
|
Trimble
|
46
|
-
|
46
|
+
].freeze
|
47
47
|
|
48
48
|
NAMESPACES = (
|
49
49
|
NAMESPACES_ADVANCED_CAMERA_TOOLS |
|
@@ -5,22 +5,33 @@ module RuboCop
|
|
5
5
|
module SketchupRequirements
|
6
6
|
# Register a single instance of SketchupExtension per extension.
|
7
7
|
# This should be done by the root .rb file in the extension package.
|
8
|
+
#
|
9
|
+
# @example Good - a single SketchupExtension is registered.
|
10
|
+
# module Example
|
11
|
+
# unless file_loaded?(__FILE__)
|
12
|
+
# extension = SketchupExtension.new('Hello World', 'example/main')
|
13
|
+
# Sketchup.register_extension(extension, true)
|
14
|
+
# file_loaded(__FILE__)
|
15
|
+
# end
|
16
|
+
# end
|
8
17
|
class SketchupExtension < SketchUp::Cop
|
9
18
|
|
10
19
|
include SketchUp::NoCommentDisable
|
11
20
|
include SketchUp::ExtensionProject
|
12
21
|
include RangeHelp
|
13
22
|
|
23
|
+
# rubocop:disable Metrics/LineLength
|
14
24
|
MSG = 'Create and register one SketchupExtension instance per extension.'.freeze
|
15
25
|
MSG_CREATE_ONE = 'Create only SketchupExtension instance per extension.'.freeze
|
16
26
|
MSG_CREATE_MISSING = 'SketchupExtension.new not found.'.freeze
|
17
27
|
MSG_REGISTER_ONE = 'Only register one SketchupExtension instance per extension.'.freeze
|
18
28
|
MSG_REGISTER_MISSING = 'Registration of SketchupExtension not found. Expected %s'.freeze
|
29
|
+
# rubocop:enable Metrics/LineLength
|
19
30
|
|
20
31
|
# Reference: http://rubocop.readthedocs.io/en/latest/node_pattern/
|
21
32
|
def_node_search :sketchup_extension_new, <<-PATTERN
|
22
33
|
(send
|
23
|
-
(const nil? :SketchupExtension) :new
|
34
|
+
(const nil? :SketchupExtension) :new ...)
|
24
35
|
PATTERN
|
25
36
|
|
26
37
|
def_node_search :sketchup_register_extension, <<-PATTERN
|
@@ -49,8 +60,7 @@ module RuboCop
|
|
49
60
|
if extension_nodes.size > 1
|
50
61
|
add_offense(nil,
|
51
62
|
location: range,
|
52
|
-
message: MSG_CREATE_ONE
|
53
|
-
severity: :error)
|
63
|
+
message: MSG_CREATE_ONE)
|
54
64
|
return
|
55
65
|
end
|
56
66
|
|
@@ -59,8 +69,19 @@ module RuboCop
|
|
59
69
|
if extension_node.nil?
|
60
70
|
add_offense(nil,
|
61
71
|
location: range,
|
62
|
-
message: MSG_CREATE_MISSING
|
63
|
-
|
72
|
+
message: MSG_CREATE_MISSING)
|
73
|
+
return
|
74
|
+
end
|
75
|
+
|
76
|
+
# Ensure it have two arguments.
|
77
|
+
if extension_node.arguments.size < 2
|
78
|
+
message = if extension_node.arguments.size == 1
|
79
|
+
'Missing second argument for the path'
|
80
|
+
else
|
81
|
+
'Missing required name arguments'
|
82
|
+
end
|
83
|
+
add_offense(extension_node,
|
84
|
+
message: message)
|
64
85
|
return
|
65
86
|
end
|
66
87
|
|
@@ -79,8 +100,7 @@ module RuboCop
|
|
79
100
|
# Make sure there is only one call to `register_extension`.
|
80
101
|
if registered_vars.size > 1
|
81
102
|
add_offense(registered_vars[1],
|
82
|
-
message: MSG_REGISTER_ONE
|
83
|
-
severity: :error)
|
103
|
+
message: MSG_REGISTER_ONE)
|
84
104
|
return
|
85
105
|
end
|
86
106
|
|
@@ -89,8 +109,7 @@ module RuboCop
|
|
89
109
|
msg = MSG_REGISTER_MISSING % extension_var.to_s
|
90
110
|
add_offense(nil,
|
91
111
|
location: range,
|
92
|
-
message: msg
|
93
|
-
severity: :error)
|
112
|
+
message: msg)
|
94
113
|
end
|
95
114
|
end
|
96
115
|
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module SketchupRequirements
|
6
|
+
# Omit file extensions when using `Sketchup.require` to allow encrypted
|
7
|
+
# files to be loaded.
|
8
|
+
#
|
9
|
+
# Ruby C extensions, `.so`/`.bundle` libraries must always be loaded via
|
10
|
+
# the normal `require`. Omit the file extension to the libraries as Ruby
|
11
|
+
# will resolve the`.so`/`.bundle` automatically.
|
12
|
+
#
|
13
|
+
# @example Bad - This will fail if extension is encrypted
|
14
|
+
# Sketchup.require 'hello/world.rb'
|
15
|
+
#
|
16
|
+
# @example Good - This will work for `.rbe`, `.rbs` and `rb` files.
|
17
|
+
# Sketchup.require 'hello/world'
|
18
|
+
#
|
19
|
+
# @example Bad - This will fail if extension is encrypted
|
20
|
+
# extension = SketchupExtension.new("Example", "Example/main.rb")
|
21
|
+
#
|
22
|
+
# @example Good - This will work for `.rbe`, `.rbs` and `rb` files.
|
23
|
+
# extension = SketchupExtension.new("Example", "Example/main")
|
24
|
+
class SketchupRequire < SketchUp::Cop
|
25
|
+
|
26
|
+
include SketchUp::ExtensionProject
|
27
|
+
include SketchUp::NoCommentDisable
|
28
|
+
include RangeHelp
|
29
|
+
|
30
|
+
MSG_SKETCHUP_REQUIRE_EXT_NAME = 'Do not hard code file extensions '\
|
31
|
+
'with `Sketchup.require`.'.freeze
|
32
|
+
|
33
|
+
MSG_EXTENSION_NEW_EXT_NAME = 'Do not hard code file extensions '\
|
34
|
+
'with `SketchupExtension.new`.'.freeze
|
35
|
+
|
36
|
+
MSG_REQUIRE_FOR_BINARY = 'Use `require` instead of `Sketchup.require` '\
|
37
|
+
'to load binary Ruby libraries.'.freeze
|
38
|
+
|
39
|
+
MSG_OMIT_BINARY_EXT = 'Do not hard code .so/.bundle file '\
|
40
|
+
'extensions'.freeze
|
41
|
+
|
42
|
+
MSG_REQUIRE_ENCRYPTED = 'Use `Sketchup.require` when loading Ruby '\
|
43
|
+
'files for encrypted extensions.'.freeze
|
44
|
+
|
45
|
+
def_node_matcher :ruby_require, <<-PATTERN
|
46
|
+
(send nil? :require (str $_))
|
47
|
+
PATTERN
|
48
|
+
|
49
|
+
def_node_matcher :ruby_require?, <<-PATTERN
|
50
|
+
(send nil? :require (str _))
|
51
|
+
PATTERN
|
52
|
+
|
53
|
+
|
54
|
+
def_node_matcher :sketchup_require, <<-PATTERN
|
55
|
+
(send (const nil? :Sketchup) :require (str $_))
|
56
|
+
PATTERN
|
57
|
+
|
58
|
+
def_node_matcher :sketchup_require?, <<-PATTERN
|
59
|
+
(send (const nil? :Sketchup) :require (str _))
|
60
|
+
PATTERN
|
61
|
+
|
62
|
+
|
63
|
+
def_node_matcher :sketchup_extension_new, <<-PATTERN
|
64
|
+
(send (const nil? :SketchupExtension) :new _ (str $_))
|
65
|
+
PATTERN
|
66
|
+
|
67
|
+
def_node_matcher :sketchup_extension_new?, <<-PATTERN
|
68
|
+
(send (const nil? :SketchupExtension) :new _ (str _))
|
69
|
+
PATTERN
|
70
|
+
|
71
|
+
|
72
|
+
TOOLS_RUBY_FILES = %w[extensions.rb langhandler.rb sketchup.rb].freeze
|
73
|
+
|
74
|
+
|
75
|
+
def on_send(node)
|
76
|
+
if sketchup_require?(node)
|
77
|
+
filename = sketchup_require(node)
|
78
|
+
return if check_binary_sketchup_require(node, filename)
|
79
|
+
return if check_sketchup_require_filename(node, filename)
|
80
|
+
|
81
|
+
elsif ruby_require?(node)
|
82
|
+
filename = ruby_require(node)
|
83
|
+
return if check_binary_ruby_require(node, filename)
|
84
|
+
return if check_encrypted_require(node, filename)
|
85
|
+
|
86
|
+
elsif sketchup_extension_new?(node)
|
87
|
+
filename = sketchup_extension_new(node)
|
88
|
+
return if check_sketchup_extension_new_filename(node, filename)
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def binary_require?(filename)
|
96
|
+
return unless extension_binaries?
|
97
|
+
return if extension_binaries.empty?
|
98
|
+
|
99
|
+
extension_binaries.include?(filename)
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_binary_sketchup_require(node, filename)
|
103
|
+
return unless binary_require?(filename)
|
104
|
+
|
105
|
+
end_pos = node.loc.dot.end_pos
|
106
|
+
range = node.receiver.loc.expression.with(end_pos: end_pos)
|
107
|
+
add_offense(node, location: range, message: MSG_REQUIRE_FOR_BINARY)
|
108
|
+
true
|
109
|
+
end
|
110
|
+
|
111
|
+
def check_binary_ruby_require(node, filename)
|
112
|
+
ext_name = File.extname(filename)
|
113
|
+
return unless %w[.so .bundle].include?(ext_name)
|
114
|
+
|
115
|
+
add_offense(node, location: file_ext_range(node.arguments.first),
|
116
|
+
message: MSG_OMIT_BINARY_EXT)
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def check_sketchup_require_filename(node, filename)
|
121
|
+
return if valid_filename?(filename)
|
122
|
+
|
123
|
+
add_offense(node, location: file_ext_range(node.arguments.first),
|
124
|
+
message: MSG_SKETCHUP_REQUIRE_EXT_NAME)
|
125
|
+
true
|
126
|
+
end
|
127
|
+
|
128
|
+
def check_sketchup_extension_new_filename(node, filename)
|
129
|
+
return if valid_filename?(filename)
|
130
|
+
|
131
|
+
add_offense(node, location: file_ext_range(node.arguments.last),
|
132
|
+
message: MSG_EXTENSION_NEW_EXT_NAME)
|
133
|
+
true
|
134
|
+
end
|
135
|
+
|
136
|
+
def check_encrypted_require(node, filename)
|
137
|
+
return unless encrypted_extension?
|
138
|
+
return unless extension_file?(filename)
|
139
|
+
|
140
|
+
add_offense(node, location: node.loc.selector,
|
141
|
+
message: MSG_REQUIRE_ENCRYPTED)
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
def first_directory(path)
|
146
|
+
path.to_s.split(File::SEPARATOR).first.downcase
|
147
|
+
end
|
148
|
+
|
149
|
+
def extension_file?(filename)
|
150
|
+
pathname = Pathname.new(filename).cleanpath
|
151
|
+
return false unless pathname.relative?
|
152
|
+
|
153
|
+
first_directory(extension_directory) == first_directory(pathname)
|
154
|
+
end
|
155
|
+
|
156
|
+
def valid_filename?(filename)
|
157
|
+
File.extname(filename).empty? || TOOLS_RUBY_FILES.include?(filename)
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module SketchupSuggestions
|
6
|
+
# The idiomatic way to create groups via the Ruby API differ from the
|
7
|
+
# way you'd do it from the UI.
|
8
|
+
#
|
9
|
+
# Using the API you should prefer to create the group first, then add
|
10
|
+
# your geometry into the group. This is more performant and predictable.
|
11
|
+
#
|
12
|
+
# Grouping existing geometry via the API have historically been affected
|
13
|
+
# by bugs and issues.
|
14
|
+
#
|
15
|
+
# If you do have to group existing geometry via the API, make sure you
|
16
|
+
# group geometry from the active context; `model.active_entities`.
|
17
|
+
# Otherwise you might run into unexpected issues, even crashes.
|
18
|
+
#
|
19
|
+
# @example Adding new geometry
|
20
|
+
# # bad
|
21
|
+
# face1 = model.active_entities.add_face(points1)
|
22
|
+
# face2 = model.active_entities.add_face(points2)
|
23
|
+
# group = model.active_entities.add_group([face1, face2])
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# group = model.active_entities.add_group
|
27
|
+
# face1 = group.entities.add_face(points1)
|
28
|
+
# face2 = group.entities.add_face(points2)
|
29
|
+
class AddGroup < Cop
|
30
|
+
|
31
|
+
include RangeHelp
|
32
|
+
|
33
|
+
MSG = 'Avoid creating groups out of existing entities.'.freeze
|
34
|
+
|
35
|
+
def_node_matcher :add_group?, <<-PATTERN
|
36
|
+
(send _ :add_group ...)
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
def on_send(node)
|
40
|
+
return unless add_group?(node)
|
41
|
+
return if node.arguments.empty?
|
42
|
+
|
43
|
+
add_offense(node, location: arguments_range(node))
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -3,22 +3,31 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module SketchupSuggestions
|
6
|
+
# It's easy to lose track of what API feature was added in what version or
|
7
|
+
# SketchUp. You can configure your target SketchUp version and be notified
|
8
|
+
# if you use features introduced in newer versions.
|
9
|
+
#
|
10
|
+
# @example Add this to your .rubocop.yml
|
11
|
+
# AllCops:
|
12
|
+
# SketchUp:
|
13
|
+
# TargetSketchUpVersion: 2016 M1
|
6
14
|
class Compatibility < SketchUp::Cop
|
7
15
|
|
8
16
|
include SketchUp::Features
|
9
17
|
|
10
|
-
MSG =
|
18
|
+
MSG = 'Incompatible feature with target SketchUp version'.freeze
|
11
19
|
|
12
20
|
def on_def(node)
|
13
21
|
return unless observer_method?(node)
|
22
|
+
|
14
23
|
feature_name = "##{node.method_name}"
|
15
24
|
check_feature(node, :method, feature_name)
|
16
25
|
end
|
17
26
|
|
18
27
|
def on_send(node)
|
28
|
+
feature_name = ''
|
19
29
|
if module_method?(node)
|
20
30
|
feature_name = "#{node.receiver.const_name}.#{node.method_name}"
|
21
|
-
check_feature(node, :method, feature_name)
|
22
31
|
else
|
23
32
|
# Instance methods are harder. It's difficult to infer the type of
|
24
33
|
# the receiver. If we only check the method name in isolation we
|
@@ -26,9 +35,10 @@ module RuboCop
|
|
26
35
|
# methods in Ruby itself and other older features.
|
27
36
|
# We try to match names that are unlikely to cause much noise.
|
28
37
|
return unless checkable_instance_method?(node)
|
38
|
+
|
29
39
|
feature_name = "##{node.method_name}"
|
30
|
-
check_feature(node, :method, feature_name)
|
31
40
|
end
|
41
|
+
check_feature(node, :method, feature_name)
|
32
42
|
end
|
33
43
|
|
34
44
|
def on_const(node)
|
@@ -42,15 +52,20 @@ module RuboCop
|
|
42
52
|
|
43
53
|
def check_feature(node, type, feature_name)
|
44
54
|
return unless sketchup_target_version?
|
55
|
+
|
45
56
|
full_feature_name = feature_name
|
46
57
|
FEATURES.each { |feature_set|
|
47
|
-
|
58
|
+
version = feature_set[:version]
|
59
|
+
feature_version = SketchUp::SketchUpVersion.new(version)
|
48
60
|
next unless feature_version > sketchup_target_version
|
61
|
+
|
49
62
|
objects = feature_set[:types][type] || []
|
50
63
|
if type == :method && instance_method?(feature_name)
|
51
64
|
# Instance methods are simply matching the method name since it's
|
52
65
|
# very difficult to determine the type of the receiver.
|
53
|
-
full_feature_name = objects.find { |object|
|
66
|
+
full_feature_name = objects.find { |object|
|
67
|
+
object.end_with?(feature_name)
|
68
|
+
}
|
54
69
|
next unless full_feature_name
|
55
70
|
else
|
56
71
|
next unless objects.include?(feature_name)
|
@@ -63,7 +78,21 @@ module RuboCop
|
|
63
78
|
message = "The #{feature_type} `#{feature_name}` was added in "\
|
64
79
|
"#{feature_version} which is incompatible with target "\
|
65
80
|
"#{sketchup_target_version}."
|
66
|
-
|
81
|
+
location = find_node_location(node)
|
82
|
+
add_offense(node, location: location, message: message)
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_node_location(node)
|
86
|
+
# Highlight the most pertinent piece of the expression.
|
87
|
+
if node.const_type?
|
88
|
+
:expression
|
89
|
+
elsif node.send_type?
|
90
|
+
:selector
|
91
|
+
elsif node.def_type?
|
92
|
+
:name
|
93
|
+
else
|
94
|
+
:expression
|
95
|
+
end
|
67
96
|
end
|
68
97
|
|
69
98
|
def module_method?(node)
|
@@ -4,12 +4,14 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module SketchupSuggestions
|
6
6
|
# Tapping into the internals of Dynamic Components is risky. It could
|
7
|
-
# change at any time.
|
7
|
+
# change at any time. If you create an extension that depend on the
|
8
|
+
# internal logic of another extension you are at the mercy of change and
|
9
|
+
# luck!
|
8
10
|
class DynamicComponentInternals < SketchUp::Cop
|
9
11
|
|
10
12
|
include SketchUp::DynamicComponentGlobals
|
11
13
|
|
12
|
-
MSG =
|
14
|
+
MSG = 'Avoid relying on internal logic of Dynamic Components.'.freeze
|
13
15
|
|
14
16
|
def on_gvar(node)
|
15
17
|
check_global(node)
|
@@ -22,7 +24,8 @@ module RuboCop
|
|
22
24
|
def check_global(node)
|
23
25
|
global_var, = *node
|
24
26
|
return unless dc_global_var?(global_var)
|
25
|
-
|
27
|
+
|
28
|
+
add_offense(node, location: :name, severity: :warning)
|
26
29
|
end
|
27
30
|
|
28
31
|
end
|
@@ -40,6 +40,7 @@ module RuboCop
|
|
40
40
|
def on_send(node)
|
41
41
|
return if file_loaded?(node)
|
42
42
|
return if node.arguments.none?(&method(:magic_file_or_dir?))
|
43
|
+
|
43
44
|
add_offense(node, location: :expression)
|
44
45
|
end
|
45
46
|
|
@@ -54,8 +55,10 @@ module RuboCop
|
|
54
55
|
# After assigning __FILE__ or __dir_ to a variable, check the parent
|
55
56
|
# scope to whether .force_encoding is called on the variable.
|
56
57
|
return if node.parent.nil?
|
58
|
+
|
57
59
|
encoded = force_encoding(node.parent).to_a
|
58
60
|
return if encoded.include?(lhs)
|
61
|
+
|
59
62
|
add_offense(node)
|
60
63
|
end
|
61
64
|
|
@@ -3,9 +3,15 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module SketchupSuggestions
|
6
|
+
# Prefer `model.active_entities` over `model.entities`.
|
7
|
+
#
|
8
|
+
# Most tools/actions act upon the active entities context. This could be
|
9
|
+
# an opened group or component instance. Because of this, prefer
|
10
|
+
# `model.active_entities` by default over `model.entities` unless you
|
11
|
+
# have an explicit reason to work in the root model context.
|
6
12
|
class ModelEntities < SketchUp::Cop
|
7
13
|
|
8
|
-
MSG = '
|
14
|
+
MSG = 'Prefer `model.active_entities` over `model.entities`.'.freeze
|
9
15
|
|
10
16
|
# Reference: http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
|
11
17
|
def_node_matcher :active_model_entities?, <<-PATTERN
|
@@ -18,10 +24,11 @@ module RuboCop
|
|
18
24
|
({lvar ivar cvar} $_) :entities)
|
19
25
|
PATTERN
|
20
26
|
|
21
|
-
MODEL_VARIABLE_NAMES = %w[model mod]
|
27
|
+
MODEL_VARIABLE_NAMES = %w[model mod].freeze
|
22
28
|
|
23
29
|
def model_entities?(node)
|
24
30
|
return true if active_model_entities?(node)
|
31
|
+
|
25
32
|
name = entities_receiver(node)
|
26
33
|
name && model_variable?(name)
|
27
34
|
end
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
|
24
24
|
if dc_method.key?(:variables)
|
25
25
|
return unless node.receiver && node.receiver.variable?
|
26
|
+
|
26
27
|
receiver_name = node.receiver.children.first
|
27
28
|
# Account for instance and class variables.
|
28
29
|
receiver_name = receiver_name.to_s.tr('@', '').to_sym
|
@@ -32,7 +33,10 @@ module RuboCop
|
|
32
33
|
path = dc_method[:path]
|
33
34
|
message = "#{path}##{name} is not part of the official API. "\
|
34
35
|
"It's a monkey-patched addition by Dynamic Components."
|
35
|
-
add_offense(node,
|
36
|
+
add_offense(node,
|
37
|
+
location: :selector,
|
38
|
+
severity: :warning,
|
39
|
+
message: message)
|
36
40
|
end
|
37
41
|
|
38
42
|
end
|
@@ -3,31 +3,43 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module SketchupSuggestions
|
6
|
+
# Operation name should be a short capitalized description. It will be
|
7
|
+
# visible to the user in the Edit > Undo menu. Make sure to give it a
|
8
|
+
# short human readable name, similar to SketchUp's own operation names.
|
9
|
+
#
|
10
|
+
# This cop make some very naive assumptions and will have more false
|
11
|
+
# positives than most of the other cops. It's purpose is mainly to enable
|
12
|
+
# awareness.
|
6
13
|
class OperationName < SketchUp::Cop
|
7
14
|
|
8
15
|
include RangeHelp
|
9
16
|
|
10
17
|
MSG = 'Operation name should be a short capitalized description.'.freeze
|
11
|
-
MSG_MAX =
|
18
|
+
MSG_MAX = 'Operation names should not be short and concise. [%d/%d]'.freeze
|
12
19
|
|
13
20
|
def on_send(node)
|
14
21
|
_, method_name, *args = *node
|
15
22
|
return unless method_name == :start_operation
|
16
23
|
return if args.empty? || !args.first.str_type?
|
24
|
+
|
25
|
+
# Ignore transparent operations.
|
26
|
+
return if args.size == 4 && args.last.true_type?
|
27
|
+
|
17
28
|
operation_name = args.first.str_content
|
18
29
|
# We can only inspect string literals.
|
19
30
|
return unless operation_name.is_a?(String)
|
31
|
+
|
20
32
|
# Check the format of the operation name.
|
21
33
|
unless acceptable_operation_name?(operation_name)
|
22
|
-
msg = %
|
34
|
+
msg = %(#{MSG} Expected: `"#{titleize(operation_name)}"`)
|
23
35
|
add_offense(args.first, location: :expression, message: msg)
|
24
36
|
end
|
25
37
|
# Check the length of the operation name.
|
26
38
|
unless operation_name.size <= max_operation_name_length
|
27
39
|
message = format(MSG_MAX, operation_name.size, max_operation_name_length)
|
28
40
|
add_offense(args.first,
|
29
|
-
|
30
|
-
|
41
|
+
location: excess_range(args.first, operation_name),
|
42
|
+
message: message)
|
31
43
|
end
|
32
44
|
# Ensure operation name is not empty.
|
33
45
|
if operation_name.empty?
|
@@ -54,6 +66,7 @@ module RuboCop
|
|
54
66
|
# Capitalization, no programmer name, no punctuation.
|
55
67
|
return false if name.end_with?('.')
|
56
68
|
return false if titleize(name) != name
|
69
|
+
|
57
70
|
true
|
58
71
|
end
|
59
72
|
|
@@ -67,21 +80,19 @@ module RuboCop
|
|
67
80
|
TITLEIZE_EXCLUDE = %w[
|
68
81
|
by for from in of to
|
69
82
|
and or if
|
70
|
-
]
|
83
|
+
].freeze
|
71
84
|
|
72
85
|
def titleize(string)
|
73
86
|
string = string.gsub(/[_.]/, ' ')
|
74
87
|
words = string.split.map { |word|
|
75
|
-
|
76
|
-
word
|
77
|
-
else
|
88
|
+
unless TITLEIZE_EXCLUDE.include?(word)
|
78
89
|
# word.capitalize won't work here, as we want to allow words like:
|
79
90
|
# "HTML", "SketchUp". So instead only the first character in each
|
80
91
|
# word is modified.
|
81
92
|
char = word[0].upcase
|
82
93
|
word[0, 1] = char
|
83
|
-
word
|
84
94
|
end
|
95
|
+
word
|
85
96
|
}
|
86
97
|
words.join(' ')
|
87
98
|
end
|
@@ -3,10 +3,20 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module SketchupSuggestions
|
6
|
-
# Avoid Sketchup.find_support_file to find your extension's files.
|
6
|
+
# Avoid `Sketchup.find_support_file` to find your extension's files.
|
7
|
+
#
|
8
|
+
# Users might install your extension to locations other than the default
|
9
|
+
# Plugins directory. If you use `Sketchup.find_support_file` to build
|
10
|
+
# a path for files in your extension it will fail in these scenarios.
|
11
|
+
#
|
12
|
+
# Instead prefer to use `__FILE__` or `__dir__` to build paths relative
|
13
|
+
# to your source files. This have the added benefit of allowing you to
|
14
|
+
# load your extensions directly from external directories under version
|
15
|
+
# control.
|
7
16
|
class SketchupFindSupportFile < SketchUp::Cop
|
8
17
|
|
9
|
-
MSG =
|
18
|
+
MSG = 'Avoid `Sketchup.find_support_file` to find your '\
|
19
|
+
"extension's files.".freeze
|
10
20
|
|
11
21
|
# http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
|
12
22
|
# https://rubocop.readthedocs.io/en/latest/node_pattern/
|
@@ -19,6 +29,7 @@ module RuboCop
|
|
19
29
|
|
20
30
|
def on_send(node)
|
21
31
|
return unless sketchup_find_support_file?(node)
|
32
|
+
|
22
33
|
add_offense(node, location: :expression)
|
23
34
|
end
|
24
35
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module SketchupSuggestions
|
6
|
+
# When drawing 3D geometry to the viewport from a tool, make sure to
|
7
|
+
# implement `getExtents` that return a `Geom::BoundingBox` object large
|
8
|
+
# enough to encompass what you draw.
|
9
|
+
#
|
10
|
+
# With out doing that the drawn content might end up being clipped.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# # good
|
14
|
+
# class ExampleTool
|
15
|
+
#
|
16
|
+
# def getExtents
|
17
|
+
# bounds = Geom::BoundingBox.new
|
18
|
+
# bounds.add(@points)
|
19
|
+
# bounds
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# def draw(view)
|
23
|
+
# view.draw(GL_LINES, @points)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# end
|
27
|
+
class ToolDrawingBounds < Cop
|
28
|
+
|
29
|
+
include SketchUp::ToolChecker
|
30
|
+
|
31
|
+
MSG_MISSING_GET_EXTENTS = 'When drawing to the viewport implement '\
|
32
|
+
'`getExtents` so drawn geometry is not clipped.'.freeze
|
33
|
+
|
34
|
+
def on_tool_class(class_node, body_methods)
|
35
|
+
return unless find_method(body_methods, :draw)
|
36
|
+
return if find_method(body_methods, :getExtents)
|
37
|
+
|
38
|
+
add_offense(class_node, message: MSG_MISSING_GET_EXTENTS)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|