rubocop-sketchup 0.0.2 → 0.1.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +95 -0
  3. data/lib/rubocop-sketchup.rb +5 -0
  4. data/lib/rubocop/sketchup.rb +12 -0
  5. data/lib/rubocop/sketchup/deprecations/operation_next_transparent.rb +2 -2
  6. data/lib/rubocop/sketchup/extension_project.rb +48 -0
  7. data/lib/rubocop/sketchup/inject.rb +17 -0
  8. data/lib/rubocop/sketchup/namespace_checker.rb +3 -4
  9. data/lib/rubocop/sketchup/performance/openssl.rb +39 -0
  10. data/lib/rubocop/sketchup/performance/operation_disable_ui.rb +3 -3
  11. data/lib/rubocop/sketchup/performance/selection_bulk.rb +72 -0
  12. data/lib/rubocop/sketchup/performance/typename.rb +1 -1
  13. data/lib/rubocop/sketchup/requirements/exit.rb +24 -0
  14. data/lib/rubocop/sketchup/requirements/extension_namespace.rb +1 -1
  15. data/lib/rubocop/sketchup/requirements/file_structure.rb +75 -0
  16. data/lib/rubocop/sketchup/requirements/global_constants.rb +8 -1
  17. data/lib/rubocop/sketchup/requirements/global_methods.rb +7 -7
  18. data/lib/rubocop/sketchup/requirements/global_variables.rb +9 -2
  19. data/lib/rubocop/sketchup/requirements/language_handler_globals.rb +42 -0
  20. data/lib/rubocop/sketchup/requirements/load_path.rb +2 -2
  21. data/lib/rubocop/sketchup/requirements/minimal_registration.rb +48 -0
  22. data/lib/rubocop/sketchup/requirements/register_extension.rb +35 -0
  23. data/lib/rubocop/sketchup/requirements/shipped_extensions_namespace.rb +61 -0
  24. data/lib/rubocop/sketchup/requirements/sketchup_extension.rb +80 -0
  25. data/lib/rubocop/sketchup/suggestions/dc_internals.rb +42 -0
  26. data/lib/rubocop/sketchup/suggestions/file_encoding.rb +78 -0
  27. data/lib/rubocop/sketchup/suggestions/model_entities.rb +2 -2
  28. data/lib/rubocop/sketchup/suggestions/operation_name.rb +59 -7
  29. data/lib/rubocop/sketchup/suggestions/sketchup_find_support_file.rb +28 -0
  30. data/lib/rubocop/sketchup/suggestions/sketchup_require.rb +42 -0
  31. data/lib/rubocop/sketchup/version.rb +1 -1
  32. data/rubocop-sketchup.gemspec +2 -1
  33. metadata +21 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75dbf007836345a888890dbbc327ca14b599dc8e
4
- data.tar.gz: 1198e5a2ac5357d3d84f467ce886ec6128ab18fd
3
+ metadata.gz: aaaa92ae5a979fc2fbfb37d4e809e3f747337029
4
+ data.tar.gz: 66216c4e9a5004d7e5a70cda380a257df65b68cf
5
5
  SHA512:
6
- metadata.gz: 8dc952cdc1b5c378ee3a1e806a7e3dbe8ec0547c4f26637d48d52adb5ad8aad6c7ec578a01f11361ab19d6c3f6c773f07174ffc55a2b8971cfc001889e15cdf3
7
- data.tar.gz: 91a8f974ab4cf849485559ab4c7f3cb234f6aecb2dc3fcfbaae1abc7bdd67a8b1b89893405a5aa8cab49412f7cab2cd9666416a0e8a2153d6689e2c3b0e1d7ab
6
+ metadata.gz: 0db51625b9ca4b001e899e5cda805ef99817badf8b710d8310204b83b86fd81b1b58519e7ebdcdf46222574c2559b93b0b9727343907b367efebaacebd084fe2
7
+ data.tar.gz: 6dc1219506f59c2abb7e0246715b35304acf14aa62d165be5aa2f7b5c2a696737537f8485186ac694682ef5e09797b96fee0b47e74a7cd5e24db06c500a861a8
@@ -0,0 +1,95 @@
1
+ ---
2
+ SketchupPerformance/SelectionBulkChanges:
3
+ Description: >-
4
+ Avoid modifying the selection set within loops. It's much
5
+ faster to change the selection in bulk.
6
+ Enabled: true
7
+
8
+ SketchupPerformance/OpenSSL:
9
+ Description: >-
10
+ Avoid using Ruby's OpenSSL within SketchUp. A flaw in how it
11
+ obtain random bits under Windows can severely affect
12
+ performance, freezing SketchUp for many minutes.
13
+ This also affects SecureRandom and anything else
14
+ that depend on OpenSSL. Net::HTTP is affected if connecting via
15
+ HTTPS. Connecting via plain HTTP is unaffected.
16
+ Enabled: true
17
+
18
+ SketchupRequirements/Exit:
19
+ Description: Exit attempts to kill the Ruby interpreter.
20
+ Enabled: true
21
+
22
+ SketchupRequirements/FileStructure:
23
+ Description: >-
24
+ Check that the extension conform to expected file structure
25
+ with a single root .rb file and a support folder with matching
26
+ name.
27
+ Enabled: true
28
+
29
+ SketchupRequirements/LanguageHandlerGlobals:
30
+ Description: >-
31
+ Avoid using globals in general, but especially these which are
32
+ known to be in use by other extensions made by SketchUp.
33
+ They are still in use due to compatibility reasons.
34
+ Enabled: true
35
+
36
+ SketchupRequirements/MinimalRegistration:
37
+ Description: >-
38
+ Don't load extension files in the root file registering the
39
+ extension. Extensions should not load additional files when
40
+ it's disabled.
41
+ Enabled: true
42
+
43
+ SketchupRequirements/RegisterExtension:
44
+ Description: >-
45
+ Always register extensions to load by default. Otherwise it
46
+ might confuse users to think the extension isn't working.
47
+ Enabled: true
48
+
49
+ SketchupRequirements/ShippedExtensionsNamespace:
50
+ Description: ShippedExtensionsNamespace
51
+ Enabled: true
52
+
53
+ SketchupRequirements/SketchupExtension:
54
+ Description: >-
55
+ Register a single instance of SketchupExtension per extension.
56
+ This should be done by the root .rb file in the extension
57
+ package.
58
+ Enabled: true
59
+
60
+ SketchupSuggestions/FileEncoding:
61
+ Description: >-
62
+ When using __FILE__ and __dir__, beware that Ruby doesn't apply
63
+ the correct encoding to the strings under Windows. When they
64
+ contain non-english characters it will lead to exceptions being
65
+ raised when the strings are used. Force encoding to work around
66
+ this.
67
+ Enabled: true
68
+
69
+ SketchupSuggestions/SketchupExtension:
70
+ Description: Check that extension registers a SketchupExtension object.
71
+ Enabled: true
72
+ SourcePath: src
73
+
74
+ SketchupSuggestions/DynamicComponentInternals:
75
+ Description: >-
76
+ Avoid relying on internal logic of Dynamic Components. It's
77
+ behavior might change between versions.
78
+ Enabled: true
79
+
80
+ SketchupSuggestions/OperationName:
81
+ Description: Check that operation names are human friendly and consistent with SketchUp.
82
+ Enabled: true
83
+ Max: 25
84
+
85
+ SketchupSuggestions/SketchupFindSupportFile:
86
+ Description: >-
87
+ Avoid Sketchup.find_support_file to find your extension's files.
88
+ Extensions might be installed outside the SketchUp Plugins folder.
89
+ Use __FILE__ and __dir__ to locate your files relative to the
90
+ executing file.
91
+ Enabled: true
92
+
93
+ SketchupSuggestions/SketchupRequire:
94
+ Description: Omit file extensions when using Sketchup.require to allow encrypted files to be loaded.
95
+ Enabled: true
@@ -1,10 +1,15 @@
1
1
  require 'rubocop'
