Class: Danger::DangerProse

Inherits:
Plugin
  • Object
show all
Defined in:
lib/danger_plugin.rb

Overview

Lint markdown files inside your projects. This is done using the [proselint](proselint.com) python egg. Results are passed out as a table in markdown.

Examples:

Running linter with custom disabled linters


# Runs a linter with comma style and tense present disabled
prose.disable_linters = ["misc.scare_quotes", "misc.tense_present"]
prose.lint_files "_posts/*.md"

Running linter with default linters


# Runs a linter with all styles, on modified and added markdown files in this PR
prose.lint_files

Running the spell checker


# Runs a spell checker on all files in `_post`
prose.check_spelling "_posts/*.md"

Running the spell checker, with some words whitelisted


prose.ignored_words = ["orta", "artsy"]
prose.check_spelling

See Also:

  • artsy/artsyartsy/artsy.githubartsy/artsy.github.io

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#disable_lintersArray<String>

Allows you to disable a collection of linters from running. Doesn’t work yet. You can get a list of [them here](github.com/amperser/proselint#checks) defaults to ‘[“misc.scare_quotes”, “typography.symbols”]` when it’s nil.

Returns:

  • (Array<String>)


38
39
40
# File 'lib/danger_plugin.rb', line 38

def disable_linters
  @disable_linters
end

#ignore_acronymsObject

Allows you to specify that you want to ignore acronyms as spelling errors. Defaults to ‘false`, switch it to `true` if you wish to ignore acronyms.

Returns:

  • false



117
118
119
# File 'lib/danger_plugin.rb', line 117

def ignore_acronyms
  @ignore_acronyms
end

#ignore_numbersObject

Allows you to specify that you want to ignore reporting numbers as spelling errors. Defaults to ‘false`, switch it to `true` if you wish to ignore numbers.

Returns:

  • false



111
112
113
# File 'lib/danger_plugin.rb', line 111

def ignore_numbers
  @ignore_numbers
end

#ignored_wordsArray<String>

Allows you to add a collection of words to skip in spellchecking. defaults to ‘[“”]` when it’s nil.

Returns:

  • (Array<String>)


105
106
107
# File 'lib/danger_plugin.rb', line 105

def ignored_words
  @ignored_words
end

#languageObject

Allows you to specify dictionary language to use for spell-checking. Defaults to ‘en-gb`, switch to `en-us`, `en-au` or `es-es`, to override.



122
123
124
# File 'lib/danger_plugin.rb', line 122

def language
  @language
end

Instance Method Details

#check_spelling(files = nil) ⇒ void

This method returns an undefined value.

Runs a markdown-specific spell checker, against a corpus of ‘.markdown` and `.md` files.

Parameters:

  • files (String) (defaults to: nil)

    A globbed string which should return the files that you want to spell check, defaults to nil. if nil, modified and added files from the diff will be used.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/danger_plugin.rb', line 135

def check_spelling(files = nil)
  # Installs my fork of the spell checker if needed
  # my fork has line numbers + indexes
  system "npm install -g orta/node-markdown-spellcheck" unless mdspell_installed?

  # Check that this is in the user's PATH after installing
  raise "mdspell is not in the user's PATH, or it failed to install" unless mdspell_installed?

  markdown_files = get_files files

  arguments = ["-r"]
  skip_words = ignored_words || []

  arguments.push("-n") if ignore_numbers
  arguments.push("-a") if ignore_acronyms
  arguments.push("--#{language}")

  File.write(".spelling", skip_words.join("\n"))
  result_texts = Hash[markdown_files.to_a.uniq.collect { |md| [md, `mdspell #{md} #{arguments.join(" ")}`.strip] }]
  spell_issues = result_texts.select { |path, output| output.include? "spelling errors found" }
  File.unlink(".spelling")

  # Get some metadata about the local setup
  current_slug = env.ci_source.repo_slug

  if spell_issues.count > 0
    message = "### Spell Checker found issues\n\n"
    spell_issues.each do |path, output|
      github_loc = "/#{current_slug}/tree/#{github.branch_for_head}/#{path}"
      message << "#### [#{path}](#{github_loc})\n\n"

      message << "Line | Typo |\n"
      message << "| --- | ------ |\n"

      output.lines[1..-3].each do |line|
        index_info = line.strip.split("|").first
        index_line, index = index_info.split(":").map { |n| n.to_i }

        file = File.read(path)

        unknown_word = file[index..-1].split(" ").first

        error_text = line.strip.split("|")[1..-1].join("|").strip
        error = error_text.gsub(unknown_word, "**" + unknown_word + "**")

        message << "#{index_line} | #{error} | \n"
      end
      markdown message
    end
  end
end

#lint_files(files = nil) ⇒ void

This method returns an undefined value.

Lints the globbed markdown files. Will fail if ‘proselint` cannot be installed correctly. Generates a `markdown` list of warnings for the prose in a corpus of .markdown and .md files.

Parameters:

  • files (String) (defaults to: nil)

    A globbed string which should return the files that you want to lint, defaults to nil. if nil, modified and added files from the diff will be used.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/danger_plugin.rb', line 48

def lint_files(files = nil)
  # Installs a prose checker if needed
  system 'pip install --user proselint' unless proselint_installed?

  # Check that this is in the user's PATH after installing
  raise "proselint is not in the user's PATH, or it failed to install" unless proselint_installed?

  # Either use files provided, or use the modified + added
  markdown_files = get_files files

  proses = {}
  to_disable = disable_linters || ["misc.scare_quotes", "typography.symbols"]
  with_proselint_disabled(to_disable) do
    # Convert paths to proselint results
    result_jsons = Hash[markdown_files.to_a.uniq.collect { |v| [v, get_proselint_json(v)] }]
    proses = result_jsons.select { |_, prose| prose['data']['errors'].count > 0 }
  end

  # Get some metadata about the local setup
  current_slug = env.ci_source.repo_slug

  # We got some error reports back from proselint
  if proses.count > 0
    message = "### Proselint found issues\n\n"
    proses.each do |path, prose|
      github_loc = "/#{current_slug}/tree/#{github.branch_for_head}/#{path}"
      message << "#### [#{path}](#{github_loc})\n\n"

      message << "Line | Message | Severity |\n"
      message << "| --- | ----- | ----- |\n"

      prose['data']['errors'].each do |error|
        message << "#{error['line']} | #{error['message']} | #{error['severity']}\n"
      end
    end

    markdown message
  end
end

#mdspell_installed?Bool

Determine if mdspell is currently installed in the system paths.

Returns:

  • (Bool)


98
99
100
# File 'lib/danger_plugin.rb', line 98

def mdspell_installed?
  `which mdspell`.strip.empty? == false
end

#proselint_installed?Bool

Determine if proselint is currently installed in the system paths.

Returns:

  • (Bool)


91
92
93
# File 'lib/danger_plugin.rb', line 91

def proselint_installed?
  `which proselint`.strip.empty? == false
end