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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -1
  3. data/assets/logo.png +0 -0
  4. data/assets/output.html.erb +301 -0
  5. data/config/default.yml +141 -36
  6. data/lib/rubocop/sketchup/config.rb +28 -8
  7. data/lib/rubocop/sketchup/cop/deprecations/add_separator_to_menu.rb +4 -1
  8. data/lib/rubocop/sketchup/cop/deprecations/operation_next_transparent.rb +11 -2
  9. data/lib/rubocop/sketchup/cop/deprecations/require_all.rb +7 -2
  10. data/lib/rubocop/sketchup/cop/deprecations/set_texture_projection.rb +6 -2
  11. data/lib/rubocop/sketchup/cop/deprecations/show_ruby_panel.rb +4 -1
  12. data/lib/rubocop/sketchup/cop/deprecations/sketchup_set.rb +8 -0
  13. data/lib/rubocop/sketchup/cop/performance/openssl.rb +5 -3
  14. data/lib/rubocop/sketchup/cop/performance/operation_disable_ui.rb +10 -2
  15. data/lib/rubocop/sketchup/cop/performance/selection_bulk.rb +11 -4
  16. data/lib/rubocop/sketchup/cop/performance/type_check.rb +63 -0
  17. data/lib/rubocop/sketchup/cop/performance/typename.rb +6 -1
  18. data/lib/rubocop/sketchup/cop/requirements/api_namespace.rb +8 -2
  19. data/lib/rubocop/sketchup/cop/requirements/exit.rb +7 -3
  20. data/lib/rubocop/sketchup/cop/requirements/extension_namespace.rb +32 -2
  21. data/lib/rubocop/sketchup/cop/requirements/file_structure.rb +22 -10
  22. data/lib/rubocop/sketchup/cop/requirements/gem_install.rb +45 -0
  23. data/lib/rubocop/sketchup/cop/requirements/get_extension_license.rb +95 -0
  24. data/lib/rubocop/sketchup/cop/requirements/global_constants.rb +10 -1
  25. data/lib/rubocop/sketchup/cop/requirements/global_include.rb +9 -2
  26. data/lib/rubocop/sketchup/cop/requirements/global_methods.rb +10 -1
  27. data/lib/rubocop/sketchup/cop/requirements/global_variables.rb +13 -4
  28. data/lib/rubocop/sketchup/cop/requirements/language_handler_globals.rb +6 -4
  29. data/lib/rubocop/sketchup/cop/requirements/load_path.rb +9 -6
  30. data/lib/rubocop/sketchup/cop/requirements/minimal_registration.rb +26 -2
  31. data/lib/rubocop/sketchup/cop/requirements/observers_start_operation.rb +28 -2
  32. data/lib/rubocop/sketchup/cop/requirements/register_extension.rb +12 -2
  33. data/lib/rubocop/sketchup/cop/requirements/ruby_core_namespace.rb +14 -8
  34. data/lib/rubocop/sketchup/cop/requirements/ruby_stdlib_namespace.rb +594 -588
  35. data/lib/rubocop/sketchup/cop/requirements/shipped_extensions_namespace.rb +6 -6
  36. data/lib/rubocop/sketchup/cop/requirements/sketchup_extension.rb +28 -9
  37. data/lib/rubocop/sketchup/cop/requirements/sketchup_require.rb +163 -0
  38. data/lib/rubocop/sketchup/cop/suggestions/add_group.rb +49 -0
  39. data/lib/rubocop/sketchup/cop/suggestions/compatibility.rb +35 -6
  40. data/lib/rubocop/sketchup/cop/suggestions/dc_internals.rb +6 -3
  41. data/lib/rubocop/sketchup/cop/suggestions/file_encoding.rb +3 -0
  42. data/lib/rubocop/sketchup/cop/suggestions/model_entities.rb +9 -2
  43. data/lib/rubocop/sketchup/cop/suggestions/monkey_patched_api.rb +5 -1
  44. data/lib/rubocop/sketchup/cop/suggestions/operation_name.rb +20 -9
  45. data/lib/rubocop/sketchup/cop/suggestions/sketchup_find_support_file.rb +13 -2
  46. data/lib/rubocop/sketchup/cop/suggestions/tool_drawing_bounds.rb +44 -0
  47. data/lib/rubocop/sketchup/cop/suggestions/tool_invalidate.rb +66 -0
  48. data/lib/rubocop/sketchup/cop/suggestions/tool_user_input.rb +41 -0
  49. data/lib/rubocop/sketchup/cop/suggestions/toolbar_timer.rb +65 -0
  50. data/lib/rubocop/sketchup/cop.rb +38 -18
  51. data/lib/rubocop/sketchup/dc_globals.rb +1 -1
  52. data/lib/rubocop/sketchup/dc_methods.rb +27 -27
  53. data/lib/rubocop/sketchup/extension_project.rb +19 -2
  54. data/lib/rubocop/sketchup/formatter/extension_review.rb +35 -15
  55. data/lib/rubocop/sketchup/inject.rb +1 -1
  56. data/lib/rubocop/sketchup/namespace.rb +1 -0
  57. data/lib/rubocop/sketchup/namespace_checker.rb +4 -1
  58. data/lib/rubocop/sketchup/no_comment_disable.rb +1 -1
  59. data/lib/rubocop/sketchup/range_help.rb +52 -0
  60. data/lib/rubocop/sketchup/sketchup_version.rb +4 -2
  61. data/lib/rubocop/sketchup/tool_checker.rb +43 -0
  62. data/lib/rubocop/sketchup/version.rb +1 -1
  63. data/lib/rubocop/sketchup.rb +1 -1
  64. data/lib/rubocop-sketchup.rb +9 -0
  65. data/rubocop-sketchup.gemspec +8 -11
  66. metadata +18 -6
  67. 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
- ).freeze
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
- ).freeze
42
+ ].freeze
43
43
 
44
- NAMESPACES_TRIMBLE_CONNECT = %w(
44
+ NAMESPACES_TRIMBLE_CONNECT = %w[
45
45
  Trimble
46
- ).freeze
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
- severity: :error)
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 = "Incompatible feature with target SketchUp version".freeze
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
- feature_version = SketchUp::SketchUpVersion.new(feature_set[:version])
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| object.end_with?(feature_name) }
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
- add_offense(node, message: message)
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 = "Avoid relying on internal logic of Dynamic Components.".freeze
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
- add_offense(node, location: :name, severity: :error)
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 = 'Typically one should use model.active_entities instead of model.entities.'.freeze
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, severity: :error, message: message)
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 = "Operation names should not be short and concise. [%d/%d]".freeze
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 = %[#{MSG} Expected: `"#{titleize(operation_name)}"`]
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
- location: excess_range(args.first, operation_name),
30
- message: message)
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
- if TITLEIZE_EXCLUDE.include?(word)
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 = "Avoid Sketchup.find_support_file to find your extension's files.".freeze
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