spellr 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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'