2
+ require 'rubocop/sketchup'
2
3
  require 'rubocop/sketchup/version'
4
+ require 'rubocop/sketchup/inject'
3
5
 
6
+ require 'rubocop/sketchup/extension_project'
4
7
  require 'rubocop/sketchup/namespace'
5
8
  require 'rubocop/sketchup/namespace_checker'
6
9
  require 'rubocop/sketchup/no_comment_disable'
7
10
 
11
+ RuboCop::SketchUp::Inject.defaults!
12
+
8
13
  # Load all custom cops.
9
14
  pattern = File.join(__dir__, 'rubocop', 'sketchup', '**/*rb')
10
15
  Dir.glob(pattern) { |file|
@@ -0,0 +1,12 @@
1
+ require 'pathname'
2
+
3
+ module RuboCop
4
+ # RuboCop SketchUp project namespace
5
+ module SketchUp
6
+ PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
7
+ CONFIG_DEFAULT = PROJECT_ROOT.join('config', 'default.yml').freeze
8
+ CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
9
+
10
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
11
+ end
12
+ end
@@ -6,14 +6,14 @@ module RuboCop
6
6
  # Weak warning. (Question?)
7
7
  class OperationNextTransparent < Cop
8
8
  MSG = 'Third argument is deprecated.'.freeze
9
-
9
+
10
10
  def on_send(node)
11
11
  _, method_name, *args = *node
12
12
  return unless method_name == :start_operation
13
13
  return if args.size < 3
14
14
  argument = args[2]
15
15
  next_transparent = argument.type == :true
16
- add_offense(argument, :expression) if next_transparent
16
+ add_offense(argument, location: :expression) if next_transparent
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Sketchup
8
+ module ExtensionProject
9
+
10
+ # @return [Pathname]
11
+ def config_path
12
+ path = config.instance_variable_get(:@loaded_path)
13
+ if path
14
+ Pathname.new(path).expand_path.dirname
15
+ else
16
+ Pathname.new(Dir.pwd).expand_path
17
+ end
18
+ end
19
+
20
+ # @return [Pathname]
21
+ def relative_source_path
22
+ Pathname.new(cop_config['SourcePath'] || 'src')
23
+ end
24
+
25
+ # @return [Pathname]
26
+ def source_path
27
+ config_path.join(relative_source_path)
28
+ end
29
+
30
+ # @param [RuboCop::ProcessedSource] processed_source
31
+ def path_relative_to_source(processed_source)
32
+ source_filename = processed_source.buffer.name
33
+ rel_path = config.path_relative_to_config(source_filename)
34
+ path = Pathname.new(rel_path).expand_path
35
+ path.relative_path_from(source_path)
36
+ end
37
+
38
+ # @param [RuboCop::ProcessedSource] processed_source
39
+ def root_file?(processed_source)
40
+ filename = path_relative_to_source(processed_source)
41
+ filename.extname.downcase == '.rb' &&
42
+ filename.parent.to_s == '.'
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ module RuboCop
2
+ # Ripped directly from rubocop-rspec.
3
+ module SketchUp
4
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
5
+ # bit of our configuration.
6
+ module Inject
7
+ def self.defaults!
8
+ path = CONFIG_DEFAULT.to_s
9
+ hash = ConfigLoader.send(:load_yaml_configuration, path)
10
+ config = Config.new(hash, path)
11
+ puts "configuration from #{path}" if ConfigLoader.debug?
12
+ config = ConfigLoader.merge_with_default(config, path)
13
+ ConfigLoader.instance_variable_set(:@default_configuration, config)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -5,8 +5,6 @@ module RuboCop
5
5
  module Sketchup
6
6
  module NamespaceChecker
7
7
 
8
- include OnMethodDef
9
-
10
8
  def on_class(node)
11
9
  check_namespace(node)
12
10
  end
@@ -15,9 +13,10 @@ module RuboCop
15
13
  check_namespace(node)
16
14
  end
17
15
 
18
- def on_method_def(node, method_name, _args, _body)
16
+ def on_def(node)
19
17
  check_namespace(node)
20
18
  end
19
+ alias on_defs on_def
21
20
 
22
21
  # Constant assignment.
23
22
  def on_casgn(node)
@@ -25,7 +24,7 @@ module RuboCop
25
24
  end
26
25
 
27
26
  def check_namespace(node)
28
- add_offense(node, :name, nil, :error) if in_namespace?(node)
27
+ add_offense(node, location: :name, severity: :error) if in_namespace?(node)
29
28
  end
30
29
 
31
30
  def in_namespace?(node)
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupPerformance
6
+ # There are performance issue with the OpenSSL library that Ruby ship. In
7
+ # a clean SU session, default model there is a small delay observed in the
8
+ # Windows version of SU.
9
+ #
10
+ # But with a larger model loaded, or session that have had larger files
11
+ # loaded the lag will be minutes.
12
+ #
13
+ # `SecureRandom` is also affected by this, as it uses OpenSSL to seed.
14
+ #
15
+ # It also affects Net::HTTP if making HTTPS connections.
16
+ class OpenSSL < Cop
17
+
18
+ MSG = 'Avoid use of OpenSSL within SketchUp due to severe performance issues.'.freeze
19
+
20
+ # http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
21
+ # https://rubocop.readthedocs.io/en/latest/node_pattern/
22
+ def_node_matcher :require, <<-PATTERN
23
+ (send nil? :require
24
+ (str $_)
25
+ )
26
+ PATTERN
27
+
28
+ OPEN_SSL_USAGE = %w[openssl securerandom net/https net/http]
29
+
30
+ def on_send(node)
31
+ filename = require(node)
32
+ return if filename.nil?
33
+ return unless OPEN_SSL_USAGE.include?(filename.downcase)
34
+ add_offense(node, location: :expression)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -11,13 +11,13 @@ module RuboCop
11
11
  _, method_name, *args = *node
12
12
  return unless method_name == :start_operation
13
13
  if args.size < 2
14
- add_offense(node, :expression)
14
+ add_offense(node, location: :expression)
15
15
  return
16
16
  end
17
17
  argument = args[1]
18
- disable_ui = argument.children.first
18
+ disable_ui = argument.truthy_literal?
19
19
  return if disable_ui
20
- add_offense(argument, :expression)
20
+ add_offense(argument, location: :expression)
21
21
  end
22
22
  end
23
23
  end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupPerformance
6
+ # Avoid modifying the selection set within loops. It's much faster to
7
+ # change the selection in bulk.
8
+ #
9
+ # @example Poor performance
10
+ # model = Sketchup.active_model
11
+ # model.active_entities.each { |entity|
12
+ # model.selection.add(entity) if entity.is_a?(Sketchup::Face)
13
+ # }
14
+ #
15
+ # @example Better performance
16
+ # model = Sketchup.active_model
17
+ # faces = model.active_entities.map { |entity|
18
+ # entity.is_a?(Sketchup::Face)
19
+ # }
20
+ # model.selection.add(faces)
21
+ #
22
+ # @example Better performance and simpler
23
+ # model = Sketchup.active_model
24
+ # faces = model.active_entities.grep(Sketchup::Face)
25
+ # model.selection.add(faces)
26
+ class SelectionBulkChanges < Cop
27
+ MSG = 'Avoid modifying selecting within loops.'.freeze
28
+
29
+ # http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
30
+ # https://rubocop.readthedocs.io/en/latest/node_pattern/
31
+ def_node_matcher :selection?, <<-PATTERN
32
+ (send
33
+ (send _ {:selection :sel}) {:add :remove :toggle}
34
+ ...)
35
+ PATTERN
36
+
37
+ def_node_matcher :block_loop?, <<-PATTERN
38
+ (block
39
+ (send
40
+ (send _ _) {
41
+ :each :each_with_index :each_with_object
42
+ :each_entry :each_index :each_slice
43
+ :each_key :each_pair :each_value
44
+ :grep
45
+ } ...)
46
+ ...)
47
+ PATTERN
48
+
49
+ def_node_matcher :numeric_loop?, <<-PATTERN
50
+ (block
51
+ (send
52
+ ({int float} _) {:times :upto :downto} ...)
53
+ ...)
54
+ PATTERN
55
+
56
+ def iterator?(node)
57
+ node.is_a?(RuboCop::AST::ForNode) ||
58
+ node.is_a?(RuboCop::AST::UntilNode) ||
59
+ node.is_a?(RuboCop::AST::WhileNode) ||
60
+ block_loop?(node) ||
61
+ numeric_loop?(node)
62
+ end
63
+
64
+ def on_send(node)
65
+ return unless selection?(node)
66
+ return unless node.ancestors.any?(&method(:iterator?))
67
+ add_offense(node, location: :expression)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -11,7 +11,7 @@ module RuboCop
11
11
  return unless method_name == :typename
12
12
  # TODO(thomthom): Should we try to detect use of #typename
13
13
  # in context of comparing against a string?
14
- add_offense(node, :expression)
14
+ add_offense(node, location: :expression)
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Don't attempt to kill the Ruby interpreter by calling exit or exit!.
7
+ class Exit < Cop
8
+
9
+ include NoCommentDisable
10
+
11
+ MSG = 'Exit attempts to kill the Ruby interpreter.'.freeze
12
+
13
+ # Reference: http://rubocop.readthedocs.io/en/latest/development/
14
+ def_node_matcher :exit?, <<-PATTERN
15
+ (send nil? {:exit :exit!})
16
+ PATTERN
17
+
18
+ def on_send(node)
19
+ add_offense(node, location: :expression) if exit?(node)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -42,7 +42,7 @@ module RuboCop
42
42
  top = namespace.first
43
43
  @@namespace ||= top
44
44
  return if @@namespace == top
45
- add_offense(node, :name, nil, :error)
45
+ add_offense(node, location: :name, severity: :error)
46
46
  end
47
47
 
48
48
  def reserved?(namespace)
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Check that the extension conform to expected file structure with a
7
+ # single root .rb file and a support folder with matching name.
8
+ class FileStructure < Cop
9
+
10
+ include Sketchup::ExtensionProject
11
+ include NoCommentDisable
12
+
13
+ IGNORED_DIRECTORIES = %w[
14
+ __MACOSX
15
+ ].freeze
16
+
17
+ def investigate(processed_source)
18
+ return if already_run?
19
+
20
+ # Using range similar to RuboCop::Cop::Naming::Filename (file_name.rb)
21
+ range = source_range(processed_source.buffer, 1, 0)
22
+
23
+ # Find all root Ruby files in the source directory.
24
+ pattern = "#{source_path}/*.rb"
25
+ root_ruby_files = Dir.glob(pattern)
26
+
27
+ # Ensure there is only one root Ruby file.
28
+ if root_ruby_files.size != 1
29
+ msg = "Extensions must have exactly one root Ruby (.rb) file. Found: %d"
30
+ add_offense(nil, location: range, message: format(msg, root_ruby_files.size))
31
+ return
32
+ end
33
+
34
+ # Find the root file and collect the sub-directories.
35
+ root_file = root_ruby_files.first
36
+ extension_basename = File.basename(root_file, '.*')
37
+ sub_folders = source_path.children.select { |c| c.directory? }
38
+ sub_folders.reject! { |folder|
39
+ IGNORED_DIRECTORIES.include?(folder.basename.to_s)
40
+ }
41
+
42
+ # Ensure there is only one sub-directory.
43
+ if sub_folders.size != 1
44
+ msg = "Extensions must have exactly one support directory. Found %d"
45
+ add_offense(nil, location: range, message: format(msg, sub_folders.size))
46
+ return
47
+ end
48
+
49
+ # Ensure support directory's name match the root Ruby file.
50
+ support_directory = sub_folders.first
51
+ unless support_directory.basename.to_s == extension_basename
52
+ msg = 'Extensions must have a support directory matching the name of the root Ruby file. Expected %s, found %s'
53
+ msg = format(msg, extension_basename, support_directory.basename)
54
+ add_offense(nil, location: range, message: msg)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ @@already_run = false
61
+
62
+ def already_run?
63
+ return true if @@already_run
64
+ @@already_run = true
65
+ false
66
+ end
67
+
68
+ def self.reset
69
+ @@already_run = false
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -10,10 +10,17 @@ module RuboCop
10
10
 
11
11
  MSG = 'Do not introduce global constants.'.freeze
12
12
 
13
+ def_node_matcher :namespaced_constant?, <<-PATTERN
14
+ (casgn
15
+ (const _ _) ...
16
+ )
17
+ PATTERN
18
+
13
19
  # Constant assignment.
14
20
  def on_casgn(node)
21
+ return if namespaced_constant?(node)
15
22
  namespace = Namespace.new(node.parent_module_name)
16
- add_offense(node, :name, nil, :error) if namespace.top_level?
23
+ add_offense(node, location: :name, severity: :error) if namespace.top_level?
17
24
  end
18
25
 
19
26
  end
@@ -6,7 +6,6 @@ module RuboCop
6
6
  class GlobalMethods < Cop
7
7
 
8
8
  include NoCommentDisable
9
- include OnMethodDef
10
9
  include SketchUp
11
10
 
12
11
  MSG = 'Do not introduce global methods.'.freeze
@@ -30,16 +29,16 @@ module RuboCop
30
29
  def_node_matcher :class_method, <<-PATTERN
31
30
  (defs
32
31
  {
33
- (const nil $_)
34
- (const (const nil $_) ...)
32
+ (const nil? $_)
33
+ (const (const nil? $_) ...)
35
34
  }
36
35
  ...
37
36
  )
38
37
  PATTERN
39
38
 
40
- def on_method_def(node, method_name, _args, _body)
41
- class_method_parent = class_method(node)
42
- if class_method_parent
39
+ def on_def(node)
40
+ if class_method?(node)
41
+ class_method_parent = class_method(node)
43
42
  namespace = Namespace.new(class_method_parent.to_s)
44
43
  else
45
44
  # If a method is defined inside a block then parent_module_name
@@ -47,8 +46,9 @@ module RuboCop
47
46
  return if node.parent_module_name.nil?
48
47
  namespace = Namespace.new(node.parent_module_name)
49
48
  end
50
- add_offense(node, :name, nil, :error) if namespace.top_level?
49
+ add_offense(node, location: :name, severity: :error) if namespace.top_level?
51
50
  end
51
+ alias on_defs on_def
52
52
 
53
53
  end
54
54
  end
@@ -44,8 +44,15 @@ module RuboCop
44
44
  $CLASSPATH $JRUBY_VERSION $JRUBY_REVISION $ENV_JAVA
45
45
  ).map(&:to_sym)
