spellr 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e106c60133833af83e09be866a0ccdb5241aaa48363567146b01b908cb180a43
4
- data.tar.gz: f2a08fee5d21140f628a8133b8bf6c7226e56b6452911a7846fee34ac22a9968
3
+ metadata.gz: 3725eb0cbde87e7f9ba2e6c8cfbd3c64143e3518e9d835ae284cd44214063f61
4
+ data.tar.gz: c7949e1f234014929bc8d57455127ca0c00e907a152cd346778621991c15f390
5
5
  SHA512:
6
- metadata.gz: 0d562f849cf66c85c6591fff5830856104bd5f3bc5176e0f247aa1f3f441e6c6b93afcbf9b6ac7fe982717cf9824d4df445ff318626024d78a1c2d81bbb76fa4
7
- data.tar.gz: 61abf55e8e008ae65386c763c039e55df0f634db23402741c15c92bc9154603be2464f793da88fedae9e56fe0d5419eadca28d47e64cf82a7cd809521ac49c98
6
+ metadata.gz: b076fef5196b5d997ef375de33f575aa83a351d6529755fa26c4de9aeab6b43162ca7b76668ec171405b57e7dec733228d10aaa860f1ebc975aa84e859ace642
7
+ data.tar.gz: 248adcf239ec2cc0c8d271d45f295a1aeeb2661ae3ba1c5852e671d98726f776ba1691bda4dec39647f9b03596c8282bf0cfac0a9ec50e6428e2f2de24837778
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ # v0.4.0 (unreleased)
2
+ - LOTS of performance improvements. it's about 4 times faster
3
+ - significantly better key heuristic matching, with configurable weight (`key_heuristic_weight`).
4
+ - Update FastIgnore dependency.
5
+ - Change the yml format slightly. `ignore` is now `excludes`. `only` is now `includes`
6
+ I feel like this makes more sense for the way the config is merged. and the right time to do it is when you'll probably have to tweak it anyway because:
7
+ - the `only`/`includes` items are now parsed using FastIgnore's gitignore inspired allow list format
8
+ (see https://github.com/robotdana/fast_ignore#using-an-includes-list)
9
+ Mostly it's the same just more flexible, though there may need to be some small adjustments.
10
+ - the cli arguments are now also managed using FastIgnore's rules, fixing issues with absolute paths and paths beginning with `./` also it's a LOT faster when just checking a single file, basically instant. so that's nice.
11
+
1
12
  # v0.3.2
2
13
  - add automatic rubygems and dockerhub deploy
3
14
 
data/Gemfile.lock CHANGED
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- spellr (0.3.2)
5
- fast_ignore
4
+ spellr (0.4.0)
5
+ fast_ignore (~> 0.4.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
@@ -10,11 +10,11 @@ GEM
10
10
  ast (2.4.0)
11
11
  coderay (1.1.2)
12
12
  diff-lcs (1.3)
13
- fast_ignore (0.3.2)
13
+ fast_ignore (0.4.0)
14
14
  jaro_winkler (1.5.3)
15
15
  method_source (0.9.2)
16
16
  parallel (1.17.0)
17
- parser (2.6.4.1)
17
+ parser (2.6.5.0)
18
18
  ast (~> 2.4.0)
19
19
  pry (0.12.2)
20
20
  coderay (~> 1.1.0)
@@ -28,23 +28,25 @@ GEM
28
28
  rspec-core (3.8.2)
29
29
  rspec-support (~> 3.8.0)
30
30
  rspec-eventually (0.2.2)
31
- rspec-expectations (3.8.4)
31
+ rspec-expectations (3.8.5)
32
32
  diff-lcs (>= 1.2.0, < 2.0)
33
33
  rspec-support (~> 3.8.0)
34
- rspec-mocks (3.8.1)
34
+ rspec-mocks (3.8.2)
35
35
  diff-lcs (>= 1.2.0, < 2.0)
36
36
  rspec-support (~> 3.8.0)
37
- rspec-support (3.8.2)
38
- rubocop (0.74.0)
37
+ rspec-support (3.8.3)
38
+ rubocop (0.75.0)
39
39
  jaro_winkler (~> 1.5.1)
40
40
  parallel (~> 1.10)
41
41
  parser (>= 2.6)
42
42
  rainbow (>= 2.2.2, < 4.0)
43
43
  ruby-progressbar (~> 1.7)
44
44
  unicode-display_width (>= 1.4.0, < 1.7)
45
- rubocop-rspec (1.35.0)
46
- rubocop (>= 0.60.0)
45
+ rubocop-rspec (1.36.0)
46
+ rubocop (>= 0.68.1)
47
47
  ruby-progressbar (1.10.1)
48
+ terminal-table (1.8.0)
49
+ unicode-display_width (~> 1.1, >= 1.1.1)
48
50
  tty_string (0.1.0)
49
51
  unicode-display_width (1.6.0)
50
52
 
@@ -60,6 +62,7 @@ DEPENDENCIES
60
62
  rubocop
61
63
  rubocop-rspec
62
64
  spellr!
65
+ terminal-table
63
66
  tty_string
64
67
 
65
68
  BUNDLED WITH
@@ -40,7 +40,9 @@ class SCOWLDownloader
40
40
 
41
41
  @options[:spelling] = a
42
42
  end
43
- opts.on('-p', '--[no-]programming', 'include common programming terms like grep (default true)') { |h| @options[:special] = :hacker if h }
43
+ opts.on('-p', '--[no-]programming', 'include common programming terms like grep (default true)') do |h|
44
+ @options[:special] = :hacker if h
45
+ end
44
46
  opts.on_tail('-h', '--help') do
45
47
  warn opts.to_s
46
48
  exit 1
data/lib/.spellr.yml CHANGED
@@ -1,14 +1,17 @@
1
1
  ---
2
2
  word_minimum_length: 3
3
+ key_heuristic_weight: 5
4
+ key_minimum_length: 6
3
5
 
4
- ignore: # this list is parsed with the .gitignore format
5
- - /.git/
6
+ excludes: # this list is parsed with the .gitignore format
7
+ - .git/
6
8
  - .spellr_wordlists/
7
9
  - .DS_Store
8
10
  - Gemfile.lock
9
11
  - .rspec_status
10
12
  - '*.png'
11
13
  - '*.jpg'
14
+ - '*.jpeg'
12
15
  - '*.gif'
13
16
  - '*.ico'
14
17
  - .gitkeep
@@ -28,7 +31,7 @@ languages:
28
31
  generate: "fetch english"
29
32
  # TODO: don't generate the ruby file until you actually need one
30
33
  ruby:
31
- only: # Filtered using File.fn_match
34
+ includes: # Filtered using gitignore format
32
35
  - '*.rb'
33
36
  - '*.rake'
34
37
  - '*.gemspec'
@@ -42,7 +45,7 @@ languages:
42
45
  hashbangs:
43
46
  - ruby
44
47
  html:
45
- only:
48
+ includes:
46
49
  - '*.html'
47
50
  - '*.hml'
48
51
  - '*.jsx'
@@ -59,7 +62,7 @@ languages:
59
62
  - '*.sass'
60
63
  - '*.less'
61
64
  js:
62
- only:
65
+ includes:
63
66
  - '*.html'
64
67
  - '*.hml'
65
68
  - '*.jsx'
@@ -71,22 +74,22 @@ languages:
71
74
  - '*.erb'
72
75
  - '*.json'
73
76
  shell:
74
- only:
77
+ includes:
75
78
  - '*.sh'
76
79
  - Dockerfile
77
80
  hashbangs:
78
81
  - bash
79
82
  - sh
80
83
  dockerfile:
81
- only:
84
+ includes:
82
85
  - Dockerfile
83
86
  css:
84
- only:
87
+ includes:
85
88
  - '*.css'
86
89
  - '*.sass'
87
90
  - '*.scss'
88
91
  xml:
89
- only:
92
+ includes:
90
93
  - '*.xml'
91
94
  - '*.html'
92
95
  - '*.haml'
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ unless RUBY_VERSION >= '2.4'
5
+ def sum
6
+ reduce(0) do |total, value|
7
+ total + if block_given?
8
+ yield value
9
+ else
10
+ value
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ class String
18
+ alias_method :match?, :match unless RUBY_VERSION >= '2.4'
19
+ end
data/lib/spellr/check.rb CHANGED
@@ -36,21 +36,24 @@ module Spellr
36
36
 
37
37
  private
38
38
 
39
- def check_file(file, start_at: nil, wordlists: Spellr.config.wordlists_for(file)) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/LineLength
40
- Spellr::Tokenizer.new(file, start_at: start_at).each_token do |token|
41
- next if wordlists.any? { |d| d.include?(token) }
42
-
43
- start_at = token.location
44
- reporter.call(token)
45
- @exit_code = 1
39
+ def check_file(file, start_at: nil, wordlists: Spellr.config.wordlists_for(file)) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
40
+ restart_token = catch(:check_file_from) do
41
+ Spellr::Tokenizer.new(file, start_at: start_at).each_token do |token|
42
+ next if wordlists.any? { |d| d.include?(token) }
43
+
44
+ start_at = token.location
45
+ reporter.call(token)
46
+ @exit_code = 1
47
+ end
48
+ nil
49
+ end
50
+ if restart_token
51
+ wordlist_arg = restart_token.replacement ? { wordlists: wordlists } : {} # new wordlist cache when adding a word
52
+ check_file(file, start_at: restart_token.location, **wordlist_arg)
46
53
  end
47
- rescue Spellr::DidReplacement => e # Yeah this is exceptions for control flow, but it makes sense to me
48
- check_file(file, start_at: e.token.location, wordlists: wordlists)
49
- rescue Spellr::DidAdd => e
50
- check_file(file, start_at: e.token.location) # don't cache the wordlists
51
54
  rescue InvalidByteSequence
52
55
  # sometimes files are binary
53
- puts "Skipped unreadable file: #{file}" unless Spellr.config.quiet?
56
+ warn "Skipped unreadable file: #{file}" unless Spellr.config.quiet?
54
57
  end
55
58
  end
56
59
  end
data/lib/spellr/config.rb CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  require_relative '../spellr'
4
4
  require_relative 'config_loader'
5
+ require_relative 'language'
6
+ require_relative 'reporter'
7
+ require 'pathname'
5
8
 
6
9
  module Spellr
7
- class Config
10
+ class Config # rubocop:disable Metrics/ClassLength
8
11
  attr_writer :reporter
9
12
  attr_reader :config_file
10
13
  attr_accessor :quiet
@@ -33,35 +36,69 @@ module Spellr
33
36
  end
34
37
 
35
38
  def word_minimum_length
36
- @config[:word_minimum_length]
39
+ @word_minimum_length ||= @config[:word_minimum_length]
37
40
  end
38
41
 
39
- def only
40
- @config[:only] || []
42
+ def key_heuristic_weight
43
+ @key_heuristic_weight ||= @config[:key_heuristic_weight]
41
44
  end
42
45
 
43
- def ignored
44
- @config[:ignore]
46
+ def key_minimum_length
47
+ @key_minimum_length ||= @config[:key_minimum_length]
48
+ end
49
+
50
+ def includes
51
+ return @includes if defined?(@includes)
52
+
53
+ if @config[:only]
54
+ warn <<~WARNING
55
+ \e[33mSpellr: `only:` yaml key with a list of fnmatch rules is deprecated.
56
+ Please use `includes:` instead, which uses gitignore-inspired rules.
57
+ see github.com/robotdana/fast_ignore#using-an-includes-list for details\e[0m
58
+ WARNING
59
+ end
60
+
61
+ @includes = (@config[:includes] || []) + (@config[:only] || [])
62
+ end
63
+
64
+ def excludes
65
+ return @excludes if defined?(@excludes)
66
+
67
+ if @config[:ignore]
68
+ warn <<~WARNING
69
+ \e[33mSpellr: `ignore:` yaml key is deprecated.
70
+ Please use `excludes:` instead.\e[0m
71
+ WARNING
72
+ end
73
+
74
+ @excludes = (@config[:excludes] || []) + (@config[:ignore] || [])
45
75
  end
46
76
 
47
77
  def color
48
78
  @config[:color]
49
79
  end
50
80
 
51
- def clear_cache
81
+ def clear_cache # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
52
82
  remove_instance_variable(:@wordlists) if defined?(@wordlists)
53
83
  remove_instance_variable(:@languages) if defined?(@languages)
54
84
  remove_instance_variable(:@errors) if defined?(@errors)
85
+ remove_instance_variable(:@word_minimum_length) if defined?(@word_minimum_length)
86
+ remove_instance_variable(:@key_heuristic_weight) if defined?(@key_heuristic_weight)
87
+ remove_instance_variable(:@key_minimum_length) if defined?(@key_minimum_length)
88
+ remove_instance_variable(:@excludes) if defined?(@excludes)
89
+ remove_instance_variable(:@includes) if defined?(@includes)
55
90
  end
56
91
 
57
92
  def languages
58
- require_relative 'language'
59
-
60
93
  @languages ||= @config[:languages].map do |key, args|
61
94
  Spellr::Language.new(key, args)
62
95
  end
63
96
  end
64
97
 
98
+ def pwd
99
+ @pwd ||= Pathname.pwd
100
+ end
101
+
65
102
  def languages_for(file)
66
103
  languages.select { |l| l.matches?(file) }
67
104
  end
@@ -109,8 +146,6 @@ module Spellr
109
146
  end
110
147
 
111
148
  def default_reporter
112
- require_relative 'reporter'
113
-
114
149
  Spellr::Reporter.new
115
150
  end
116
151
  end
data/lib/spellr/file.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'pathname'
4
4
 
5
+ # TODO: maybe just extend pathname if you have to
6
+
5
7
  module Spellr
6
8
  class File < Pathname
7
9
  def self.wrap(file)
@@ -9,19 +11,16 @@ module Spellr
9
11
  end
10
12
 
11
13
  def hashbang
12
- return if extname != ''
13
- return unless first_line&.start_with?('#!')
14
+ @hashbang ||= begin
15
+ return if extname != ''
16
+ return unless first_line&.start_with?('#!')
14
17
 
15
- first_line
18
+ first_line
19
+ end
16
20
  end
17
21
 
18
22
  def first_line
19
23
  @first_line ||= each_line.first
20
24
  end
21
-
22
- def fnmatch?(pattern)
23
- relative_path_from(Pathname.pwd).fnmatch?(pattern, ::File::FNM_DOTMATCH) ||
24
- Pathname.new(basename).fnmatch?(pattern, ::File::FNM_DOTMATCH)
25
- end
26
25
  end
27
26
  end
@@ -12,22 +12,27 @@ module Spellr
12
12
  @patterns = patterns
13
13
  end
14
14
 
15
- def config_only?(file)
16
- Spellr.config.only.empty? || Spellr.config.only.any? { |o| file.fnmatch?(o) }
17
- end
18
-
19
- def cli_only?(file)
20
- @patterns.empty? || @patterns.any? { |p| file.fnmatch?(p) }
15
+ # anchored patterns are significantly faster on large codebases
16
+ def cli_patterns
17
+ @patterns.map do |pattern|
18
+ if pattern.match?(%r{^([/~*]|\.{1,2}/)})
19
+ pattern
20
+ else
21
+ "/#{pattern}"
22
+ end
23
+ end
21
24
  end
22
25
 
23
26
  def each
24
- # TODO: handle no gitignore
25
27
  gitignore = ::File.join(Dir.pwd, '.gitignore')
26
28
  gitignore = nil unless ::File.exist?(gitignore)
27
- FastIgnore.new(rules: Spellr.config.ignored, gitignore: gitignore).each do |file|
29
+
30
+ FastIgnore.new(
31
+ ignore_rules: Spellr.config.excludes,
32
+ include_rules: Spellr.config.includes + cli_patterns,
33
+ gitignore: gitignore
34
+ ).each do |file|
28
35
  file = Spellr::File.new(file)
29
- next unless cli_only?(file)
30
- next unless config_only?(file)
31
36
 
32
37
  yield(file)
33
38
  end
@@ -61,6 +61,7 @@ module Spellr
61
61
  return unless global_skips.include?(token.to_s) ||
62
62
  global_insensitive_skips.include?(token.normalize)
63
63
 
64
+ puts "Automatically skipped #{red(token)}"
64
65
  self.total_skipped += 1
65
66
  end
66
67
 
@@ -71,7 +72,8 @@ module Spellr
71
72
 
72
73
  token.replace(global_replacement)
73
74
  self.total_fixed += 1
74
- raise Spellr::DidReplacement, token
75
+ puts "Automatically replaced #{red(token)} with #{green(global_replacement)}"
76
+ throw :check_file_from, token
75
77
  end
76
78
 
77
79
  def clear_current_line
@@ -112,6 +114,7 @@ module Spellr
112
114
  def handle_skip(token)
113
115
  self.total_skipped += 1
114
116
  yield token if block_given?
117
+ puts "Skipped #{red(token)}"
115
118
  end
116
119
 
117
120
  # TODO: handle more than 16 options
@@ -133,7 +136,8 @@ module Spellr
133
136
 
134
137
  wl.add(token)
135
138
  self.total_added += 1
136
- raise Spellr::DidAdd, token
139
+ puts "Added #{red(token)} to #{wl.name} wordlist"
140
+ throw :check_file_from, token
137
141
  else
138
142
  handle_add(token)
139
143
  end
@@ -152,7 +156,8 @@ module Spellr
152
156
  token.replace(full_replacement)
153
157
  yield replacement if block_given?
154
158
  self.total_fixed += 1
155
- raise Spellr::DidReplacement, token
159
+ puts "Replaced #{red(token.chomp)} with #{green(replacement.chomp)}"
160
+ throw :check_file_from, token
156
161
  end
157
162
  rescue Interrupt
158
163
  puts '^C again to exit'