danger-toc 0.1.3 → 0.3.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +103 -24
  5. data/lib/toc/config.rb +59 -24
  6. data/lib/toc/constructors/kramdown_constructor.rb +5 -6
  7. data/lib/toc/constructors.rb +0 -4
  8. data/lib/toc/extractor.rb +2 -1
  9. data/lib/toc/gem_version.rb +1 -1
  10. data/lib/toc/markdown_file.rb +62 -22
  11. data/lib/toc/plugin.rb +59 -37
  12. metadata +18 -163
  13. data/.gitignore +0 -11
  14. data/.rspec +0 -2
  15. data/.rubocop.yml +0 -14
  16. data/.rubocop_todo.yml +0 -86
  17. data/.travis.yml +0 -14
  18. data/CONTRIBUTING.md +0 -125
  19. data/Dangerfile +0 -4
  20. data/Gemfile +0 -5
  21. data/Guardfile +0 -19
  22. data/RELEASING.md +0 -67
  23. data/Rakefile +0 -23
  24. data/danger-toc.gemspec +0 -33
  25. data/images/toc-missing.png +0 -0
  26. data/spec/config_spec.rb +0 -66
  27. data/spec/constructors_spec.rb +0 -23
  28. data/spec/fixtures/markdown_file/one_section.md +0 -5
  29. data/spec/fixtures/markdown_file/one_section_with_custom_toc.md +0 -11
  30. data/spec/fixtures/markdown_file/one_section_with_invalid_toc.md +0 -11
  31. data/spec/fixtures/markdown_file/one_section_with_toc.md +0 -11
  32. data/spec/fixtures/markdown_file/one_section_with_toc_level.md +0 -11
  33. data/spec/fixtures/markdown_file/one_section_with_underscore_in_name.md +0 -11
  34. data/spec/fixtures/markdown_file/with_quoted_example.md +0 -26
  35. data/spec/markdown_file/danger_toc_readme_spec.rb +0 -19
  36. data/spec/markdown_file/one_section_spec.rb +0 -28
  37. data/spec/markdown_file/one_section_with_toc_level_spec.rb +0 -28
  38. data/spec/markdown_file/one_section_with_toc_spec.rb +0 -28
  39. data/spec/markdown_file/with_quoted_example_spec.rb +0 -33
  40. data/spec/markdown_file/with_underscore_in_section_name_spec.rb +0 -32
  41. data/spec/spec_helper.rb +0 -38
  42. data/spec/toc_spec.rb +0 -136
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f762724f89edff7e76c51e6ffae851d257b1f4f5ef2fd668a9c7e28c5a62c0f
4
- data.tar.gz: 9b9cd63986e67d85dda7a1067eb70abdd782ed27ad684ac72b781fb5145f5493
3
+ metadata.gz: 873eadcebb5c7ad1e01a9789251aaaf974f3852e6259d85cb95c4799c6d151b5
4
+ data.tar.gz: 4a852a1f1e5b173d136c47e022bd8037d7bdb40f0d09158460f8c53a0b39c1f1
5
5
  SHA512:
6
- metadata.gz: 18157fc64bfcc1efb3db474d650b771a314a90b15838bfe5d222a21ab344c18a80bb848b41f5623e1391f30921b3ae058f4f3c40e0076f5a5b7af2e840b02993
7
- data.tar.gz: 932ac02e3ebaac4ada45b08fe6353bcbf7bc84b66f87e44e4c35f799e0a881db35843f6c31dafa6f22fcf0631a6b3551c8ce25cacffde51f81eb5a5ee8c7c255
6
+ metadata.gz: 1f163608c603f0e539a9e26ed82580b0ecfd22b120fc055e9376811645527147d2d0f243535898d123737307b0cdc5e2423c09c9fc533cb07d392c1f7fb12595
7
+ data.tar.gz: 1b5b1e067a7491ba736f62b4cada1226cb814bcbbe14c43774f6733e2a3c8c4d199b88125fdbfda4de1ee07aa6e4460439677411b53e5ce7e159774681534d51
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## Changelog
2
2
 