46
46
 
47
+ # TODO: This should probably be read only.
48
+ SKETCHUP_VARS = %w(
49
+ $loaded_files
50
+ ).map(&:to_sym)
51
+
52
+ ALLOWED_VARS = BUILT_IN_VARS | SKETCHUP_VARS
53
+
47
54
  def allowed_var?(global_var)
48
- BUILT_IN_VARS.include?(global_var)
55
+ ALLOWED_VARS.include?(global_var)
49
56
  end
50
57
 
51
58
  def on_gvar(node)
@@ -59,7 +66,7 @@ module RuboCop
59
66
  def check(node)
60
67
  global_var, = *node
61
68
 
62
- add_offense(node, :name, nil, :error) unless allowed_var?(global_var)
69
+ add_offense(node, location: :name, severity: :error) unless allowed_var?(global_var)
63
70
  end
64
71
  end
65
72
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Avoid using globals in general, but especially these which are known to
7
+ # be in use by other extensions made by SketchUp.
8
+ # They are still in use due to compatibility reasons.
9
+ class LanguageHandlerGlobals < Cop
10
+
11
+ MSG = "Avoid globals in general, but especially these which are known to be in use.".freeze
12
+
13
+ LH_GLOBALS = %i[
14
+ $dc_strings
15
+ $devl_strings
16
+ $exStrings
17
+ $fs_strings
18
+ $make_pano_string
19
+ $oceanStrings
20
+ $sn_strings
21
+ $ssf_strings
22
+ $suStrings
23
+ $tStrings
24
+ $unitsStrings
25
+ $uStrings
26
+ $wt_strings
27
+ ]
28
+
29
+ def hl_global_var?(global_var)
30
+ LH_GLOBALS.include?(global_var)
31
+ end
32
+
33
+ def on_gvasgn(node)
34
+ global_var, = *node
35
+ return unless hl_global_var?(global_var)
36
+ add_offense(node, location: :name, severity: :error)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -64,14 +64,14 @@ module RuboCop
64
64
  def on_gvasgn(node)
