mdspell 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.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +44 -0
- data/bin/mdspell +10 -0
- data/lib/mdspell.rb +37 -0
- data/lib/mdspell/cli.rb +58 -0
- data/lib/mdspell/configuration.rb +8 -0
- data/lib/mdspell/spell_checker.rb +39 -0
- data/lib/mdspell/text_line.rb +113 -0
- data/lib/mdspell/typo.rb +44 -0
- data/lib/mdspell/version.rb +3 -0
- data/spec/examples.cache +39 -0
- data/spec/examples/simple.md +5 -0
- data/spec/examples/with_errors.md +5 -0
- data/spec/mdspell/spell_checker_spec.rb +70 -0
- data/spec/mdspell/text_line_spec.rb +111 -0
- data/spec/mdspell/typo_spec.rb +83 -0
- data/spec/spec_helper.rb +68 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 557ab534f964bbd18e0453ddcbe9561698441d5b
|
4
|
+
data.tar.gz: bb03a5b7f5a40b1e1fe689d383abd1a35a4ccaa8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 21066ee2957e17175a5c353dacd2d9aea1a0a7067ef83813fad211c4548705027867509cfed1a4195893840013e2e92986b9ab8a34ffe6f22c66c81bc3562299
|
7
|
+
data.tar.gz: c96d072fc81fb88d458807e34953dcae4c01f96d3ec997f8269e527025ee5f1b6b4f4216ce1a9ffc50875b25efdf26b97478e5510833b888cde9ddf0777ecb1f
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Marek Tuchowski
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# MdSpell
|
2
|
+
|
3
|
+
[](
|
4
|
+
https://travis-ci.org/mtuchowski/mdspell)
|
5
|
+
[](
|
6
|
+
https://codeclimate.com/github/mtuchowski/mdspell)
|
7
|
+
[](
|
8
|
+
https://codeclimate.com/github/mtuchowski/mdspell/coverage)
|
9
|
+
|
10
|
+
A Ruby markdown spell checking tool.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
To install from [rubygems.org](http://rubygems.org/), run:
|
15
|
+
|
16
|
+
```console
|
17
|
+
gem install mdspell
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
To spell-check markdown files, simply run `mdspell` with the filenames as a parameter:
|
23
|
+
|
24
|
+
```console
|
25
|
+
mdspell README.md
|
26
|
+
```
|
27
|
+
|
28
|
+
To check all markdown files within the directory:
|
29
|
+
|
30
|
+
```console
|
31
|
+
mdspell docs/
|
32
|
+
```
|
33
|
+
|
34
|
+
For each spelling error found, MdSpell will display the misspelled word, filename and line number:
|
35
|
+
|
36
|
+
```console
|
37
|
+
Spell-checking ./README.md...
|
38
|
+
./README.md:10: actualy
|
39
|
+
```
|
40
|
+
|
41
|
+
## MIT Licensed
|
42
|
+
|
43
|
+
See [LICENSE](https://github.com/mtuchowski/mdspell/blob/master/LICENSE) file for full license
|
44
|
+
text.
|
data/bin/mdspell
ADDED
data/lib/mdspell.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'mdspell/cli'
|
2
|
+
require_relative 'mdspell/configuration'
|
3
|
+
require_relative 'mdspell/spell_checker'
|
4
|
+
require_relative 'mdspell/text_line'
|
5
|
+
require_relative 'mdspell/typo'
|
6
|
+
require_relative 'mdspell/version'
|
7
|
+
|
8
|
+
require 'rainbow'
|
9
|
+
|
10
|
+
# This module holds all the MdSpell code (except mdspell shell command).
|
11
|
+
module MdSpell
|
12
|
+
def self.run
|
13
|
+
cli = MdSpell::CLI.new
|
14
|
+
cli.run
|
15
|
+
|
16
|
+
# Spell-check each file.
|
17
|
+
cli.files.each do |filename|
|
18
|
+
verbose "Spell-checking #{filename}..."
|
19
|
+
|
20
|
+
SpellChecker.new(filename).typos.each do |typo|
|
21
|
+
error "#{filename}:#{typo.line.location}: #{typo.word}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Private class methods
|
27
|
+
|
28
|
+
def self.verbose(str)
|
29
|
+
puts str if Configuration[:verbose]
|
30
|
+
end
|
31
|
+
private_class_method :verbose
|
32
|
+
|
33
|
+
def self.error(str)
|
34
|
+
puts Rainbow(str).red
|
35
|
+
end
|
36
|
+
private_class_method :error
|
37
|
+
end
|
data/lib/mdspell/cli.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'mixlib/cli'
|
2
|
+
|
3
|
+
module MdSpell
|
4
|
+
# Class responsible for parsing all of command line arguments.
|
5
|
+
class CLI
|
6
|
+
include Mixlib::CLI
|
7
|
+
|
8
|
+
banner "Usage: #{File.basename($PROGRAM_NAME)} [options] [FILE.md|DIR ...]"
|
9
|
+
|
10
|
+
option :config_file,
|
11
|
+
short: '-c',
|
12
|
+
long: '--config FILE',
|
13
|
+
description: 'The configuration file to use',
|
14
|
+
default: '~/.mdspell'
|
15
|
+
|
16
|
+
option :language,
|
17
|
+
short: '-l',
|
18
|
+
long: '--language LANG',
|
19
|
+
description: 'Set documents language',
|
20
|
+
default: 'en_US'
|
21
|
+
|
22
|
+
option :verbose,
|
23
|
+
short: '-v',
|
24
|
+
long: '--[no-]verbose',
|
25
|
+
description: 'Be more/less verbose',
|
26
|
+
boolean: true
|
27
|
+
|
28
|
+
option :version,
|
29
|
+
on: :tail,
|
30
|
+
short: '-V',
|
31
|
+
long: '--version',
|
32
|
+
description: 'Show version',
|
33
|
+
boolean: true,
|
34
|
+
proc: proc { puts MdSpell::VERSION },
|
35
|
+
exit: 0
|
36
|
+
|
37
|
+
def run(argv = ARGV)
|
38
|
+
parse_options(argv)
|
39
|
+
|
40
|
+
# Load optional config file if it's present.
|
41
|
+
config_filename = File.expand_path(config[:config_file])
|
42
|
+
MdSpell::Configuration.from_file(config_filename) if File.exist?(config_filename)
|
43
|
+
|
44
|
+
# Store command line configuration options.
|
45
|
+
MdSpell::Configuration.merge!(config)
|
46
|
+
end
|
47
|
+
|
48
|
+
# List of markdown files from argument list.
|
49
|
+
def files
|
50
|
+
cli_arguments.each_with_index do |filename, index|
|
51
|
+
if Dir.exist?(filename)
|
52
|
+
cli_arguments[index] = Dir["#{filename}/**/*.md"]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
cli_arguments.flatten!
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'configuration'
|
2
|
+
|
3
|
+
require 'kramdown'
|
4
|
+
require 'ffi/aspell'
|
5
|
+
|
6
|
+
module MdSpell
|
7
|
+
# A class for finding spelling errors in document.
|
8
|
+
class SpellChecker
|
9
|
+
# Name of the file this object was created from.
|
10
|
+
attr_reader :filename
|
11
|
+
|
12
|
+
# A Kramdown::Document object containing the parsed markdown document.
|
13
|
+
attr_reader :document
|
14
|
+
|
15
|
+
# Create a new instance from specified file.
|
16
|
+
# @param filename [String] a name of file to load.
|
17
|
+
def initialize(filename)
|
18
|
+
@filename = filename
|
19
|
+
@document = Kramdown::Document.new(File.read(filename), input: 'GFM')
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns found spelling errors.
|
23
|
+
def typos
|
24
|
+
results = []
|
25
|
+
|
26
|
+
FFI::Aspell::Speller.open(Configuration[:language]) do |speller|
|
27
|
+
TextLine.scan(document).each do |line|
|
28
|
+
line.words.each do |word|
|
29
|
+
unless speller.correct? word
|
30
|
+
results << Typo.new(line, word, speller.suggestions(word))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
results
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
|
3
|
+
module MdSpell
|
4
|
+
# Containing concatenated text from single document line.
|
5
|
+
class TextLine
|
6
|
+
# Accepted types of elements.
|
7
|
+
ELEMENT_TYPES = [:text, :smart_quote]
|
8
|
+
|
9
|
+
# Line number of the element in document.
|
10
|
+
attr_reader :location
|
11
|
+
|
12
|
+
# Textual content of the element.
|
13
|
+
attr_reader :content
|
14
|
+
|
15
|
+
# Create a new TextLine from Kramdown::Element object.
|
16
|
+
# @param element [Kramdown::Element]
|
17
|
+
def initialize(element)
|
18
|
+
TextLine.assert_element_type element
|
19
|
+
|
20
|
+
@content = ''
|
21
|
+
@location = element.options[:location]
|
22
|
+
self << element
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return all words in this element.
|
26
|
+
def words
|
27
|
+
@content.scan(/[\w\']+/)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Append Kramdown::Element's content to this element.
|
31
|
+
# @param element [Kramdown::Element]
|
32
|
+
# @raise ArgumentError if element is in different location.
|
33
|
+
def <<(element)
|
34
|
+
TextLine.assert_element_type element
|
35
|
+
|
36
|
+
return self unless ELEMENT_TYPES.include? element.type
|
37
|
+
return self unless element.options[:location] == @location
|
38
|
+
|
39
|
+
value = element.value
|
40
|
+
|
41
|
+
if element.type == :smart_quote
|
42
|
+
appent_quote(value)
|
43
|
+
else
|
44
|
+
append_text(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Scan Kramdown::Document for TextLines.
|
51
|
+
# @param document [Kramdown::Document]
|
52
|
+
def self.scan(document)
|
53
|
+
results = []
|
54
|
+
|
55
|
+
get_all_textual_elements(document.root.children).each do |element|
|
56
|
+
matching_element_found = results.any? do |text_element|
|
57
|
+
if text_element.location == element.options[:location]
|
58
|
+
text_element << element
|
59
|
+
true
|
60
|
+
else
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
results << TextLine.new(element) unless matching_element_found
|
66
|
+
end
|
67
|
+
|
68
|
+
results
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def append_text(value)
|
74
|
+
return if value.nil? || value.empty?
|
75
|
+
|
76
|
+
value = value.strip
|
77
|
+
if @content.empty? || @content[-1] == "'"
|
78
|
+
@content += value
|
79
|
+
else
|
80
|
+
@content += ' ' + value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def appent_quote(type)
|
85
|
+
case type
|
86
|
+
when :lsquo, :rsquo
|
87
|
+
@content += "'"
|
88
|
+
when :ldquo, :rdquo
|
89
|
+
@content += '"'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Private class methods
|
94
|
+
|
95
|
+
def self.assert_element_type(elem)
|
96
|
+
fail ArgumentError, 'expected Kramdown::Element' unless elem.instance_of? Kramdown::Element
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.get_all_textual_elements(elements)
|
100
|
+
result = []
|
101
|
+
|
102
|
+
elements.each do |element|
|
103
|
+
if ELEMENT_TYPES.include? element.type
|
104
|
+
result << element
|
105
|
+
else
|
106
|
+
result |= get_all_textual_elements(element.children)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
result
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/mdspell/typo.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module MdSpell
|
2
|
+
# A wrapper class for single, misspelled word.
|
3
|
+
class Typo
|
4
|
+
# A TextLine that contains this error.
|
5
|
+
attr_reader :line
|
6
|
+
|
7
|
+
# A misspelled word.
|
8
|
+
attr_reader :word
|
9
|
+
|
10
|
+
# A list of suggestions for this error.
|
11
|
+
attr_reader :suggestions
|
12
|
+
|
13
|
+
# Create a new SpellingError.
|
14
|
+
# @param line [TextLine] the TextLine that contains the error.
|
15
|
+
# @param word [String] the misspelled word.
|
16
|
+
# @param suggestions [Array] an array of suggestions for the word.
|
17
|
+
def initialize(line, word, suggestions)
|
18
|
+
assert_proper_line_type(line)
|
19
|
+
assert_proper_word_type(word)
|
20
|
+
assert_proper_suggestions_type(suggestions)
|
21
|
+
|
22
|
+
@line = line
|
23
|
+
@word = word
|
24
|
+
@suggestions = suggestions
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def assert_proper_line_type(line)
|
30
|
+
fail ArgumentError, "expected TextLine, got #{line.class.inspect}" unless
|
31
|
+
line.class == TextLine
|
32
|
+
end
|
33
|
+
|
34
|
+
def assert_proper_word_type(word)
|
35
|
+
fail ArgumentError, "expected String, got #{word.class.inspect}" unless
|
36
|
+
word.class == String
|
37
|
+
end
|
38
|
+
|
39
|
+
def assert_proper_suggestions_type(suggestions)
|
40
|
+
fail ArgumentError, "expected Array, got #{suggestions.class.inspect}" unless
|
41
|
+
suggestions.class == Array
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/spec/examples.cache
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
--------------------------------------------- | ------- | --------------- |
|
3
|
+
./spec/mdspell/spell_checker_spec.rb[1:1:1] | passed | 0.00141 seconds |
|
4
|
+
./spec/mdspell/spell_checker_spec.rb[1:1:2] | passed | 0.00088 seconds |
|
5
|
+
./spec/mdspell/spell_checker_spec.rb[1:1:3] | passed | 0.00055 seconds |
|
6
|
+
./spec/mdspell/spell_checker_spec.rb[1:2:1] | passed | 0.00014 seconds |
|
7
|
+
./spec/mdspell/spell_checker_spec.rb[1:2:2] | passed | 0.00048 seconds |
|
8
|
+
./spec/mdspell/spell_checker_spec.rb[1:2:3] | failed | 0.01636 seconds |
|
9
|
+
./spec/mdspell/spell_checker_spec.rb[1:3:1] | passed | 0.03322 seconds |
|
10
|
+
./spec/mdspell/spell_checker_spec.rb[1:3:2:1] | failed | 0.00015 seconds |
|
11
|
+
./spec/mdspell/spell_checker_spec.rb[1:4:1] | passed | 0.00181 seconds |
|
12
|
+
./spec/mdspell/spell_checker_spec.rb[1:5:1] | failed | 0.00789 seconds |
|
13
|
+
./spec/mdspell/spell_checker_spec.rb[1:5:2] | passed | 0.00703 seconds |
|
14
|
+
./spec/mdspell/spell_checker_spec.rb[1:5:3] | pending | 0.00001 seconds |
|
15
|
+
./spec/mdspell/text_line_spec.rb[1:1:1] | passed | 0.00007 seconds |
|
16
|
+
./spec/mdspell/text_line_spec.rb[1:1:2] | passed | 0.00008 seconds |
|
17
|
+
./spec/mdspell/text_line_spec.rb[1:1:3] | passed | 0.00008 seconds |
|
18
|
+
./spec/mdspell/text_line_spec.rb[1:1:4] | passed | 0.00009 seconds |
|
19
|
+
./spec/mdspell/text_line_spec.rb[1:2:1] | passed | 0.0004 seconds |
|
20
|
+
./spec/mdspell/text_line_spec.rb[1:3:1] | passed | 0.00008 seconds |
|
21
|
+
./spec/mdspell/text_line_spec.rb[1:4:1] | passed | 0.00009 seconds |
|
22
|
+
./spec/mdspell/text_line_spec.rb[1:5:1] | passed | 0.0001 seconds |
|
23
|
+
./spec/mdspell/text_line_spec.rb[1:6:1] | passed | 0.00014 seconds |
|
24
|
+
./spec/mdspell/text_line_spec.rb[1:6:2:1] | passed | 0.00008 seconds |
|
25
|
+
./spec/mdspell/text_line_spec.rb[1:6:3:1] | passed | 0.00011 seconds |
|
26
|
+
./spec/mdspell/text_line_spec.rb[1:6:3:2] | passed | 0.0001 seconds |
|
27
|
+
./spec/mdspell/text_line_spec.rb[1:6:4:1] | passed | 0.00009 seconds |
|
28
|
+
./spec/mdspell/text_line_spec.rb[1:7:1] | passed | 0.00012 seconds |
|
29
|
+
./spec/mdspell/text_line_spec.rb[1:7:2] | passed | 0.00086 seconds |
|
30
|
+
./spec/mdspell/typo_spec.rb[1:1:1] | passed | 0.00011 seconds |
|
31
|
+
./spec/mdspell/typo_spec.rb[1:1:2] | passed | 0.0001 seconds |
|
32
|
+
./spec/mdspell/typo_spec.rb[1:1:3] | passed | 0.00008 seconds |
|
33
|
+
./spec/mdspell/typo_spec.rb[1:2:1] | passed | 0.00012 seconds |
|
34
|
+
./spec/mdspell/typo_spec.rb[1:2:2] | passed | 0.00016 seconds |
|
35
|
+
./spec/mdspell/typo_spec.rb[1:2:3] | passed | 0.00014 seconds |
|
36
|
+
./spec/mdspell/typo_spec.rb[1:3:1] | passed | 0.00044 seconds |
|
37
|
+
./spec/mdspell/typo_spec.rb[1:4:1] | passed | 0.00007 seconds |
|
38
|
+
./spec/mdspell/typo_spec.rb[1:5:1] | passed | 0.00008 seconds |
|
39
|
+
./spec/mdspell/typo_spec.rb[1:5:2] | passed | 0.0001 seconds |
|
@@ -0,0 +1,70 @@
|
|
1
|
+
describe MdSpell::SpellChecker do
|
2
|
+
let(:simple) { MdSpell::SpellChecker.new('spec/examples/simple.md') }
|
3
|
+
let(:with_errors) { MdSpell::SpellChecker.new('spec/examples/with_errors.md') }
|
4
|
+
let(:from_stdin) do
|
5
|
+
allow(STDIN).to receive(:read) { '# Header' }
|
6
|
+
MdSpell::SpellChecker.new('-')
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'attributes' do
|
10
|
+
subject { simple }
|
11
|
+
|
12
|
+
it { is_expected.to respond_to :filename }
|
13
|
+
it { is_expected.to respond_to :document }
|
14
|
+
it { is_expected.to respond_to :typos }
|
15
|
+
end
|
16
|
+
|
17
|
+
context '#initialize' do
|
18
|
+
it 'should expect filename' do
|
19
|
+
expect { MdSpell::SpellChecker.new }.to raise_error ArgumentError
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should fail if given wrong filename' do
|
23
|
+
expect { MdSpell::SpellChecker.new('not_existing.md') }.to raise_error Errno::ENOENT
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should read from stdin if given '-' as filename" do
|
27
|
+
allow(STDIN).to receive(:read) { '# Header' }
|
28
|
+
expect { MdSpell::SpellChecker.new('-') }.not_to raise_error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '#filename' do
|
33
|
+
it 'should return proper path' do
|
34
|
+
expect(simple.filename).to eq 'spec/examples/simple.md'
|
35
|
+
expect(with_errors.filename).to eq 'spec/examples/with_errors.md'
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'if initialized from stdin' do
|
39
|
+
it "should return 'stdin'" do
|
40
|
+
expect(stdin_md.filename).to eq 'stdin'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context '#document' do
|
46
|
+
it 'should be of proper type' do
|
47
|
+
expect(simple.document).to be_instance_of Kramdown::Document
|
48
|
+
expect(with_errors.document).to be_instance_of Kramdown::Document
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context '#typos' do
|
53
|
+
it 'should return empty array if there are no errors' do
|
54
|
+
expect(simple.typos).to have(0).items
|
55
|
+
expect(from_stdin.typos).to have(0).items
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should return proper results' do
|
59
|
+
typos = with_errors.typos
|
60
|
+
|
61
|
+
expect(typos).to have(4).items
|
62
|
+
expect(typos[0].word).to eq 'mispelled'
|
63
|
+
expect(typos[1].word).to eq 'qiute'
|
64
|
+
expect(typos[2].word).to eq 'actualy'
|
65
|
+
expect(typos[3].word).to eq 'tobe'
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should use configured language'
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
describe MdSpell::TextLine do
|
2
|
+
let(:md_p_element) { Kramdown::Element.new(:p) }
|
3
|
+
let(:md_header_element) { Kramdown::Element.new(:header) }
|
4
|
+
let(:md_text_element) { Kramdown::Element.new(:text, "Joe's shop", nil, location: 42) }
|
5
|
+
let(:md_other_text_element) { Kramdown::Element.new(:text, 'text', nil, location: 42) }
|
6
|
+
let(:md_quote_element) { Kramdown::Element.new(:smart_quote, :lsquo, nil, location: 42) }
|
7
|
+
let(:md_empty_text_element) { Kramdown::Element.new(:text, nil, nil, location: 42) }
|
8
|
+
let(:md_empty_quote_element) { Kramdown::Element.new(:smart_quote, nil, nil, location: 42) }
|
9
|
+
let(:md_text_element_diff_location) { Kramdown::Element.new(:text, 'test', nil, location: 41) }
|
10
|
+
|
11
|
+
subject { MdSpell::TextLine.new(md_text_element) }
|
12
|
+
|
13
|
+
context 'attributes' do
|
14
|
+
it { is_expected.to respond_to :location }
|
15
|
+
it { is_expected.to respond_to :content }
|
16
|
+
it { is_expected.to respond_to :words }
|
17
|
+
it { is_expected.to respond_to :<< }
|
18
|
+
end
|
19
|
+
|
20
|
+
context '#initialize' do
|
21
|
+
it 'should expect Kramdown::Element as argument' do
|
22
|
+
expect { MdSpell::TextLine.new }.to raise_error ArgumentError
|
23
|
+
expect { MdSpell::TextLine.new('test') }.to raise_error ArgumentError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context '#location' do
|
28
|
+
it 'should return proper value' do
|
29
|
+
expect(subject.location).to eq 42
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context '#content' do
|
34
|
+
it 'should contain proper text' do
|
35
|
+
expect(subject.content).to eq "Joe's shop"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context '#words' do
|
40
|
+
it 'should return proper content' do
|
41
|
+
expect(subject.words).to eq ["Joe's", 'shop']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context '#<<' do
|
46
|
+
it 'should accept only Kramdown::Element' do
|
47
|
+
expect { subject << 'p_element' }.to raise_error ArgumentError
|
48
|
+
expect { subject << nil }.to raise_error ArgumentError
|
49
|
+
expect { subject << md_empty_text_element }.not_to raise_error
|
50
|
+
expect { subject << md_empty_quote_element }.not_to raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when given text element' do
|
54
|
+
it 'should update content' do
|
55
|
+
subject << md_other_text_element
|
56
|
+
expect(subject.content).to eq "Joe's shop text"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when given smart quote element' do
|
61
|
+
let(:lsquo_element) { Kramdown::Element.new(:smart_quote, :lsquo, nil, location: 42) }
|
62
|
+
let(:rsquo_element) { Kramdown::Element.new(:smart_quote, :rsquo, nil, location: 42) }
|
63
|
+
let(:ldquo_element) { Kramdown::Element.new(:smart_quote, :ldquo, nil, location: 42) }
|
64
|
+
let(:rdquo_element) { Kramdown::Element.new(:smart_quote, :rdquo, nil, location: 42) }
|
65
|
+
|
66
|
+
it 'should translate to content properly' do
|
67
|
+
subject << lsquo_element
|
68
|
+
expect(subject.content).to eq "Joe's shop'"
|
69
|
+
subject << ldquo_element
|
70
|
+
expect(subject.content).to eq "Joe's shop'\""
|
71
|
+
subject << rsquo_element
|
72
|
+
expect(subject.content).to eq "Joe's shop'\"'"
|
73
|
+
subject << rdquo_element
|
74
|
+
expect(subject.content).to eq "Joe's shop'\"'\""
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should properly concatenate words' do
|
78
|
+
subject << md_quote_element
|
79
|
+
subject << md_other_text_element
|
80
|
+
expect(subject.content).to eq "Joe's shop'text"
|
81
|
+
subject << md_other_text_element
|
82
|
+
expect(subject.content).to eq "Joe's shop'text text"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when given element from different location' do
|
87
|
+
it 'should not update content' do
|
88
|
+
subject << md_text_element_diff_location
|
89
|
+
expect(subject.content).to eq "Joe's shop"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context '::scan' do
|
95
|
+
let(:simple_md) { Kramdown::Document.new(File.read('spec/examples/simple.md'), input: 'GFM') }
|
96
|
+
|
97
|
+
it 'should expect Kramdown::Document as argument' do
|
98
|
+
expect { MdSpell::TextLine.scan }. to raise_error ArgumentError
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should find proper lines' do
|
102
|
+
lines = MdSpell::TextLine.scan(simple_md)
|
103
|
+
|
104
|
+
expect(lines).to have(3).items
|
105
|
+
|
106
|
+
expect(lines[0].content).to eq 'Simple'
|
107
|
+
expect(lines[1].content).to eq 'markdown'
|
108
|
+
expect(lines[2].content).to eq 'file'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
describe MdSpell::Typo do
|
2
|
+
let(:text_line) do
|
3
|
+
MdSpell::TextLine.new(Kramdown::Element.new(:text, 'black rabit', nil, location: 42))
|
4
|
+
end
|
5
|
+
let(:misspelled_word) { 'rabit' }
|
6
|
+
let(:suggested_word) { 'rabbit' }
|
7
|
+
|
8
|
+
subject { MdSpell::Typo.new(text_line, misspelled_word, [suggested_word]) }
|
9
|
+
|
10
|
+
context 'attributes' do
|
11
|
+
it { is_expected.to respond_to :line }
|
12
|
+
it { is_expected.to respond_to :word }
|
13
|
+
it { is_expected.to respond_to :suggestions }
|
14
|
+
end
|
15
|
+
|
16
|
+
context '#initialize' do
|
17
|
+
it 'should expect TextLine as first argument' do
|
18
|
+
expect do
|
19
|
+
MdSpell::Typo.new(nil, misspelled_word, [suggested_word])
|
20
|
+
end.to raise_error ArgumentError, 'expected TextLine, got NilClass'
|
21
|
+
|
22
|
+
expect do
|
23
|
+
MdSpell::Typo.new('black rabit', misspelled_word, [suggested_word])
|
24
|
+
end.to raise_error ArgumentError, 'expected TextLine, got String'
|
25
|
+
|
26
|
+
expect do
|
27
|
+
MdSpell::Typo.new(text_line, misspelled_word, [suggested_word])
|
28
|
+
end.not_to raise_error
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should expect word as second argument' do
|
32
|
+
expect do
|
33
|
+
MdSpell::Typo.new(text_line, nil, [suggested_word])
|
34
|
+
end.to raise_error ArgumentError, 'expected String, got NilClass'
|
35
|
+
|
36
|
+
expect do
|
37
|
+
MdSpell::Typo.new(text_line, :c, [suggested_word])
|
38
|
+
end.to raise_error ArgumentError, 'expected String, got Symbol'
|
39
|
+
|
40
|
+
expect do
|
41
|
+
MdSpell::Typo.new(text_line, misspelled_word, [suggested_word])
|
42
|
+
end.not_to raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should expect array as third argument' do
|
46
|
+
expect do
|
47
|
+
MdSpell::Typo.new(text_line, misspelled_word, nil)
|
48
|
+
end.to raise_error ArgumentError, 'expected Array, got NilClass'
|
49
|
+
|
50
|
+
expect do
|
51
|
+
MdSpell::Typo.new(text_line, misspelled_word, 'a')
|
52
|
+
end.to raise_error ArgumentError, 'expected Array, got String'
|
53
|
+
|
54
|
+
expect { MdSpell::Typo.new(text_line, misspelled_word, []) }.not_to raise_error
|
55
|
+
expect do
|
56
|
+
MdSpell::Typo.new(text_line, misspelled_word, [suggested_word])
|
57
|
+
end.not_to raise_error
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context '#line' do
|
62
|
+
it 'should return proper object' do
|
63
|
+
expect(subject.line).to be text_line
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context '#word' do
|
68
|
+
it 'should return proper word' do
|
69
|
+
expect(subject.word).to eq misspelled_word
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context '#suggestions' do
|
74
|
+
it 'should be an array' do
|
75
|
+
expect(subject.suggestions).to be_instance_of Array
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should return proper items' do
|
79
|
+
expect(subject.suggestions).to have(1).item
|
80
|
+
expect(subject.suggestions).to eq [suggested_word]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Conventionally, all specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
2
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this file to always
|
3
|
+
# be loaded, without a need to explicitly require it in any files.
|
4
|
+
#
|
5
|
+
# Given that it is always loaded, keep this file as light-weight as possible. Requiring heavyweight
|
6
|
+
# dependencies from this file will add to the boot time of test suite on EVERY test run, even for
|
7
|
+
# an individual file that may not need all of that loaded. Instead, consider making a separate
|
8
|
+
# helper file that requires the additional dependencies and performs the additional setup,
|
9
|
+
# and require it from the spec files that actually need it.
|
10
|
+
#
|
11
|
+
# The `.rspec` file also contains a few flags that are not defaults but that are commonly wanted.
|
12
|
+
#
|
13
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
14
|
+
|
15
|
+
# Setup Code Climate test reporter.
|
16
|
+
require 'codeclimate-test-reporter'
|
17
|
+
|
18
|
+
CodeClimate::TestReporter.start
|
19
|
+
|
20
|
+
# For checking arrays.
|
21
|
+
require 'rspec/collection_matchers'
|
22
|
+
|
23
|
+
# The whole lib is not that big, requiring it here cleans up the specs a little bit.
|
24
|
+
require 'mdspell'
|
25
|
+
|
26
|
+
RSpec.configure do |config|
|
27
|
+
# rspec-expectations config goes here.
|
28
|
+
config.expect_with :rspec do |expectations|
|
29
|
+
# This option will default to `true` in RSpec 4. It makes the `description` and
|
30
|
+
# `failure_message` of custom matchers include text for helper methods defined using
|
31
|
+
# `chain`, e.g.:
|
32
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
33
|
+
# # => "be bigger than 2 and smaller than 4"
|
34
|
+
# ...rather than:
|
35
|
+
# # => "be bigger than 2"
|
36
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
37
|
+
|
38
|
+
# Only allow expect syntax.
|
39
|
+
expectations.syntax = :expect
|
40
|
+
end
|
41
|
+
|
42
|
+
# rspec-mocks config goes here.
|
43
|
+
config.mock_with :rspec do |mocks|
|
44
|
+
# Prevents from mocking or stubbing a method that does not exist on a real object.
|
45
|
+
# This is generally recommended, and will default to `true` in RSpec 4.
|
46
|
+
mocks.verify_partial_doubles = true
|
47
|
+
end
|
48
|
+
|
49
|
+
# Allows RSpec to persist some state between runs in order to support the `--only-failures` and
|
50
|
+
# `--next-failure` CLI options. Source control system should be configured to ignore this file.
|
51
|
+
config.example_status_persistence_file_path = 'spec/examples.cache'
|
52
|
+
|
53
|
+
# Run specs in random order to surface order dependencies. To debug an order dependency after
|
54
|
+
# finding one, fix the order by providing the seed, which is printed after each run.
|
55
|
+
# --seed 1234
|
56
|
+
config.order = :random
|
57
|
+
|
58
|
+
# Seed global randomization in this process using the `--seed` CLI option. Setting this allows
|
59
|
+
# to use `--seed` to deterministically reproduce test failures related to randomization
|
60
|
+
# by passing the same `--seed` value as the one that triggered the failure.
|
61
|
+
Kernel.srand config.seed
|
62
|
+
|
63
|
+
# Allow filtering specs with `focus: true`.
|
64
|
+
config.filter_run focus: true
|
65
|
+
|
66
|
+
# Run all specs if none are filtered.
|
67
|
+
config.run_all_when_everything_filtered = true
|
68
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mdspell
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marek Tuchowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: kramdown
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ffi-aspell
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
description: Check markdown files for spelling errors.
|
42
|
+
email: marek@tuchowski.com.pl
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files:
|
46
|
+
- README.md
|
47
|
+
- LICENSE
|
48
|
+
files:
|
49
|
+
- LICENSE
|
50
|
+
- README.md
|
51
|
+
- bin/mdspell
|
52
|
+
- lib/mdspell.rb
|
53
|
+
- lib/mdspell/cli.rb
|
54
|
+
- lib/mdspell/configuration.rb
|
55
|
+
- lib/mdspell/spell_checker.rb
|
56
|
+
- lib/mdspell/text_line.rb
|
57
|
+
- lib/mdspell/typo.rb
|
58
|
+
- lib/mdspell/version.rb
|
59
|
+
- spec/examples.cache
|
60
|
+
- spec/examples/simple.md
|
61
|
+
- spec/examples/with_errors.md
|
62
|
+
- spec/mdspell/spell_checker_spec.rb
|
63
|
+
- spec/mdspell/text_line_spec.rb
|
64
|
+
- spec/mdspell/typo_spec.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
homepage: https://github.com/mtuchowski/mdspell
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options:
|
72
|
+
- "--charset"
|
73
|
+
- UTF-8
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 2.0.0
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 2.4.8
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: A Ruby markdown spell checking tool.
|
92
|
+
test_files:
|
93
|
+
- spec/spec_helper.rb
|
94
|
+
- spec/examples/with_errors.md
|
95
|
+
- spec/examples/simple.md
|
96
|
+
- spec/mdspell/text_line_spec.rb
|
97
|
+
- spec/mdspell/typo_spec.rb
|
98
|
+
- spec/mdspell/spell_checker_spec.rb
|
99
|
+
- spec/examples.cache
|
100
|
+
has_rdoc:
|