mdtoc 0.1.5 → 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.
- checksums.yaml +4 -4
- data/bin/mdtoc +1 -1
- data/lib/mdtoc/cli.rb +1 -1
- data/lib/mdtoc/markdown/fragment_generator.rb +50 -0
- data/lib/mdtoc/markdown/header.rb +15 -8
- data/lib/mdtoc/markdown/parser.rb +71 -12
- data/lib/mdtoc/node.rb +25 -16
- data/lib/mdtoc/version.rb +1 -1
- data/lib/mdtoc/writer.rb +16 -11
- metadata +56 -57
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c01cef42824ceeae479dc235cbe2c7565c2d6ef045074c8d55b34a821ee719d
|
|
4
|
+
data.tar.gz: c25c67f2bf841dd9e1e3bb6a632035693299e7a53367b5dd255ecfbc125adab2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 603bec1ab105a7438bb2d0c2e4fb0c7eead9c63b57bfe33ca9354b05458d435805ce614cd11549527749092f2cffdc1289691a177b3c86161bac714f877c3eb7
|
|
7
|
+
data.tar.gz: 6bb0b63c4217f899d2277ba35baf7966cd97219e88f111c7a842b535108c698ac0260c0cc1f2c7e24858c9464a9cac333ea1fa4a7244104b051ba3184ed326bd
|
data/bin/mdtoc
CHANGED
data/lib/mdtoc/cli.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Mdtoc
|
|
|
19
19
|
exit
|
|
20
20
|
end
|
|
21
21
|
parser_.on('-o', '--output PATH', 'Update a table of contents in the file at PATH')
|
|
22
|
-
parser_.on('-a', '--[no-]append',
|
|
22
|
+
parser_.on('-a', '--[no-]append', "Append to the --output file if a <!-- mdtoc --> tag isn't found")
|
|
23
23
|
parser_.on('-c', '--[no-]create', 'Create the --output file if it does not exist')
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'sorbet-runtime'
|
|
5
|
+
|
|
6
|
+
module Mdtoc
|
|
7
|
+
module Markdown
|
|
8
|
+
module FragmentGenerator
|
|
9
|
+
extend T::Sig
|
|
10
|
+
extend T::Helpers
|
|
11
|
+
|
|
12
|
+
interface!
|
|
13
|
+
|
|
14
|
+
sig { abstract.params(label: String).returns(String) }
|
|
15
|
+
def generate(label); end
|
|
16
|
+
|
|
17
|
+
class GitHub
|
|
18
|
+
extend T::Sig
|
|
19
|
+
include FragmentGenerator
|
|
20
|
+
|
|
21
|
+
sig { void }
|
|
22
|
+
def initialize
|
|
23
|
+
@counts = T.let(Hash.new(0), T::Hash[String, Integer])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { override.params(label: String).returns(String) }
|
|
27
|
+
def generate(label)
|
|
28
|
+
# GitHub's fragment generation:
|
|
29
|
+
# 1. Downcase
|
|
30
|
+
# 2. Replace spaces with dashes
|
|
31
|
+
# 3. Remove non-alphanumeric characters (keeping dashes, dots and underscores)
|
|
32
|
+
# 4. Collapse multiple dashes
|
|
33
|
+
# 5. Remove leading/trailing dashes and dots (common in many implementations)
|
|
34
|
+
fragment = label.downcase.tr(' ', '-').gsub(/[^\w.-]/, '')
|
|
35
|
+
fragment = fragment.gsub(/-+/, '-')
|
|
36
|
+
fragment = fragment.gsub(/^[.-]+|[.-]+$/, '')
|
|
37
|
+
|
|
38
|
+
count = @counts[fragment]
|
|
39
|
+
@counts[fragment] += 1
|
|
40
|
+
|
|
41
|
+
if count.positive?
|
|
42
|
+
"#{fragment}-#{count}"
|
|
43
|
+
else
|
|
44
|
+
fragment
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
4
|
require 'sorbet-runtime'
|
|
5
|
+
require_relative 'fragment_generator'
|
|
5
6
|
|
|
6
7
|
module Mdtoc
|
|
7
8
|
module Markdown
|
|
@@ -10,9 +11,8 @@ module Mdtoc
|
|
|
10
11
|
|
|
11
12
|
sig { params(depth: Integer, label: String, url: String).void }
|
|
12
13
|
def initialize(depth, label, url)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
14
|
+
raise ArgumentError, "Header depth must be >= 0, but was #{depth}" if depth.negative?
|
|
15
|
+
|
|
16
16
|
@depth = depth
|
|
17
17
|
@label = normalize_label(label)
|
|
18
18
|
@url = url
|
|
@@ -33,15 +33,22 @@ module Mdtoc
|
|
|
33
33
|
|
|
34
34
|
def normalize_label(label)
|
|
35
35
|
label = label.strip.tr("\t\n\r", '') # Remove whitespace characters other than spaces.
|
|
36
|
-
label.gsub(/\[(
|
|
36
|
+
label.gsub(/\[(.*?)\]\(.*?\)/, '\1') # Remove links
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
class HeaderWithFragment < Header
|
|
41
|
-
sig
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
sig do
|
|
42
|
+
params(
|
|
43
|
+
depth: Integer,
|
|
44
|
+
label: String,
|
|
45
|
+
url: String,
|
|
46
|
+
generator: FragmentGenerator
|
|
47
|
+
).void
|
|
48
|
+
end
|
|
49
|
+
def initialize(depth, label, url, generator:)
|
|
50
|
+
super(depth, label, url)
|
|
51
|
+
@url += "##{generator.generate(@label)}"
|
|
45
52
|
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
@@ -3,43 +3,102 @@
|
|
|
3
3
|
|
|
4
4
|
require 'sorbet-runtime'
|
|
5
5
|
require_relative 'header'
|
|
6
|
+
require_relative 'fragment_generator'
|
|
6
7
|
|
|
7
8
|
module Mdtoc
|
|
8
9
|
module Markdown
|
|
9
10
|
class Parser
|
|
10
11
|
extend T::Sig
|
|
11
12
|
|
|
12
|
-
sig
|
|
13
|
-
|
|
13
|
+
sig do
|
|
14
|
+
params(
|
|
15
|
+
depth: Integer,
|
|
16
|
+
url: String,
|
|
17
|
+
generator: FragmentGenerator
|
|
18
|
+
).void
|
|
19
|
+
end
|
|
20
|
+
def initialize(depth, url, generator: FragmentGenerator::GitHub.new)
|
|
14
21
|
@depth = depth
|
|
15
22
|
@url = url
|
|
23
|
+
@generator = generator
|
|
24
|
+
@in_code_block = T.let(false, T::Boolean)
|
|
25
|
+
@in_html_comment = T.let(false, T::Boolean)
|
|
16
26
|
end
|
|
17
27
|
|
|
18
28
|
sig { params(lines: T::Enumerable[String]).returns(T::Array[Header]) }
|
|
19
29
|
def headers(lines)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
@in_code_block = false
|
|
31
|
+
@in_html_comment = false
|
|
32
|
+
|
|
33
|
+
headers = T.let([], T::Array[Header])
|
|
34
|
+
prev_line = T.let(nil, T.nilable(String))
|
|
35
|
+
|
|
36
|
+
lines.each do |line|
|
|
37
|
+
stripped = line.strip
|
|
38
|
+
|
|
39
|
+
if skip_line?(line, stripped)
|
|
40
|
+
prev_line = line
|
|
41
|
+
next
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if line.start_with?('#')
|
|
45
|
+
headers << header(line)
|
|
46
|
+
elsif (h = process_setext_header(stripped, prev_line))
|
|
47
|
+
headers << h
|
|
27
48
|
end
|
|
28
|
-
next if skip || !line.start_with?('#')
|
|
29
49
|
|
|
30
|
-
|
|
50
|
+
prev_line = line
|
|
31
51
|
end
|
|
52
|
+
|
|
53
|
+
headers
|
|
32
54
|
end
|
|
33
55
|
|
|
34
56
|
private
|
|
35
57
|
|
|
58
|
+
sig { params(line: String, stripped: String).returns(T::Boolean) }
|
|
59
|
+
def skip_line?(line, stripped)
|
|
60
|
+
html_comment?(stripped) || code_block?(line)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
sig { params(stripped: String).returns(T::Boolean) }
|
|
64
|
+
def html_comment?(stripped)
|
|
65
|
+
if stripped.start_with?('<!--')
|
|
66
|
+
@in_html_comment = true unless stripped.end_with?('-->')
|
|
67
|
+
return true
|
|
68
|
+
elsif @in_html_comment && stripped.end_with?('-->')
|
|
69
|
+
@in_html_comment = false
|
|
70
|
+
return true
|
|
71
|
+
end
|
|
72
|
+
@in_html_comment
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
sig { params(line: String).returns(T::Boolean) }
|
|
76
|
+
def code_block?(line)
|
|
77
|
+
if line.start_with?('```') && !T.must(line[3..]).strip.end_with?('```')
|
|
78
|
+
@in_code_block = !@in_code_block
|
|
79
|
+
return true
|
|
80
|
+
end
|
|
81
|
+
@in_code_block
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
sig { params(stripped: String, prev_line: T.nilable(String)).returns(T.nilable(Header)) }
|
|
85
|
+
def process_setext_header(stripped, prev_line)
|
|
86
|
+
return nil unless prev_line && !prev_line.strip.empty?
|
|
87
|
+
|
|
88
|
+
if stripped.match?(/^=+$/)
|
|
89
|
+
HeaderWithFragment.new(@depth, prev_line.strip, @url, generator: @generator)
|
|
90
|
+
elsif stripped.match?(/^-+$/)
|
|
91
|
+
HeaderWithFragment.new(@depth + 1, prev_line.strip, @url, generator: @generator)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
36
95
|
sig { params(line: String).returns(HeaderWithFragment) }
|
|
37
96
|
def header(line)
|
|
38
97
|
m = T.must(line.strip.match(/^(#+)\s*(.*)$/))
|
|
39
98
|
num_hashes = m[1]&.count('#') || 1
|
|
40
99
|
depth = @depth + num_hashes - 1
|
|
41
100
|
label = m[2] || ''
|
|
42
|
-
HeaderWithFragment.new(depth, label, @url)
|
|
101
|
+
HeaderWithFragment.new(depth, label, @url, generator: @generator)
|
|
43
102
|
end
|
|
44
103
|
end
|
|
45
104
|
end
|
data/lib/mdtoc/node.rb
CHANGED
|
@@ -10,6 +10,7 @@ module Mdtoc
|
|
|
10
10
|
class Node
|
|
11
11
|
extend T::Helpers
|
|
12
12
|
extend T::Sig
|
|
13
|
+
|
|
13
14
|
abstract!
|
|
14
15
|
|
|
15
16
|
class << self
|
|
@@ -18,10 +19,9 @@ module Mdtoc
|
|
|
18
19
|
sig { params(path: String, depth: Integer).returns(Node) }
|
|
19
20
|
def for_path(path, depth = 0)
|
|
20
21
|
# Ensure that `path` is a relative path, so that all links are relative and therefore portable.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
File.directory?(path) ? DirNode.new(path, depth) : FileNode.new(path, depth)
|
|
22
|
+
pathname = Pathname.new(path)
|
|
23
|
+
pathname = pathname.relative_path_from(Dir.pwd) if pathname.absolute?
|
|
24
|
+
pathname.directory? ? DirNode.new(pathname, depth) : FileNode.new(pathname, depth)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
sig { params(paths: T::Array[String]).returns(String) }
|
|
@@ -32,7 +32,7 @@ module Mdtoc
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
sig { params(path:
|
|
35
|
+
sig { params(path: Pathname, depth: Integer).void }
|
|
36
36
|
def initialize(path, depth)
|
|
37
37
|
@path = path
|
|
38
38
|
@depth = depth
|
|
@@ -43,22 +43,30 @@ module Mdtoc
|
|
|
43
43
|
|
|
44
44
|
sig { returns(String) }
|
|
45
45
|
def label
|
|
46
|
-
|
|
46
|
+
@path.basename(@path.extname).to_s.gsub(/_+/, ' ').gsub(/\s+/, ' ').capitalize
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
class DirNode < Node
|
|
50
50
|
sig { override.returns(T::Array[Mdtoc::Markdown::Header]) }
|
|
51
51
|
def headers
|
|
52
|
-
readme_path = T.let(nil, T.nilable(
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
readme_path = T.let(nil, T.nilable(Pathname))
|
|
53
|
+
children = @path.children.reject do |child|
|
|
54
|
+
if child.basename.to_s.casecmp?('readme.md')
|
|
55
|
+
readme_path = child
|
|
56
|
+
true
|
|
57
|
+
else
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
child_headers = children.sort.flat_map do |child|
|
|
63
|
+
Node.for_path(child.to_s, @depth + 1).headers
|
|
64
|
+
end
|
|
65
|
+
|
|
58
66
|
return child_headers unless readme_path
|
|
59
67
|
|
|
60
68
|
# Include the headers from the README at the beginning.
|
|
61
|
-
readme_headers = FileNode.new(readme_path, @depth).headers
|
|
69
|
+
readme_headers = FileNode.new(T.must(readme_path), @depth).headers
|
|
62
70
|
readme_headers + child_headers
|
|
63
71
|
end
|
|
64
72
|
end
|
|
@@ -66,11 +74,12 @@ module Mdtoc
|
|
|
66
74
|
class FileNode < Node
|
|
67
75
|
sig { override.returns(T::Array[Mdtoc::Markdown::Header]) }
|
|
68
76
|
def headers
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
path_s = @path.to_s
|
|
78
|
+
parser = Markdown::Parser.new(@depth, path_s)
|
|
79
|
+
headers = parser.headers(@path.each_line)
|
|
71
80
|
return headers if headers[0]&.top_level?(@depth)
|
|
72
81
|
|
|
73
|
-
headers.unshift(Mdtoc::Markdown::Header.new(@depth, label,
|
|
82
|
+
headers.unshift(Mdtoc::Markdown::Header.new(@depth, label, path_s))
|
|
74
83
|
end
|
|
75
84
|
end
|
|
76
85
|
end
|
data/lib/mdtoc/version.rb
CHANGED
data/lib/mdtoc/writer.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: true
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'tempfile'
|
|
6
|
+
|
|
4
7
|
module Mdtoc
|
|
5
8
|
module Writer
|
|
6
9
|
COMMENT_BEGIN = '<!-- mdtoc -->'
|
|
@@ -13,8 +16,15 @@ module Mdtoc
|
|
|
13
16
|
def write(toc, path, append, create)
|
|
14
17
|
validate_path(path, create)
|
|
15
18
|
new_content = content(toc, path, append)
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
|
|
20
|
+
# Write to a temporary file and rename it to the target path to ensure atomic writing.
|
|
21
|
+
temp = Tempfile.new(File.basename(path), File.dirname(path))
|
|
22
|
+
begin
|
|
23
|
+
temp.write(new_content)
|
|
24
|
+
temp.close
|
|
25
|
+
FileUtils.mv(temp.path, path)
|
|
26
|
+
ensure
|
|
27
|
+
temp.close!
|
|
18
28
|
end
|
|
19
29
|
end
|
|
20
30
|
|
|
@@ -25,17 +35,12 @@ module Mdtoc
|
|
|
25
35
|
toc = "#{COMMENT_BEGIN}\n#{toc}\n#{COMMENT_END}"
|
|
26
36
|
|
|
27
37
|
begin
|
|
28
|
-
|
|
29
|
-
rescue
|
|
30
|
-
# If File.
|
|
38
|
+
old_content = File.read(path)
|
|
39
|
+
rescue StandardError
|
|
40
|
+
# If File.read failed because the file didn't exist, then we know that --create
|
|
31
41
|
# was specified due to the validation in validate_path.
|
|
32
42
|
return "#{toc}\n"
|
|
33
43
|
end
|
|
34
|
-
begin
|
|
35
|
-
old_content = T.must(f.read)
|
|
36
|
-
ensure
|
|
37
|
-
f.close
|
|
38
|
-
end
|
|
39
44
|
|
|
40
45
|
if Regexp.new(Regexp.escape(COMMENT_BEGIN), Regexp::IGNORECASE).match?(old_content)
|
|
41
46
|
return old_content.gsub(
|
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.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- andornaut
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-03-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '5'
|
|
19
|
+
version: '5.25'
|
|
20
20
|
type: :development
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '5'
|
|
26
|
+
version: '5.25'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -58,56 +58,42 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '1.50'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: rubocop-shopify
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: 1.0.4
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: 1.0.4
|
|
68
|
+
version: '1.50'
|
|
83
69
|
- !ruby/object:Gem::Dependency
|
|
84
70
|
name: rubocop-sorbet
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
86
72
|
requirements:
|
|
87
73
|
- - "~>"
|
|
88
74
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
75
|
+
version: 0.10.0
|
|
90
76
|
type: :development
|
|
91
77
|
prerelease: false
|
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
79
|
requirements:
|
|
94
80
|
- - "~>"
|
|
95
81
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
82
|
+
version: 0.10.0
|
|
97
83
|
- !ruby/object:Gem::Dependency
|
|
98
84
|
name: unparser
|
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
|
100
86
|
requirements:
|
|
101
87
|
- - "~>"
|
|
102
88
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: 0.
|
|
89
|
+
version: 0.6.0
|
|
104
90
|
type: :development
|
|
105
91
|
prerelease: false
|
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
93
|
requirements:
|
|
108
94
|
- - "~>"
|
|
109
95
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: 0.
|
|
96
|
+
version: 0.6.0
|
|
111
97
|
- !ruby/object:Gem::Dependency
|
|
112
98
|
name: sorbet-runtime
|
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -133,13 +119,13 @@ description: |
|
|
|
133
119
|
|
|
134
120
|
* [Ruby](https://www.ruby-lang.org/en/) (see [.ruby-version](./.ruby-version))
|
|
135
121
|
|
|
136
|
-
```
|
|
137
|
-
|
|
122
|
+
```bash
|
|
123
|
+
gem install mdtoc
|
|
138
124
|
```
|
|
139
125
|
|
|
140
126
|
## Usage
|
|
141
127
|
|
|
142
|
-
```
|
|
128
|
+
```bash
|
|
143
129
|
$ mdtoc --help
|
|
144
130
|
Usage: mdtoc [options] files or directories...
|
|
145
131
|
-h, --help Show this message
|
|
@@ -149,12 +135,15 @@ description: |
|
|
|
149
135
|
```
|
|
150
136
|
|
|
151
137
|
1. Add a `<!-- mdtoc -->` tag to a Markdown file.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
echo '<!-- mdtoc -->' >> README.md
|
|
152
141
|
```
|
|
153
|
-
|
|
154
|
-
```
|
|
142
|
+
|
|
155
143
|
2. Run `mdtoc` and specify input files or directories (eg. the "test/samples" directory) and an output file (eg. "README.md").
|
|
156
|
-
|
|
157
|
-
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
mdtoc -aco README.md test/samples
|
|
158
147
|
```
|
|
159
148
|
|
|
160
149
|
## Example Rakefile
|
|
@@ -162,29 +151,33 @@ description: |
|
|
|
162
151
|
Create a `Rakefile` with the contents below, then run
|
|
163
152
|
[`rake`](https://github.com/ruby/rake) to:
|
|
164
153
|
|
|
165
|
-
* `git
|
|
154
|
+
* `git pullgem push pkg/mdtoc-0.2.0.gem`
|
|
166
155
|
* `git add` any *.md files
|
|
167
156
|
* Run `mdtoc` to update the generated table of contents in the ./README.md file
|
|
168
157
|
* Git commit and push any changes
|
|
169
158
|
|
|
170
|
-
```
|
|
159
|
+
```ruby
|
|
171
160
|
task default: %w[mdtoc]
|
|
172
161
|
|
|
173
162
|
desc 'Update Markdown table of contents and push changes to the git repository'
|
|
174
|
-
task :mdtoc do
|
|
175
|
-
command = <<~
|
|
163
|
+
task :mdtoc do
|
|
164
|
+
command = <<~CMD
|
|
176
165
|
set -e
|
|
177
|
-
git pull
|
|
178
166
|
if [ -n "$(git diff --name-only --diff-filter=U)" ]; then
|
|
179
167
|
echo 'Error: conflicts exist' >&2
|
|
180
168
|
exit 1
|
|
181
169
|
fi
|
|
182
170
|
mdtoc --append --create --output README.md docs/
|
|
183
171
|
git add *.md **/*.md
|
|
184
|
-
git commit -
|
|
172
|
+
git commit -qm 'Update TOC' || true
|
|
173
|
+
git pull
|
|
185
174
|
git push
|
|
186
|
-
|
|
187
|
-
|
|
175
|
+
CMD
|
|
176
|
+
sh command, verbose: false do |ok, status|
|
|
177
|
+
unless ok
|
|
178
|
+
fail "Failed with status: #{status.exitstatus}"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
188
181
|
end
|
|
189
182
|
```
|
|
190
183
|
|
|
@@ -192,38 +185,42 @@ description: |
|
|
|
192
185
|
|
|
193
186
|
## Development
|
|
194
187
|
|
|
195
|
-
###
|
|
188
|
+
### Setup
|
|
196
189
|
|
|
197
190
|
Requirements:
|
|
198
191
|
|
|
199
192
|
* [Bundler](https://bundler.io/)
|
|
193
|
+
* [chruby](https://github.com/postmodern/chruby) (recommended)
|
|
200
194
|
|
|
201
|
-
```
|
|
202
|
-
#
|
|
203
|
-
|
|
195
|
+
```bash
|
|
196
|
+
# Setup development environment
|
|
197
|
+
bin/setup
|
|
204
198
|
```
|
|
205
199
|
|
|
206
|
-
###
|
|
200
|
+
### Tasks
|
|
207
201
|
|
|
208
|
-
```
|
|
202
|
+
```bash
|
|
209
203
|
# List rake tasks
|
|
210
204
|
$ rake -T
|
|
211
|
-
rake build
|
|
212
|
-
rake default
|
|
213
|
-
rake install
|
|
214
|
-
rake
|
|
215
|
-
rake
|
|
216
|
-
rake
|
|
217
|
-
|
|
218
|
-
rake sorbet # Run the Sorbet type checker
|
|
219
|
-
rake test # Run tests
|
|
220
|
-
|
|
205
|
+
rake build # Build gem into the pkg directory
|
|
206
|
+
rake default # Run the build, rubocop, sorbet and test tasks
|
|
207
|
+
rake install # Build and install gem into system gems
|
|
208
|
+
rake rubocop # Run RuboCop
|
|
209
|
+
rake sorbet # Run the Sorbet type checker
|
|
210
|
+
rake test # Run tests
|
|
211
|
+
```
|
|
221
212
|
# Run mdtoc with test inputs
|
|
222
213
|
$ ruby -Ilib bin/mdtoc test/samples
|
|
223
214
|
|
|
224
215
|
# Run mdtoc with test inputs, and write to a newly created output file
|
|
225
216
|
$ f=$(mktemp) && ruby -Ilib bin/mdtoc -aco ${f} test/samples ; cat ${f}
|
|
226
217
|
```
|
|
218
|
+
|
|
219
|
+
### Publishing
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
rake release
|
|
223
|
+
```
|
|
227
224
|
email:
|
|
228
225
|
executables:
|
|
229
226
|
- mdtoc
|
|
@@ -233,6 +230,7 @@ files:
|
|
|
233
230
|
- bin/mdtoc
|
|
234
231
|
- lib/mdtoc.rb
|
|
235
232
|
- lib/mdtoc/cli.rb
|
|
233
|
+
- lib/mdtoc/markdown/fragment_generator.rb
|
|
236
234
|
- lib/mdtoc/markdown/header.rb
|
|
237
235
|
- lib/mdtoc/markdown/parser.rb
|
|
238
236
|
- lib/mdtoc/node.rb
|
|
@@ -241,7 +239,8 @@ files:
|
|
|
241
239
|
homepage: https://github.com/andornaut/mdtoc
|
|
242
240
|
licenses:
|
|
243
241
|
- MIT
|
|
244
|
-
metadata:
|
|
242
|
+
metadata:
|
|
243
|
+
rubygems_mfa_required: 'true'
|
|
245
244
|
post_install_message:
|
|
246
245
|
rdoc_options: []
|
|
247
246
|
require_paths:
|
|
@@ -257,7 +256,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
257
256
|
- !ruby/object:Gem::Version
|
|
258
257
|
version: '0'
|
|
259
258
|
requirements: []
|
|
260
|
-
rubygems_version: 3.
|
|
259
|
+
rubygems_version: 3.4.10
|
|
261
260
|
signing_key:
|
|
262
261
|
specification_version: 4
|
|
263
262
|
summary: Read Markdown files and output a table of contents
|