rubocop-sketchup 0.0.2 → 0.1.0

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