mdtoc 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mdtoc.rb +2 -14
- data/lib/mdtoc/cli.rb +16 -14
- data/lib/mdtoc/markdown/header.rb +41 -0
- data/lib/mdtoc/{markdown.rb → markdown/parser.rb} +2 -34
- data/lib/mdtoc/node.rb +25 -15
- data/lib/mdtoc/version.rb +1 -1
- data/lib/mdtoc/writer.rb +7 -9
- metadata +99 -13
- data/test/samples/README.md +0 -18
- data/test/samples/a/c.md +0 -2
- data/test/samples/a/d/f.md +0 -1
- data/test/samples/a/e.md +0 -1
- data/test/samples/a/g/README.md +0 -1
- data/test/samples/a/g/h.md +0 -1
- data/test/samples/a/readme.md +0 -4
- data/test/test_markdown.rb +0 -127
- data/test/test_node.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4b54718ab7b2f7bbc8e90ae473318895665986e0a7408674670db03294f12d6e
|
4
|
+
data.tar.gz: f1b251a78ba55942f8c85d82b99f614d3b9368541b7dcb1f144d1094e293d01f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 247816469ed9798906c93e6ca0c58bd4b863d379795d9f776b71fc6c7d3835d7eb4ee890372a408ee04981658336a5f924156d85456e66d90793e0d58b7bd9e2
|
7
|
+
data.tar.gz: 2c10991bfa7f4b95896d468f803bae94b44899fc695c1113bd6fa6d7d90c784f4303939a7c2637ed70cd6335bf51f66688d6c932b03a6c0d284f5060cc009646
|
data/lib/mdtoc.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'tempfile'
|
6
6
|
require_relative 'mdtoc/cli'
|
7
|
-
require_relative 'mdtoc/markdown'
|
8
7
|
require_relative 'mdtoc/node'
|
9
8
|
require_relative 'mdtoc/writer'
|
10
9
|
|
@@ -14,8 +13,8 @@ module Mdtoc
|
|
14
13
|
|
15
14
|
sig { params(args: T::Array[String]).void }
|
16
15
|
def main(args)
|
17
|
-
options = Mdtoc::CLI
|
18
|
-
toc =
|
16
|
+
options = Mdtoc::CLI.parse(args)
|
17
|
+
toc = Mdtoc::Node.render(options.paths)
|
19
18
|
unless options.output
|
20
19
|
puts toc
|
21
20
|
return
|
@@ -23,17 +22,6 @@ module Mdtoc
|
|
23
22
|
|
24
23
|
Mdtoc::Writer.write(toc, T.must(options.output), options.append, options.create)
|
25
24
|
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
sig { params(paths: T::Array[String]).returns(String) }
|
30
|
-
def render_toc(paths)
|
31
|
-
paths
|
32
|
-
.map { |path| Mdtoc::Node.for_path(path).headers }
|
33
|
-
.flatten(1)
|
34
|
-
.map(&:to_s)
|
35
|
-
.join("\n")
|
36
|
-
end
|
37
25
|
end
|
38
26
|
end
|
39
27
|
|
data/lib/mdtoc/cli.rb
CHANGED
@@ -4,26 +4,14 @@
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'sorbet-runtime'
|
6
6
|
require 'tempfile'
|
7
|
-
require_relative 'markdown'
|
8
|
-
require_relative 'node'
|
9
|
-
require_relative 'writer'
|
10
7
|
|
11
8
|
module Mdtoc
|
12
9
|
module CLI
|
13
|
-
class
|
10
|
+
class << self
|
14
11
|
extend T::Sig
|
15
12
|
|
16
|
-
prop :append, T::Boolean, default: false
|
17
|
-
prop :create, T::Boolean, default: false
|
18
|
-
prop :output, T.nilable(String)
|
19
|
-
prop :paths, T::Array[String], default: []
|
20
|
-
|
21
|
-
def []=(key, val)
|
22
|
-
send("#{key}=", val)
|
23
|
-
end
|
24
|
-
|
25
13
|
sig { params(args: T::Array[String]).returns(Options) }
|
26
|
-
def
|
14
|
+
def parse(args)
|
27
15
|
parser = OptionParser.new do |parser_|
|
28
16
|
parser_.banner = "Usage: #{parser_.program_name} [options] files or directories..."
|
29
17
|
parser_.on('-h', '--help', 'Show this message') do
|
@@ -44,5 +32,19 @@ module Mdtoc
|
|
44
32
|
options
|
45
33
|
end
|
46
34
|
end
|
35
|
+
|
36
|
+
class Options < T::Struct
|
37
|
+
extend T::Sig
|
38
|
+
|
39
|
+
prop :append, T::Boolean, default: false
|
40
|
+
prop :create, T::Boolean, default: false
|
41
|
+
prop :output, T.nilable(String)
|
42
|
+
prop :paths, T::Array[String], default: []
|
43
|
+
|
44
|
+
sig { params(key: Symbol, val: T.untyped).returns(T.untyped) }
|
45
|
+
def []=(key, val)
|
46
|
+
send("#{key}=", val)
|
47
|
+
end
|
48
|
+
end
|
47
49
|
end
|
48
50
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'sorbet-runtime'
|
5
|
+
|
6
|
+
module Mdtoc
|
7
|
+
module Markdown
|
8
|
+
class Header
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { params(depth: Integer, label: String, url: String).void }
|
12
|
+
def initialize(depth, label, url)
|
13
|
+
if depth < 0
|
14
|
+
raise ArgumentError, "Header depth must be >= 0, but was #{depth}"
|
15
|
+
end
|
16
|
+
@depth = depth
|
17
|
+
@label = label.strip.gsub(/\s+/, ' ')
|
18
|
+
@url = url
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { returns(String) }
|
22
|
+
def to_s
|
23
|
+
prefix = ' ' * 2 * @depth
|
24
|
+
"#{prefix}* [#{@label}](#{@url})"
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { params(relative_to_depth: Integer).returns(T::Boolean) }
|
28
|
+
def top_level?(relative_to_depth)
|
29
|
+
@depth == relative_to_depth
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class HeaderWithFragment < Header
|
34
|
+
sig { params(depth: Integer, label: String, url: String).void }
|
35
|
+
def initialize(depth, label, url)
|
36
|
+
url = "#{url}##{label.strip.downcase.tr(' ', '-').gsub(/[^\w\-]/, '')}"
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -2,42 +2,10 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'sorbet-runtime'
|
5
|
+
require_relative 'header'
|
5
6
|
|
6
7
|
module Mdtoc
|
7
8
|
module Markdown
|
8
|
-
class Header
|
9
|
-
extend T::Sig
|
10
|
-
|
11
|
-
sig { params(depth: Integer, label: String, url: String).void }
|
12
|
-
def initialize(depth, label, url)
|
13
|
-
if depth < 0
|
14
|
-
raise ArgumentError, "Header depth must be >= 0, but was #{depth}"
|
15
|
-
end
|
16
|
-
@depth = depth
|
17
|
-
@label = label.strip.gsub(/\s+/, ' ')
|
18
|
-
@url = url
|
19
|
-
end
|
20
|
-
|
21
|
-
sig { params(relative_to_depth: Integer).returns(T::Boolean) }
|
22
|
-
def top_level?(relative_to_depth)
|
23
|
-
@depth == relative_to_depth
|
24
|
-
end
|
25
|
-
|
26
|
-
sig { returns(String) }
|
27
|
-
def to_s
|
28
|
-
prefix = ' ' * 2 * @depth
|
29
|
-
"#{prefix}* [#{@label}](#{@url})"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class HeaderWithFragment < Header
|
34
|
-
sig { params(depth: Integer, label: String, url: String).void }
|
35
|
-
def initialize(depth, label, url)
|
36
|
-
url = "#{url}##{label.downcase.strip.gsub(/ /, '-').gsub(/[^\w\-_ ]/, '')}"
|
37
|
-
super
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
9
|
class Parser
|
42
10
|
extend T::Sig
|
43
11
|
|
@@ -65,7 +33,7 @@ module Mdtoc
|
|
65
33
|
|
66
34
|
private
|
67
35
|
|
68
|
-
sig { params(line: String).returns(
|
36
|
+
sig { params(line: String).returns(HeaderWithFragment) }
|
69
37
|
def header(line)
|
70
38
|
m = T.must(line.strip.match(/^(#+)\s*(.*)$/))
|
71
39
|
num_hashes = m[1]&.count('#') || 1
|
data/lib/mdtoc/node.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
|
4
4
|
require 'pathname'
|
5
5
|
require 'sorbet-runtime'
|
6
|
-
require_relative 'markdown'
|
6
|
+
require_relative 'markdown/header'
|
7
|
+
require_relative 'markdown/parser'
|
7
8
|
|
8
9
|
module Mdtoc
|
9
10
|
class Node
|
@@ -11,13 +12,24 @@ module Mdtoc
|
|
11
12
|
extend T::Sig
|
12
13
|
abstract!
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
path
|
18
|
-
path =
|
19
|
-
|
20
|
-
|
15
|
+
class << self
|
16
|
+
extend T::Sig
|
17
|
+
|
18
|
+
sig { params(path: String, depth: Integer).returns(Node) }
|
19
|
+
def for_path(path, depth = 0)
|
20
|
+
# Ensure that `path` is a relative path, so that all links are relative and therefore portable.
|
21
|
+
path = Pathname.new(path)
|
22
|
+
path = path.relative_path_from(Dir.pwd) if path.absolute?
|
23
|
+
path = path.to_s
|
24
|
+
File.directory?(path) ? DirNode.new(path, depth) : FileNode.new(path, depth)
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { params(paths: T::Array[String]).returns(String) }
|
28
|
+
def render(paths)
|
29
|
+
paths
|
30
|
+
.flat_map { |path| for_path(path).headers }
|
31
|
+
.join("\n")
|
32
|
+
end
|
21
33
|
end
|
22
34
|
|
23
35
|
sig { params(path: String, depth: Integer).void }
|
@@ -26,13 +38,13 @@ module Mdtoc
|
|
26
38
|
@depth = depth
|
27
39
|
end
|
28
40
|
|
41
|
+
sig { abstract.returns(T::Array[Mdtoc::Markdown::Header]) }
|
42
|
+
def headers; end
|
43
|
+
|
29
44
|
sig { returns(String) }
|
30
45
|
def label
|
31
46
|
File.basename(@path, File.extname(@path)).gsub(/_+/, ' ').gsub(/\s+/, ' ').capitalize
|
32
47
|
end
|
33
|
-
|
34
|
-
sig { abstract.returns(T::Array[Mdtoc::Markdown::Header]) }
|
35
|
-
def headers; end
|
36
48
|
end
|
37
49
|
|
38
50
|
class DirNode < Node
|
@@ -41,11 +53,9 @@ module Mdtoc
|
|
41
53
|
readme_path = T.let(nil, T.nilable(String))
|
42
54
|
child_headers = Dir
|
43
55
|
.each_child(@path)
|
44
|
-
.
|
45
|
-
.reject { |path| readme_path = path if File.basename(path).downcase == 'readme.md' }
|
56
|
+
.reject { |path| readme_path = File.join(@path, path) if path.casecmp?('readme.md') }
|
46
57
|
.sort!
|
47
|
-
.
|
48
|
-
.flatten(1)
|
58
|
+
.flat_map { |path| Node.for_path(File.join(@path, path), @depth + 1).headers }
|
49
59
|
return child_headers unless readme_path
|
50
60
|
|
51
61
|
# Include the headers from the README at the beginning.
|
data/lib/mdtoc/version.rb
CHANGED
data/lib/mdtoc/writer.rb
CHANGED
@@ -28,7 +28,7 @@ module Mdtoc
|
|
28
28
|
f = File.open(path)
|
29
29
|
rescue
|
30
30
|
# If File.open failed because the file didn't exist, then we know that --create
|
31
|
-
# was specified due to the validation in
|
31
|
+
# was specified due to the validation in validate_path.
|
32
32
|
return "#{toc}\n"
|
33
33
|
end
|
34
34
|
begin
|
@@ -51,16 +51,14 @@ module Mdtoc
|
|
51
51
|
|
52
52
|
sig { params(path: String, create: T::Boolean).void }
|
53
53
|
def validate_path(path, create)
|
54
|
-
if path
|
55
|
-
|
56
|
-
|
57
|
-
warn("--output PATH \"#{path}\" is not a regular file")
|
58
|
-
exit
|
59
|
-
end
|
60
|
-
elsif !create
|
61
|
-
warn("--output PATH \"#{path}\" does not exist. Specify --create to create it.")
|
54
|
+
if File.exist?(path)
|
55
|
+
unless File.file?(path)
|
56
|
+
warn("--output PATH \"#{path}\" is not a regular file")
|
62
57
|
exit
|
63
58
|
end
|
59
|
+
elsif !create
|
60
|
+
warn("--output PATH \"#{path}\" does not exist. Specify --create to create it.")
|
61
|
+
exit
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mdtoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- andornaut
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-11-
|
11
|
+
date: 2020-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -122,7 +122,101 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
|
-
description:
|
125
|
+
description: |
|
126
|
+
# mdtoc - Markdown Table of Contents
|
127
|
+
|
128
|
+
Read Markdown files and output a table of contents.
|
129
|
+
|
130
|
+
## Installation
|
131
|
+
|
132
|
+
Requirements:
|
133
|
+
|
134
|
+
* [Ruby](https://www.ruby-lang.org/en/) (see [.ruby-version](./.ruby-version))
|
135
|
+
|
136
|
+
```
|
137
|
+
$ gem install mdtoc
|
138
|
+
```
|
139
|
+
|
140
|
+
## Usage
|
141
|
+
|
142
|
+
```
|
143
|
+
$ mdtoc --help
|
144
|
+
Usage: mdtoc [options] files or directories...
|
145
|
+
-h, --help Show this message
|
146
|
+
-o, --output PATH Update a table of contents in the file at PATH
|
147
|
+
-a, --[no-]append Append to the --output file if a <!-- mdtoc --> tag isn't found
|
148
|
+
-c, --[no-]create Create the --output file if it does not exist
|
149
|
+
```
|
150
|
+
|
151
|
+
1. Add a `<!-- mdtoc -->` tag to a Markdown file.
|
152
|
+
```
|
153
|
+
$ echo '<!-- mdtoc -->` >> README.md
|
154
|
+
```
|
155
|
+
2. Run `mdtoc` and specify input files or directories (eg. the "test/samples" directory) and an output file (eg. "README.md").
|
156
|
+
```
|
157
|
+
$ mdtoc -aco README.md test/samples
|
158
|
+
```
|
159
|
+
|
160
|
+
## Example Rakefile
|
161
|
+
|
162
|
+
Run the [rake](https://github.com/ruby/rake) "mdtoc" task to update a table of contents.
|
163
|
+
See [andornaut/til](https://github.com/andornaut/til) for an example.
|
164
|
+
|
165
|
+
```
|
166
|
+
task default: %w[mdtoc]
|
167
|
+
|
168
|
+
desc 'Update Markdown table of contents and push changes to the git repository'
|
169
|
+
task :mdtoc do |t|
|
170
|
+
command = <<~END
|
171
|
+
set -e
|
172
|
+
git pull
|
173
|
+
if [ -n "$(git diff --name-only --diff-filter=U)" ]; then
|
174
|
+
echo 'Error: conflicts exist' >&2
|
175
|
+
exit 1
|
176
|
+
fi
|
177
|
+
mdtoc --append --create --output README.md docs/
|
178
|
+
git add *.md **/*.md
|
179
|
+
git commit -m 'Update TOC'
|
180
|
+
git push
|
181
|
+
END
|
182
|
+
%x|#{command}|
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
## Development
|
187
|
+
|
188
|
+
### Installation
|
189
|
+
|
190
|
+
Requirements:
|
191
|
+
|
192
|
+
* [Bundler](https://bundler.io/)
|
193
|
+
|
194
|
+
```
|
195
|
+
# Install dependencies
|
196
|
+
$ bundle
|
197
|
+
```
|
198
|
+
|
199
|
+
### Usage
|
200
|
+
|
201
|
+
```
|
202
|
+
# List rake tasks
|
203
|
+
$ rake -T
|
204
|
+
rake build # Build mdtoc-0.0.2.gem into the pkg directory
|
205
|
+
rake default # Run the build, rubocop:auto_correct, sorbet and test tasks
|
206
|
+
rake install # Build and install mdtoc-0.0.2.gem into system gems
|
207
|
+
rake install:local # Build and install mdtoc-0.0.2.gem into system gems without...
|
208
|
+
rake release[remote] # Create tag v0.0.2 and build and push mdtoc-0.0.2.gem to ru...
|
209
|
+
rake rubocop # Run RuboCop
|
210
|
+
rake rubocop:auto_correct # Auto-correct RuboCop offenses
|
211
|
+
rake sorbet # Run the Sorbet type checker
|
212
|
+
rake test # Run tests
|
213
|
+
|
214
|
+
# Run mdtoc with test inputs
|
215
|
+
$ ruby -Ilib bin/mdtoc test/samples
|
216
|
+
|
217
|
+
# Run mdtoc with test inputs, and write to a newly created output file
|
218
|
+
$ f=$(mktemp) && ruby -Ilib bin/mdtoc -aco ${f} test/samples ; cat ${f}
|
219
|
+
```
|
126
220
|
email:
|
127
221
|
executables:
|
128
222
|
- mdtoc
|
@@ -132,19 +226,11 @@ files:
|
|
132
226
|
- bin/mdtoc
|
133
227
|
- lib/mdtoc.rb
|
134
228
|
- lib/mdtoc/cli.rb
|
135
|
-
- lib/mdtoc/markdown.rb
|
229
|
+
- lib/mdtoc/markdown/header.rb
|
230
|
+
- lib/mdtoc/markdown/parser.rb
|
136
231
|
- lib/mdtoc/node.rb
|
137
232
|
- lib/mdtoc/version.rb
|
138
233
|
- lib/mdtoc/writer.rb
|
139
|
-
- test/samples/README.md
|
140
|
-
- test/samples/a/c.md
|
141
|
-
- test/samples/a/d/f.md
|
142
|
-
- test/samples/a/e.md
|
143
|
-
- test/samples/a/g/README.md
|
144
|
-
- test/samples/a/g/h.md
|
145
|
-
- test/samples/a/readme.md
|
146
|
-
- test/test_markdown.rb
|
147
|
-
- test/test_node.rb
|
148
234
|
homepage: https://github.com/andornaut/mdtoc
|
149
235
|
licenses:
|
150
236
|
- MIT
|
data/test/samples/README.md
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# Title
|
2
|
-
intro text
|
3
|
-
# Ignore this non-title
|
4
|
-
# ignore this non title
|
5
|
-
```
|
6
|
-
ignore this multi-line code block
|
7
|
-
```
|
8
|
-
```ignore this inline block```
|
9
|
-
<!-- ignore this comment -->
|
10
|
-
## 2
|
11
|
-
### 3
|
12
|
-
|
13
|
-
## 2
|
14
|
-
text
|
15
|
-
|
16
|
-
#### 4
|
17
|
-
text
|
18
|
-
## 2
|
data/test/samples/a/c.md
DELETED
data/test/samples/a/d/f.md
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
## f 2
|
data/test/samples/a/e.md
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# e 1
|
data/test/samples/a/g/README.md
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# README 1 for g
|
data/test/samples/a/g/h.md
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# h 1
|
data/test/samples/a/readme.md
DELETED
data/test/test_markdown.rb
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
# typed: false
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "minitest/autorun"
|
5
|
-
require "mdtoc/markdown"
|
6
|
-
|
7
|
-
class TestHeader < Minitest::Test
|
8
|
-
def test_fragment_normalization
|
9
|
-
sample = [
|
10
|
-
'Spaces 1 space',
|
11
|
-
'Spaces 2 spaces',
|
12
|
-
"Spaces\t1 tab",
|
13
|
-
"Spaces\t\t2 tabs",
|
14
|
-
' Spaces leading and trailing ',
|
15
|
-
'Numbers 1234567890',
|
16
|
-
'Symbols -=~!@#$%^&',
|
17
|
-
'Symbols *()_+',
|
18
|
-
'Symbols <>?:"{}|[]\;\',./',
|
19
|
-
]
|
20
|
-
expecteds = [
|
21
|
-
'/a#spaces-1-space',
|
22
|
-
'/a#spaces--2-spaces',
|
23
|
-
'/a#spaces1-tab',
|
24
|
-
'/a#spaces2-tabs',
|
25
|
-
'/a#spaces-leading-and-trailing',
|
26
|
-
'/a#numbers-1234567890',
|
27
|
-
'/a#symbols--',
|
28
|
-
'/a#symbols-_',
|
29
|
-
'/a#symbols-',
|
30
|
-
]
|
31
|
-
actuals = sample.map do |label|
|
32
|
-
Mdtoc::Markdown::HeaderWithFragment.new(1, label, '/a').instance_variable_get(:@url)
|
33
|
-
end
|
34
|
-
|
35
|
-
expecteds.zip(actuals).each { |expected, actual| assert_equal(expected, actual) }
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_invalid_depth
|
39
|
-
assert_raises(ArgumentError) do
|
40
|
-
Mdtoc::Markdown::Header.new(-1, 'a', '/a')
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def test_label_normalization
|
45
|
-
sample = [
|
46
|
-
' strip ',
|
47
|
-
"squeeze internal \t spaces",
|
48
|
-
'Don\'t change "#1?|!@#$%^&*()+ 2--',
|
49
|
-
]
|
50
|
-
expecteds = [
|
51
|
-
'strip',
|
52
|
-
"squeeze internal spaces",
|
53
|
-
'Don\'t change "#1?|!@#$%^&*()+ 2--',
|
54
|
-
]
|
55
|
-
actuals = sample.map { |label| Mdtoc::Markdown::Header.new(0, label, '/a').instance_variable_get(:@label) }
|
56
|
-
|
57
|
-
expecteds.zip(actuals).each { |expected, actual| assert_equal(expected, actual) }
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_to_s_prefix
|
61
|
-
str = Mdtoc::Markdown::Header.new(3, 'a', '/a').to_s
|
62
|
-
|
63
|
-
assert_equal(' * [a](/a)', str)
|
64
|
-
end
|
65
|
-
|
66
|
-
def test_to_s_with_fragment
|
67
|
-
str = Mdtoc::Markdown::HeaderWithFragment.new(0, 'a', '/a').to_s
|
68
|
-
|
69
|
-
assert_equal('* [a](/a#a)', str)
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_to_s_without_fragment
|
73
|
-
str = Mdtoc::Markdown::Header.new(0, 'a', '/a').to_s
|
74
|
-
|
75
|
-
assert_equal('* [a](/a)', str)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
class TestParser < Minitest::Test
|
80
|
-
def test_skips_multiline_code_blocks
|
81
|
-
parser = Mdtoc::Markdown::Parser.new(0, '/')
|
82
|
-
sample = <<~END
|
83
|
-
# title
|
84
|
-
```
|
85
|
-
code
|
86
|
-
# code
|
87
|
-
```
|
88
|
-
END
|
89
|
-
|
90
|
-
headers = parser.headers(sample.each_line)
|
91
|
-
|
92
|
-
assert_equal(1, headers.size)
|
93
|
-
assert_equal('title', headers[0].instance_variable_get(:@label))
|
94
|
-
end
|
95
|
-
|
96
|
-
def test_skips_inline_code_blocks
|
97
|
-
parser = Mdtoc::Markdown::Parser.new(0, '/')
|
98
|
-
sample = <<~END
|
99
|
-
```code #```
|
100
|
-
# Title
|
101
|
-
```# code```
|
102
|
-
```code```#
|
103
|
-
END
|
104
|
-
|
105
|
-
headers = parser.headers(sample.each_line)
|
106
|
-
|
107
|
-
assert_equal(1, headers.size)
|
108
|
-
assert_equal(headers[0].instance_variable_get(:@label), 'Title')
|
109
|
-
end
|
110
|
-
|
111
|
-
def test_depth
|
112
|
-
parser = Mdtoc::Markdown::Parser.new(10, '/')
|
113
|
-
sample = <<~END
|
114
|
-
# 1
|
115
|
-
## 2
|
116
|
-
### 3
|
117
|
-
#### 4
|
118
|
-
END
|
119
|
-
|
120
|
-
headers = parser.headers(sample.each_line)
|
121
|
-
|
122
|
-
assert_equal(4, headers.size)
|
123
|
-
(0..3).each do |i|
|
124
|
-
assert_equal(10 + i, headers[i].instance_variable_get(:@depth))
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
data/test/test_node.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# typed: false
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require "minitest/autorun"
|
5
|
-
require "mdtoc/node"
|
6
|
-
|
7
|
-
class TestNode < Minitest::Test
|
8
|
-
SAMPLE_DIR = File.join(File.dirname(__FILE__), 'samples')
|
9
|
-
|
10
|
-
def test_dir
|
11
|
-
expected = <<~END
|
12
|
-
* [readme 1](test/samples/a/readme.md#readme-1)
|
13
|
-
* [readme 2](test/samples/a/readme.md#readme-2)
|
14
|
-
* [readme 3](test/samples/a/readme.md#readme-3)
|
15
|
-
* [readme 4](test/samples/a/readme.md#readme-4)
|
16
|
-
* [c 1](test/samples/a/c.md#c-1)
|
17
|
-
* [c 2](test/samples/a/c.md#c-2)
|
18
|
-
* [F](test/samples/a/d/f.md)
|
19
|
-
* [f 2](test/samples/a/d/f.md#f-2)
|
20
|
-
* [e 1](test/samples/a/e.md#e-1)
|
21
|
-
* [README 1 for g](test/samples/a/g/README.md#readme-1-for-g)
|
22
|
-
* [h 1](test/samples/a/g/h.md#h-1)
|
23
|
-
END
|
24
|
-
node = Mdtoc::Node.for_path(sample_path('a'))
|
25
|
-
actual = node.headers.join("\n") + "\n"
|
26
|
-
|
27
|
-
assert_equal(expected, actual)
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_file
|
31
|
-
expected = <<~END
|
32
|
-
* [Title](test/samples/README.md#title)
|
33
|
-
* [2](test/samples/README.md#2)
|
34
|
-
* [3](test/samples/README.md#3)
|
35
|
-
* [2](test/samples/README.md#2)
|
36
|
-
* [4](test/samples/README.md#4)
|
37
|
-
* [2](test/samples/README.md#2)
|
38
|
-
END
|
39
|
-
node = Mdtoc::Node.for_path(sample_path('README.md'))
|
40
|
-
actual = node.headers.join("\n") + "\n"
|
41
|
-
|
42
|
-
assert_equal(expected, actual)
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def sample_path(path)
|
48
|
-
File.join('test/samples', path)
|
49
|
-
end
|
50
|
-
end
|