3
+ ### 0.3.0 (2026-10-20)
4
+
5
+ * [#21](https://github.com/dblock/danger-toc/pull/21): Support every global TOC setting as a per-check keyword override without changing defaults; snapshot configuration and report every failing file - [@dblock](https://github.com/dblock).
6
+ * [#20](https://github.com/dblock/danger-toc/pull/20): Configure bullets, indentation, maximum heading depth, and input parsers; exclude document titles from missing TOCs and support opt-in diff reports - [@dblock](https://github.com/dblock).
7
+ * [#19](https://github.com/dblock/danger-toc/pull/19): Require Ruby 3.1, test through Ruby 4.0, declare the GFM parser dependency, and update pinned Danger workflows and contributor and release instructions - [@dblock](https://github.com/dblock).
8
+ * [#18](https://github.com/dblock/danger-toc/pull/18): Migrate to danger-pr-comment workflow - [@dblock](https://github.com/dblock).
9
+ * [#18](https://github.com/dblock/danger-toc/pull/18): Migrate from Travis CI to GitHub Actions - [@dblock](https://github.com/dblock).
10
+ * [#18](https://github.com/dblock/danger-toc/pull/18): Upgrade rubocop to 1.82.0 - [@dblock](https://github.com/dblock).
11
+
12
+ ### 0.2.0 (2019/07/28)
13
+
14
+ * [#16](https://github.com/dblock/danger-toc/pull/16): Deprecate `check` in favor of `check!` - [@dblock](https://github.com/dblock).
15
+ * [#16](https://github.com/dblock/danger-toc/pull/16): Deprecate `configure`, configure directly on `toc` - [@dblock](https://github.com/dblock).
16
+
3
17
  ### 0.1.3 (2018/08/15)
4
18
 
5
19
  * [#7](https://github.com/dblock/danger-toc/issues/7): Parse documents as Github Flavored Markdown by default - [@dblock](https://github.com/dblock).
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017 Daniel Doubrovkine <dblock@dblock.org>
1
+ Copyright (c) 2017-2019 Daniel Doubrovkine <dblock@dblock.org>
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,19 +1,25 @@
1
1
  # danger-toc
2
2
 
3
- A plugin for [danger.systems](http://danger.systems) that makes sure your project’s `README.md` has a TOC.
3
+ A plugin for [danger.systems](https://danger.systems) that makes sure your project's `README.md` has a TOC. Requires Ruby 3.1 or newer.
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/danger-toc.svg)](https://badge.fury.io/rb/danger-toc)
6
- [![Build Status](https://travis-ci.org/dblock/danger-toc.svg?branch=master)](https://travis-ci.org/dblock/danger-toc)
6
+ [![CI](https://github.com/dblock/danger-toc/actions/workflows/ci.yml/badge.svg)](https://github.com/dblock/danger-toc/actions/workflows/ci.yml)
7
7
 
8
8
  ## Table of Contents
9
9
 
10
10
  - [Installation](#installation)
11
11
  - [Usage](#usage)
12
- - [Configuration](#configuration)
13
- - [Methods and Attributes](#methods-and-attributes)
14
- - [toc.files](#tocfiles)
15
- - [toc.header](#tocheader)
16
- - [toc.check](#toccheck)
12
+ - [toc.check!](#toccheck)
13
+ - [Options](#options)
14
+ - [files](#files)
15
+ - [header](#header)
16
+ - [parser](#parser)
17
+ - [format](#format)
18
+ - [bullet](#bullet)
19
+ - [indent](#indent)
20
+ - [max_depth](#max_depth)
21
+ - [report](#report)
22
+ - [Global Defaults](#global-defaults)
17
23
  - [Contributing](#contributing)
18
24
  - [Copyright](#copyright)
19
25
 
@@ -22,41 +28,114 @@ A plugin for [danger.systems](http://danger.systems) that makes sure your projec
22
28
  Add `danger-toc` to your Gemfile.
23
29
 
24
30
  ```
25
- gem 'danger-toc', '~> 0.1'
31
+ gem 'danger-toc'
26
32
  ```
27
33
 
28
- Add `toc.check` to your Dangerfile. Make a pull request and see this plugin in action.
34
+ Add `toc.check!` to your Dangerfile. Make a pull request and see this plugin in action.
29
35
 
30
36
  <img src='images/toc-missing.png'>
31
37
 
32
38
  ## Usage
33
39
 
34
- ### Configuration
40
+ Methods from this plugin are available in your `Dangerfile` under the `toc` namespace. Every option can be set globally or passed to a check. Per-check options override global defaults without changing them.
35
41
 
36
- The plugin supports following configuration options:
42
+ ### toc.check!
43
+
44
+ Check README.md with default options, or pass options for a particular set of files:
45
+
46
+ ```ruby
47
+ toc.check!
48
+ toc.check!(files: ['README.md', 'DEV.md'], header: 'Contents')
49
+ ```
50
+
51
+ The method returns whether all checked TOCs are correct and reports every failing file. Use `toc.toc_changes?(files: ['README.md'])` to detect modified or added files before running a check.
52
+
53
+ ### Options
37
54
 
38
55
  ```ruby
39
- Danger::Toc.configure do |config|
40
- config.files = [ 'README.md' ]
41
- config.header = 'Table of Contents'
42
- end
56
+ toc.check!(files: ['README.md'], max_depth: 3)
57
+ toc.check!(files: ['docs/guide.md'], parser: 'Kramdown', format: :kramdown, bullet: '*', indent: 4)
43
58
  ```
44
59
 
45
- ### Methods and Attributes
60
+ #### files
46
61
 
47
- Methods and attributes from this plugin are available in your `Dangerfile` under the `toc` namespace.
62
+ File paths to check. Default: `['README.md']`.
48
63
 
49
- #### toc.files
64
+ ```ruby
65
+ toc.check!(files: ['README.md', 'DEV.md'])
66
+ ```
67
+
68
+ #### header
50
69
 
51
- List of files to ensure TOC in, default is `README.md`.
70
+ TOC heading text, without `#`. Default: `'Table of Contents'`.
71
+
72
+ ```ruby
73
+ toc.check!(header: 'Contents')
74
+ ```
75
+
76
+ When a TOC is missing, a leading `# Title` is excluded if every subsequent heading is nested below it. A lone heading or multiple top-level sections are retained. When a TOC exists, headings before it are excluded.
77
+
78
+ #### parser
79
+
80
+ Kramdown input parser name. Default: `'GFM'`. GFM and Kramdown input are available out of the box; install and require additional Kramdown parsers before selecting them.
81
+
82
+ ```ruby
83
+ toc.check!(parser: 'Kramdown')
84
+ ```
85
+
86
+ #### format
87
+
88
+ Anchor ID format: `:github` (default) or `:kramdown`. This is independent of the input parser.
89
+
90
+ ```ruby
91
+ toc.check!(parser: 'Kramdown', format: :kramdown)
92
+ ```
52
93
 
53
- #### toc.header
94
+ #### bullet
54
95
 
55
- The Table of Contents header, without `#`, default is `Table of Contents`.
96
+ List marker: `'-'` (default), `'*'`, or `'+'`.
56
97
 
57
- #### toc.check
98
+ ```ruby
99
+ toc.check!(bullet: '*')
100
+ ```
101
+
102
+ #### indent
103
+
104
+ Non-negative number of spaces per nesting level. Default: `2`.
105
+
106
+ ```ruby
107
+ toc.check!(indent: 4)
108
+ ```
109
+
110
+ #### max_depth
111
+
112
+ Maximum absolute Markdown heading level (1–6). Default: `nil`, including all levels. For example, `2` includes `#` and `##` headings but not `###`.
113
+
114
+ ```ruby
115
+ toc.check!(max_depth: 2)
116
+ ```
117
+
118
+ #### report
119
+
120
+ Report style: `:full` (default) shows the complete expected TOC. Use `:diff` to show mismatched entries with two surrounding entries of context. Removed lines are prefixed with `-`, additions with `+`. Missing TOCs always show the full suggestion.
121
+
122
+ ```ruby
123
+ toc.check!(report: :diff)
124
+ ```
125
+
126
+ ### Global Defaults
127
+
128
+ Set shared defaults on `toc`, then override individual settings for a particular check:
129
+
130
+ ```ruby
131
+ toc.files = ['README.md', 'DEV.md']
132
+ toc.header = 'Contents'
133
+ toc.check!
134
+ toc.check!(files: ['docs/guide.md'], header: 'Guide Contents', parser: 'Kramdown')
135
+ toc.check! # still checks README.md and DEV.md using the Contents header
136
+ ```
58
137
 
59
- Run all checks with defaults.
138
+ The same global/per-check choice applies to every option in the table, including `report`, `format`, and `max_depth`. Global settings are shared across plugin instances; each check captures its own immutable configuration snapshot.
60
139
 
61
140
  ## Contributing
62
141
 
@@ -64,6 +143,6 @@ See [CONTRIBUTING](CONTRIBUTING.md).
64
143
 
65
144
  ## Copyright
66
145
 
67
- Copyright (c) Daniel Doubrovkine, 2017
146
+ Copyright (c) Daniel Doubrovkine, 2017-2019
68
147
 
69
148
  MIT License, see [LICENSE](LICENSE.txt) for details.
data/lib/toc/config.rb CHANGED
@@ -1,46 +1,81 @@
1
1
  module Danger
2
2
  module Toc
3
- module Config
4
- extend self
3
+ class Config
4
+ DEFAULTS = {
5
+ files: ['README.md'].freeze,
6
+ header: 'Table of Contents',
7
+ format: :github,
8
+ parser: 'GFM',
9
+ bullet: '-',
10
+ indent: 2,
11
+ max_depth: nil,
12
+ report: :full
13
+ }.freeze
5
14
 
6
- attr_accessor :files, :header
7
- attr_writer :format
15
+ ATTRIBUTES = DEFAULTS.keys.freeze
16
+ DELEGATORS = (ATTRIBUTES + ATTRIBUTES.map { |name| :"#{name}=" }).freeze
8
17
 
9
- # Files to process
10
- def files=(value)
11
- @files = value
18
+ VALIDATORS = {
19
+ files: ->(value) { value.is_a?(Array) && value.all? { |file| file.is_a?(String) && !file.strip.empty? } },
20
+ header: ->(value) { value.is_a?(String) && !value.strip.empty? },
21
+ parser: ->(value) { value.is_a?(String) && !value.strip.empty? },
22
+ format: ->(value) { %i[github kramdown].include?(value) },
23
+ bullet: ->(value) { %w[- * +].include?(value) },
24
+ indent: ->(value) { value.is_a?(Integer) && value >= 0 },
25
+ max_depth: ->(value) { value.nil? || (value.is_a?(Integer) && (1..6).cover?(value)) },
26
+ report: ->(value) { %i[full diff].include?(value) }
27
+ }.freeze
28
+
29
+ attr_reader(*ATTRIBUTES)
30
+
31
+ ATTRIBUTES.each do |name|
32
+ define_method("#{name}=") do |value|
33
+ validate_option!(name, value)
34
+ instance_variable_set("@#{name}", snapshot(value))
35
+ end
12
36
  end
13
37
 
14
- # Table of Contents Header
15
- def header=(value)
16
- @header = value
38
+ def initialize(**options)
39
+ unknown = options.keys - ATTRIBUTES
40
+ raise ArgumentError, "Unknown TOC options: #{unknown.join(', ')}" unless unknown.empty?
41
+
42
+ DEFAULTS.merge(options).each { |name, value| public_send("#{name}=", value) }
17
43
  end
18
44
 
19
- def format
20
- @format ||= default_format
45
+ def with(**options)
46
+ self.class.new(**to_h, **options).freeze
21
47
  end
22
48
 
23
- def default_format
24
- :github
49
+ def to_h
50
+ ATTRIBUTES.to_h { |name| [name, public_send(name)] }
25
51
  end
26
52
 
27
53
  def reset
28
- self.files = ['README.md']
29
- self.header = 'Table of Contents'
30
- self.format = default_format
54
+ DEFAULTS.each { |name, value| public_send("#{name}=", value) }
31
55
  end
32
56
 
33
- reset
34
- end
57
+ private
35
58
 
36
- class << self
37
- def configure
38
- block_given? ? yield(Config) : Config
59
+ def validate_option!(name, value)
60
+ raise ArgumentError, "Invalid TOC #{name}: #{value.inspect}" unless VALIDATORS.fetch(name).call(value)
39
61
  end
40
62
 
41
- def config
42
- Config
63
+ def snapshot(value)
64
+ case value
65
+ when Array then value.map { |item| snapshot(item) }.freeze
66
+ when String then value.dup.freeze
67
+ else value
68
+ end
43
69
  end
44
70
  end
71
+
72
+ def self.config
73
+ @config ||= Config.new
74
+ end
75
+
76
+ def self.configure
77
+ warn '[DEPRECATION] `configure` is deprecated. Please directly configure the Danger plugin via `toc.xyz=` instead.'
78
+ block_given? ? yield(config) : config
79
+ end
45
80
  end
46
81
  end
@@ -6,6 +6,7 @@ module Danger
6
6
  class KramdownConstructor < Kramdown::Converter::Toc
7
7
  def flatten(el)
8
8
  return [] unless el.type == :toc
9
+
9
10
  result = []
10
11
  if el.value
11
12
  result << {
@@ -14,20 +15,18 @@ module Danger
14
15
  depth: el.value.options[:level]
15
16
  }
16
17
  end
17
- if el.children
18
- el.children.each do |child|
19
- result.concat(flatten(child))
20
- end
18
+ el.children&.each do |child|
19
+ result.concat(flatten(child))
21
20
  end
22
21
  result
23
22
  end
24
23
 
25
24
  def convert(el)
26
- toc = flatten(super(el))
25
+ toc = flatten(super)
27
26
  has_toc = false
28
27
  headers = []
29
28
  toc.each do |line|
30
- if !has_toc && line[:text] == Danger::Toc.config.header
29
+ if !has_toc && line[:text] == (@options[:toc_header] || 'Table of Contents')
31
30
  headers = [] # drop any headers prior to TOC
32
31
  has_toc = true
33
32
  else
@@ -7,10 +7,6 @@ module Danger
7
7
  def self.get(name)
8
8
  const_get "#{name.to_s.camelize}Constructor"
9
9
  end
10
-
11
- def self.current
12
- get Danger::Toc.config.format
13
- end
14
10
  end
15
11
  end
16
12
  end
data/lib/toc/extractor.rb CHANGED
@@ -8,10 +8,11 @@ module Danger
8
8
  @toc_start = nil
9
9
  @toc_end = nil
10
10
  @in_toc = false
11
+ @toc_header = options.fetch(:toc_header, 'Table of Contents')
11
12
  end
12
13
 
13
14
  def convert(el)
14
- if el.type == :header && el.options[:raw_text] == Danger::Toc.config.header
15
+ if el.type == :header && el.options[:raw_text] == @toc_header
15
16
  @in_toc = true
16
17
  @toc_start = el.options[:location]
17
18
  elsif el.type == :header
@@ -1,3 +1,3 @@
1
1
  module Toc
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -1,24 +1,25 @@
1
1
  require 'active_support/core_ext/string/inflections'
2
+ require 'kramdown-parser-gfm'
3
+ require 'diff/lcs'
2
4
 
3
5
  require_relative 'extractor'
4
6
  require_relative 'constructors'
7
+ require_relative 'config'
5
8
 
6
9
  module Danger
7
10
  module Toc
8
11
  class MarkdownFile
9
- attr_reader :filename
10
- attr_reader :exists
11
- attr_reader :toc
12
- attr_reader :headers
12
+ attr_reader :filename, :exists, :toc, :headers, :config
13
13
 
14
- def initialize(filename = 'README.md')
14
+ def initialize(filename = 'README.md', config: Danger::Toc.config.with)
15
+ @config = config
15
16
  @filename = filename
16
17
  @exists = File.exist?(filename)
17
- if @exists
18
- parse!
19
- reduce!
20
- validate!
21
- end
18
+ return unless @exists
19
+
20
+ parse!
21
+ reduce!
22
+ validate!
22
23
  end
23
24
 
24
25
  def exists?
@@ -40,27 +41,66 @@ module Danger
40
41
  def toc_from_headers
41
42
  headers.map do |header|
42
43
  [
43
- ' ' * header[:depth] * 2,
44
- "- [#{header[:text]}]",
44
+ ' ' * header[:depth] * config.indent,
45
+ "#{config.bullet} [#{header[:text]}]",
45
46
  "(##{header[:id]})"
46
47
  ].compact.join
47
48
  end
48
49
  end
49
50
 
51
+ def toc_diff
52
+ changes = Diff::LCS.sdiff(toc || [], toc_from_headers)
53
+ lines = []
54
+ previous = nil
55
+ diff_context(changes).each do |index|
56
+ lines << '...' if previous && index > previous + 1
57
+ lines.concat(diff_lines(changes[index]))
58
+ previous = index
59
+ end
60
+ lines.join("\n")
61
+ end
62
+
50
63
  private
51
64
 
65
+ def diff_context(changes)
66
+ changes.each_index.reject { |index| changes[index].action == '=' }.flat_map do |index|
67
+ (([index - 2, 0].max)..([index + 2, changes.length - 1].min)).to_a
68
+ end.uniq.sort
69
+ end
70
+
71
+ def diff_lines(change)
72
+ case change.action
73
+ when '!' then ["-#{change.old_element}", "+#{change.new_element}"]
74
+ when '-' then ["-#{change.old_element}"]
75
+ when '+' then ["+#{change.new_element}"]
76
+ else [" #{change.old_element}"]
77
+ end
78
+ end
79
+
52
80
  # Parse markdown file for TOC.
53
81
  def parse!
54
82
  md = File.read(filename)
55
- doc = Kramdown::Document.new(md, input: 'GFM')
83
+ doc = Kramdown::Document.new(md, input: config.parser)
84
+
85
+ extract_toc!(md, doc)
86
+ construct_headers!(doc)
87
+ end
56
88
 
57
- # extract toc
58
- toc_start, toc_end = Danger::Toc::Extractor.convert(doc.root).first
89
+ def extract_toc!(markdown, doc)
90
+ toc_start, toc_end = Danger::Toc::Extractor.convert(doc.root, toc_header: config.header).first
91
+ toc_end ||= markdown.lines.length + 1 if toc_start
59
92
  @has_toc = toc_start && toc_end
60
- @toc = md.split("\n")[toc_start, toc_end - toc_start - 1].reject(&:empty?) if @has_toc
93
+ @toc = markdown.split("\n")[toc_start, toc_end - toc_start - 1].reject(&:empty?) if @has_toc
94
+ end
61
95
 
62
- # construct toc
63
- @headers = Danger::Toc::Constructors.current.convert(doc.root).first
96
+ def construct_headers!(doc)
97
+ @headers = Danger::Toc::Constructors.get(config.format).convert(doc.root, toc_header: config.header).first
98
+ headers.shift if !@has_toc && document_title?
99
+ headers.select! { |header| header[:depth] <= config.max_depth } if config.max_depth
100
+ end
101
+
102
+ def document_title?
103
+ headers.length > 1 && headers.first[:depth] == 1 && headers.drop(1).all? { |header| header[:depth] > 1 }
64
104
  end
65
105
 
66
106
  def reduce!
@@ -68,10 +108,10 @@ module Danger
68
108
  headers.each do |header|
69
109
  min_depth = header[:depth] unless min_depth && min_depth < header[:depth]
70
110
  end
71
- if min_depth
72
- headers.each do |header|
73
- header[:depth] -= min_depth
74
- end
111
+ return unless min_depth
112
+
113
+ headers.each do |header|
114
+ header[:depth] -= min_depth
75
115
  end
76
116
  end
77
117
 
data/lib/toc/plugin.rb CHANGED
@@ -3,65 +3,87 @@ module Danger
3
3
  #
4
4
  # @example Run all checks on the default README.md.
5
5
  #
6
- # toc.check
6
+ # toc.check!
7
7
  #
8
- # @example Customize filenames and remind the requester to update TOCs when necessary.
8
+ # @example Customize files and remind the requester to update TOCs when necessary.
9
9
  #
10
- # toc.filenames = ['README.md']
11
- # toc.is_toc_correct?
10
+ # toc.check!(files: ['README.md'], max_depth: 3)
12
11
  #
13
12
  # @see dblock/danger-toc
14
13
  # @tags toc
15
14
 
16
15
  class DangerToc < Plugin
17
- # The toc filenames, defaults to `[README.md]`.
18
- # @return [Array]
19
- attr_accessor :filenames
16
+ extend Forwardable
20
17
 
21
- def initialize(dangerfile)
22
- @filenames = ['README.md']
23
- super
18
+ def_delegators 'Danger::Toc.config', *Danger::Toc::Config::DELEGATORS
19
+
20
+ # Run all checks.
21
+ # @param options [Hash] Per-check files, header, parser, format, bullet, indent, max_depth, and report settings.
22
+ # @return [boolean] Whether all files have correct TOCs.
23
+ def check!(**options)
24
+ config = Danger::Toc.config.with(**options)
25
+ config.files.map { |filename| check_file(filename, config) }.all?
24
26
  end
25
27
 
26
28
  # Run all checks.
27
- # @return [void]
28
- def check
29
- is_toc_correct?
29
+ # @return [boolean]
30
+ def check(**options)
31
+ warn '[DEPRECATION] `check` is deprecated. Please use `check!` instead.'
32
+ check!(**options)
30
33
  end
31
34
 
32
35
  # Has the README file been modified?
33
36
  # @return [boolean]
34
- def toc_changes?
35
- (git.modified_files & filenames).any? || (git.added_files & filenames).any?
37
+ def toc_changes?(**options)
38
+ files = Danger::Toc.config.with(**options).files
39
+ git.modified_files.intersect?(files) || git.added_files.intersect?(files)
36
40
  end
37
41
 
38
42
  # Is the TOC format correct?
39
43
  # @return [boolean]
40
- def is_toc_correct?
41
- filenames.all? do |filename|
42
- toc_file = Danger::Toc::MarkdownFile.new(filename)
43
- if !toc_file.exists?
44
- messaging.fail("The #{filename} file does not exist.", sticky: false)
45
- false
46
- elsif toc_file.good?
47
- true
48
- else
49
- markdown <<-MARKDOWN
50
- Here's the expected TOC for #{filename}:
44
+ def is_toc_correct?(**options)
45
+ check!(**options)
46
+ end
51
47
 
52
- ```markdown
53
- # #{Danger::Toc.config.header}
48
+ private
54
49
 
55
- #{toc_file.toc_from_headers.join("\n")}
56
- ```
57
- MARKDOWN
58
- if toc_file.has_toc?
59
- messaging.fail("The TOC found in #{filename} doesn't match the sections of the file.", sticky: false)
60
- else
61
- messaging.fail("The #{filename} file is missing a TOC.", sticky: false)
62
- end
63
- false
50
+ def check_file(filename, config)
51
+ toc_file = Danger::Toc::MarkdownFile.new(filename, config: config)
52
+ if !toc_file.exists?
53
+ messaging.fail("The #{filename} file does not exist.", sticky: false)
54
+ false
55
+ elsif toc_file.good?
56
+ true
57
+ else
58
+ report_toc(toc_file)
59
+ if toc_file.has_toc?
60
+ messaging.fail("The TOC found in #{filename} doesn't match the sections of the file.", sticky: false)
61
+ else
62
+ messaging.fail("The #{filename} file is missing a TOC.", sticky: false)
64
63
  end
64
+ false
65
+ end
66
+ end
67
+
68
+ def report_toc(toc_file)
69
+ if toc_file.config.report == :diff && toc_file.has_toc?
70
+ markdown <<~MARKDOWN
71
+ Here's the TOC diff for #{toc_file.filename} (- current, + expected):
72
+
73
+ ```diff
74
+ #{toc_file.toc_diff}
75
+ ```
76
+ MARKDOWN
77
+ else
78
+ markdown <<~MARKDOWN
79
+ Here's the expected TOC for #{toc_file.filename}:
80
+
81
+ ```markdown
82
+ # #{toc_file.config.header}
83
+
84
+ #{toc_file.toc_from_headers.join("\n")}
85
+ ```
86
+ MARKDOWN
65
87
  end
66
88
  end
67
89
  end