65
65
  global_var, = *node
66
66
 
67
- add_offense(node, :name, nil, :error) if load_path?(global_var)
67
+ add_offense(node, location: :name, severity: :error) if load_path?(global_var)
68
68
  end
69
69
 
70
70
  def on_send(node)
71
71
  method_name = load_path_mutator?(node)
72
72
  return unless method_name
73
73
 
74
- add_offense(node, :expression, nil, :error)
74
+ add_offense(node, location: :expression, severity: :error)
75
75
  end
76
76
 
77
77
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module SketchupRequirements
8
+ # Don't load extension files in the root file registering the extension.
9
+ # Extensions should not load additional files when it's disabled.
10
+ class MinimalRegistration < Cop
11
+
12
+ include Sketchup::ExtensionProject
13
+ include NoCommentDisable
14
+
15
+ MSG = "Don't load extension files in the root file registering the extension.".freeze
16
+
17
+ # Reference: http://rubocop.readthedocs.io/en/latest/node_pattern/
18
+ def_node_matcher :require_filename, <<-PATTERN
19
+ (:send
20
+ {(const nil? :Sketchup) nil?} :require
21
+ (str $_))
22
+ PATTERN
23
+
24
+ def investigate(processed_source)
25
+ if root_file?(processed_source)
26
+ filename = processed_source.buffer.name
27
+ @extension_basename = File.basename(filename, '.*')
28
+ end
29
+ end
30
+
31
+ def extension_file?(filename)
32
+ return false unless filename.include?('/')
33
+ first_directory = filename.split('/').first
34
+ @extension_basename.casecmp(first_directory) == 0
35
+ end
36
+
37
+ def on_send(node)
38
+ return unless @extension_basename
39
+ filename = require_filename(node)
40
+ return if filename.nil?
41
+ return unless extension_file?(filename)
42
+ add_offense(node)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Always register extensions to load by default. Otherwise it might
7
+ # confuse users to think the extension isn't working.
8
+ class RegisterExtension < Cop
9
+
10
+ include NoCommentDisable
11
+
12
+ MSG = 'Always register extensions to load by default.'.freeze
13
+
14
+ def_node_search :sketchup_register_extension, <<-PATTERN
15
+ (:send
16
+ (const nil? :Sketchup) :register_extension
17
+ $...)
18
+ PATTERN
19
+
20
+ def on_send(node)
21
+ sketchup_register_extension(node).each { |args|
22
+ if args.size < 2
23
+ add_offense(node)
24
+ next
25
+ end
26
+ load_arg = args[1]
27
+ next if load_arg.true_type?
28
+ add_offense(load_arg)
29
+ }
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Don't modify SketchUp's shipped extensions.
7
+ class ShippedExtensionsNamespace < Cop
8
+
9
+ include NoCommentDisable
10
+ include Sketchup::NamespaceChecker
11
+
12
+ MSG = 'Do not modify shipped extensions.'.freeze
13
+
14
+ # We check only against the top level namespaces. The core define more
15
+ # objects, but they are under one of the top level namespaces listed.
16
+
17
+ NAMESPACES_ADVANCED_CAMERA_TOOLS = %w(
18
+ ACTUtil
19
+ CameraAppObserver
20
+ CameraEntityObserver
21
+ CameraFrameChangeObserver
22
+ CameraRep
23
+ CameraToolModelObserver
24
+ CameraToolPagesObserver
25
+ CameraToolViewObserver
26
+ FilmCameraTool
27
+ FSCameraData
28
+ FSGeomUtils
29
+ FSValidate
30
+ PageNameChangeObserver
31
+ ).freeze
32
+
33
+ NAMESPACES_DYNAMIC_COMPONENTS = %w(
34
+ DCConverter
35
+ DCDownloader
36
+ DCFunctionsV1
37
+ DCInteractTool
38
+ DCObservers
39
+ DCProgressBar
40
+ DynamicComponents
41
+ DynamicComponentsV1
42
+ ).freeze
43
+
44
+ NAMESPACES_TRIMBLE_CONNECT = %w(
45
+ Trimble
46
+ ).freeze
47
+
48
+ NAMESPACES = (
49
+ NAMESPACES_ADVANCED_CAMERA_TOOLS |
50
+ NAMESPACES_DYNAMIC_COMPONENTS |
51
+ NAMESPACES_TRIMBLE_CONNECT
52
+ ).freeze
53
+
54
+ def namespaces
55
+ NAMESPACES
56
+ end
57
+
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupRequirements
6
+ # Register a single instance of SketchupExtension per extension.
7
+ # This should be done by the root .rb file in the extension package.
8
+ class SketchupExtension < Cop
9
+
10
+ include Sketchup::ExtensionProject
11
+ include NoCommentDisable
12
+
13
+ MSG = 'Create and register one SketchupExtension instance per extension.'.freeze
14
+ MSG_CREATE_ONE = 'Create only SketchupExtension instance per extension.'.freeze
15
+ MSG_CREATE_MISSING = 'SketchupExtension.new not found.'.freeze
16
+ MSG_REGISTER_ONE = 'Only register one SketchupExtension instance per extension.'.freeze
17
+ MSG_REGISTER_MISSING = 'Registration of SketchupExtension not found. Expected %s'.freeze
18
+
19
+ # Reference: http://rubocop.readthedocs.io/en/latest/node_pattern/
20
+ def_node_search :sketchup_extension_new, <<-PATTERN
21
+ ({:lvasgn :ivasgn :cvasgn :gvasgn :casgn} ...
22
+ (:send
23
+ (:const nil? :SketchupExtension) :new
24
+ _
25
+ _))
26
+ PATTERN
27
+
28
+ def_node_search :sketchup_register_extension, <<-PATTERN
29
+ (:send
30
+ (const nil? :Sketchup) :register_extension
31
+ {({:lvar :ivar :cvar :gvar} $_)(const nil? $_)}
32
+ _)
33
+ PATTERN
34
+
35
+ def investigate(processed_source)
36
+ return unless root_file?(processed_source)
37
+
38
+ source_node = processed_source.ast
39
+ # Using range similar to RuboCop::Cop::Naming::Filename (file_name.rb)
40
+ range = source_range(processed_source.buffer, 1, 0)
41
+
42
+ # Look for SketchupExtension.new.
43
+ extension_nodes = sketchup_extension_new(source_node).to_a
44
+ if extension_nodes.size > 1
45
+ add_offense(nil, location: range, message: MSG_CREATE_ONE)
46
+ return
47
+ end
48
+ extension_node = extension_nodes.first
49
+ if extension_node.nil?
50
+ add_offense(nil, location: range, message: MSG_CREATE_MISSING)
51
+ return
52
+ end
53
+
54
+ # Find the name of the value SketchupExtension.new was assigned to.
55
+ if extension_node.casgn_type?
56
+ extension_var = extension_node.to_a[1]
57
+ else
58
+ extension_var = extension_node.to_a[0]
59
+ end
60
+
61
+ # Look for Sketchup.register and make sure it register the extension
62
+ # object detected earlier.
63
+ registered_vars = sketchup_register_extension(source_node).to_a
64
+ # TODO: The offences here should probably highlight the line where
65
+ # Sketchup.register_extension is.
66
+ if registered_vars.size > 1
67
+ add_offense(nil, location: range, message: MSG_REGISTER_ONE)
68
+ return
69
+ end
70
+ registered_var = sketchup_register_extension(source_node).first
71
+ unless registered_var == extension_var
72
+ msg = MSG_REGISTER_MISSING % extension_var.to_s
73
+ add_offense(nil, location: range, message: msg)
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Tapping into the internals of Dynamic Components is risky. It could
7
+ # change at any time.
8
+ class DynamicComponentInternals < Cop
9
+
10
+ MSG = "Avoid relying on internal logic of Dynamic Components.".freeze
11
+
12
+ DC_GLOBALS = %i[
13
+ $dc_strings
14
+ $dc_extension
15
+ $dc_CONFIGURATOR_NAME
16
+ $dc_REPORTER_NAME
17
+ $dc_MANAGER_NAME
18
+ $dc_observers
19
+ ]
20
+
21
+ def dc_global_var?(global_var)
22
+ DC_GLOBALS.include?(global_var)
23
+ end
24
+
25
+ def on_gvar(node)
26
+ check(node)
27
+ end
28
+
29
+ def on_gvasgn(node)
30
+ check(node)
31
+ end
32
+
33
+ def check(node)
34
+ global_var, = *node
35
+ return unless dc_global_var?(global_var)
36
+ add_offense(node, location: :name, severity: :error)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # When using __FILE__ and __dir__, beware that Ruby doesn't apply the
7
+ # correct encoding to the strings under Windows. When they contain
8
+ # non-english characters it will lead to exceptions being raised when the
9
+ # strings are used. Force encoding to work around this.
10
+ #
11
+ # @example Might fail
12
+ # basename = File.basename(__FILE__, '.*')
13
+ #
14
+ # @example Workaround
15
+ # file = __FILE__.dup
16
+ # file.force_encoding('UTF-8') if file.respond_to?(:force_encoding)
17
+ # basename = File.basename(file, '.*')
18
+ class FileEncoding < Cop
19
+
20
+ MSG = 'Beware encoding bug with __FILE__ and __dir__.'.freeze
21
+
22
+ def_node_matcher :file_loaded?, <<-PATTERN
23
+ (send nil? {:file_loaded? :file_loaded} ...)
24
+ PATTERN
25
+
26
+ def_node_matcher :magic_dir?, <<-PATTERN
27
+ (send nil? :__dir__)
28
+ PATTERN
29
+
30
+ def magic_file?(node)
31
+ node.respond_to?(:str_type?) &&
32
+ node.str_type? &&
33
+ node.source_range.is?('__FILE__')
34
+ end
35
+
36
+ def magic_file_or_dir?(node)
37
+ magic_file?(node) || magic_dir?(node)
38
+ end
39
+
40
+ def on_send(node)
41
+ return if file_loaded?(node)
42
+ return if node.arguments.none?(&method(:magic_file_or_dir?))
43
+ add_offense(node, location: :expression)
44
+ end
45
+
46
+
47
+ def_node_search :force_encoding, <<-PATTERN
48
+ (send
49
+ (_ $_)
50
+ :force_encoding ...)
51
+ PATTERN
52
+
53
+ def on_assign(node)
54
+ lhs, value = *node
55
+ return unless magic_file_or_dir?(value)
56
+ # After assigning __FILE__ or __dir_ to a variable, check the parent
57
+ # scope to whether .force_encoding is called on the variable.
58
+ return if node.parent.nil?
59
+ encoded = force_encoding(node.parent).to_a
60
+ return if encoded.include?(lhs)
61
+ add_offense(node)
62
+ end
63
+
64
+ # TODO: Review this list of aliases.
65
+ alias on_lvasgn on_assign
66
+ alias on_masgn on_assign
67
+ alias on_casgn on_assign
68
+ alias on_ivasgn on_assign
69
+ alias on_cvasgn on_assign
70
+ alias on_gvasgn on_assign
71
+ alias on_or_asgn on_assign
72
+ alias on_and_asgn on_assign
73
+ alias on_op_asgn on_assign
74
+
75
+ end
76
+ end
77
+ end
78
+ end
@@ -18,14 +18,14 @@ module RuboCop
18
18
  def_node_matcher :model_entities?, <<-PATTERN
19
19
  (send
20
20
  {
21
- (send (const nil :Sketchup) :active_model)
21
+ (send (const nil? :Sketchup) :active_model)
22
22
  (lvar {:model :mod})
23
23
  }
24
24
  :entities)
25
25
  PATTERN
26
26
 
27
27
  def on_send(node)
28
- add_offense(node, :expression) if model_entities?(node)
28
+ add_offense(node, location: :expression) if model_entities?(node)
29
29
  end
30
30
  end
31
31
  end
@@ -4,28 +4,80 @@ module RuboCop
4
4
  module Cop
5
5
  module SketchupSuggestions
6
6
  class OperationName < Cop
7
+
7
8
  MSG = 'Operation name should be a short capitalized description.'.freeze
9
+ MSG_MAX = "Operation names should not be short and concise. [%d/%d]".freeze
8
10
 
9
11
  def on_send(node)
10
12
  _, method_name, *args = *node
11
13
  return unless method_name == :start_operation
12
- operation_name = args.first.children.first
13
- return if acceptable_operation_name?(operation_name)
14
- add_offense(args.first, :expression)
14
+ return unless args.first.str_type?
15
+ operation_name = args.first.str_content
16
+ # We can only inspect string literals.
17
+ return unless operation_name.is_a?(String)
18
+ # Check the format of the operation name.
19
+ unless acceptable_operation_name?(operation_name)
20
+ msg = %[#{MSG} Expected: "#{titleize(operation_name)}"]
21
+ add_offense(args.first, location: :expression, message: msg)
22
+ end
23
+ # Check the length of the operation name.
24
+ unless operation_name.size <= max_operation_name_length
25
+ message = format(MSG_MAX, operation_name.size, max_operation_name_length)
26
+ add_offense(args.first,
27
+ location: excess_range(args.first, operation_name),
28
+ message: message)
29
+ end
30
+ # Ensure operation name is not empty.
31
+ if operation_name.empty?
32
+ msg = 'Operation names should not be empty.'
33
+ add_offense(args.first, location: :expression, message: msg)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def excess_range(node, operation_name)
40
+ string_start = node.source.index(operation_name)
41
+ range = node.loc.expression
42
+ if string_start
43
+ excess_start = range.begin_pos + string_start + max_operation_name_length
44
+ excess_end = range.begin_pos + string_start + operation_name.size
45
+ range_between(excess_start, excess_end)
46
+ else
47
+ range_between(range.begin_pos, range.end_pos)
48
+ end
15
49
  end
16
50
 
17
51
  def acceptable_operation_name?(name)
18
- # Capitalization, no programmer name, no punctuation
19
- return false if name.size > 25 # TODO: Separate Cop?
52
+ # Capitalization, no programmer name, no punctuation.
20
53
  return false if name.end_with?('.')
21
54
  return false if titleize(name) != name
22
55
  true
23
56
  end
24
57
 
25
- # TODO(thomthom): Might need to ignore words like 'and/or'
58
+ def max_operation_name_length
59
+ length = cop_config['Max'] || 25
60
+ return length if length.is_a?(Integer) && length > 0
61
+
62
+ raise 'Max needs to be a positive integer!'
63
+ end
64
+
65
+ TITLEIZE_EXCLUDE = %w[
66
+ by for from in of to
67
+ and or if
68
+ ]
69
+
26
70
  def titleize(string)
27
- string.split.map(&:capitalize).join(' ')
71
+ words = string.split.map { |word|
72
+ if TITLEIZE_EXCLUDE.include?(word)
73
+ word
74
+ else
75
+ word.capitalize
76
+ end
77
+ }
78
+ words.join(' ')
28
79
  end
80
+
29
81
  end
30
82
  end
31
83
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Avoid Sketchup.find_support_file to find your extension's files.
7
+ class SketchupFindSupportFile < Cop
8
+
9
+ MSG = "Don't hard code file extensions with Sketchup.require".freeze
10
+
11
+ # http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
12
+ # https://rubocop.readthedocs.io/en/latest/node_pattern/
13
+ def_node_matcher :sketchup_find_support_file?, <<-PATTERN
14
+ (send
15
+ (const nil? :Sketchup) :find_support_file
16
+ ...
17
+ )
18
+ PATTERN
19
+
20
+ def on_send(node)
21
+ return unless sketchup_find_support_file?(node)
22
+ add_offense(node, location: :expression)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module SketchupSuggestions
6
+ # Omit file extensions when using Sketchup.require to allow encrypted
7
+ # files to be loaded.
8
+ class SketchupRequire < Cop
9
+
10
+ MSG = "Don't hard code file extensions with Sketchup.require".freeze
11
+
12
+ # http://www.rubydoc.info/gems/rubocop/RuboCop/NodePattern
13
+ # https://rubocop.readthedocs.io/en/latest/node_pattern/
14
+ def_node_matcher :sketchup_require, <<-PATTERN
15
+ (send
16
+ (const nil? :Sketchup) :require
17
+ (str $_)
18
+ )
19
+ PATTERN
20
+
21
+ def_node_matcher :sketchup_require?, <<-PATTERN
22
+ (send
23
+ (const nil? :Sketchup) :require
24
+ (str _)
25
+ )
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ return unless sketchup_require?(node)
30
+ filename = sketchup_require(node)
31
+ add_offense(node, location: :expression) unless valid_filename?(filename)
32
+ end
33
+
34
+ private
35
+
36
+ def valid_filename?(filename)
37
+ File.extname(filename).empty?
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  module RuboCop
2
2
  module SketchUp
3
- VERSION = '0.0.2'
3
+ VERSION = '0.1.0'
4
4
  end
5
5
  end
@@ -17,11 +17,12 @@ Gem::Specification.new do |spec|
17
17
 
18
18
  spec.require_paths = ['lib']
19
19
  spec.files = Dir[
20
+ 'config/**/*',
20
21
  'lib/**/*',
21
22
  '*.gemspec',
22
23
  'Gemfile'
23
24
  ]
24
25
 
25
- spec.add_dependency 'rubocop', '~> 0.47.0'
26
+ spec.add_dependency 'rubocop', '~> 0.51.0'
26
27
  spec.add_development_dependency 'bundler', '~> 1.13'
27
28
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-sketchup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Trimble Inc, SketchUp Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-28 00:00:00.000000000 Z
11
+ date: 2017-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.47.0
19
+ version: 0.51.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.47.0
26
+ version: 0.51.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -45,23 +45,40 @@ extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
47
  - Gemfile
48
+ - config/default.yml
48
49
  - lib/rubocop-sketchup.rb
50
+ - lib/rubocop/sketchup.rb
49
51
  - lib/rubocop/sketchup/deprecations/operation_next_transparent.rb
52
+ - lib/rubocop/sketchup/extension_project.rb
53
+ - lib/rubocop/sketchup/inject.rb
50
54
  - lib/rubocop/sketchup/namespace.rb
51
55
  - lib/rubocop/sketchup/namespace_checker.rb
52
56
  - lib/rubocop/sketchup/no_comment_disable.rb
57
+ - lib/rubocop/sketchup/performance/openssl.rb
53
58
  - lib/rubocop/sketchup/performance/operation_disable_ui.rb
59
+ - lib/rubocop/sketchup/performance/selection_bulk.rb
54
60
  - lib/rubocop/sketchup/performance/typename.rb
55
61
  - lib/rubocop/sketchup/requirements/api_namespace.rb
62
+ - lib/rubocop/sketchup/requirements/exit.rb
56
63
  - lib/rubocop/sketchup/requirements/extension_namespace.rb
64
+ - lib/rubocop/sketchup/requirements/file_structure.rb
57
65
  - lib/rubocop/sketchup/requirements/global_constants.rb
58
66
  - lib/rubocop/sketchup/requirements/global_methods.rb
59
67
  - lib/rubocop/sketchup/requirements/global_variables.rb
68
+ - lib/rubocop/sketchup/requirements/language_handler_globals.rb
60
69
  - lib/rubocop/sketchup/requirements/load_path.rb
70
+ - lib/rubocop/sketchup/requirements/minimal_registration.rb
71
+ - lib/rubocop/sketchup/requirements/register_extension.rb
61
72
  - lib/rubocop/sketchup/requirements/ruby_core_namespace.rb
62
73
  - lib/rubocop/sketchup/requirements/ruby_stdlib_namespace.rb
74
+ - lib/rubocop/sketchup/requirements/shipped_extensions_namespace.rb
75
+ - lib/rubocop/sketchup/requirements/sketchup_extension.rb
76
+ - lib/rubocop/sketchup/suggestions/dc_internals.rb
77
+ - lib/rubocop/sketchup/suggestions/file_encoding.rb
63
78
  - lib/rubocop/sketchup/suggestions/model_entities.rb
64
79
  - lib/rubocop/sketchup/suggestions/operation_name.rb
80
+ - lib/rubocop/sketchup/suggestions/sketchup_find_support_file.rb
81
+ - lib/rubocop/sketchup/suggestions/sketchup_require.rb
65
82
  - lib/rubocop/sketchup/version.rb
66
83
  - rubocop-sketchup.gemspec
67
84
  homepage: http://github.com/sketchup/rubocop-sketchup