@@ -33,20 +33,65 @@ class MarkdownToc {
33
33
const menus = [ "# Table of contents" , "" ] ;
34
34
let isCodeBlock = false ;
35
35
let topLevel = NaN ;
36
+ let previous = null ;
36
37
37
38
for ( let line of input . split ( "\n" ) ) {
38
39
39
- if ( line . startsWith ( "```" ) ) {
40
+ const trimmed = line . trim ( ) ;
41
+
42
+ if ( trimmed . startsWith ( "```" ) ) {
40
43
isCodeBlock = ! isCodeBlock ;
41
44
}
42
45
43
46
if ( isCodeBlock ) {
44
47
continue ;
45
48
}
46
49
47
- if ( line . startsWith ( "#" ) ) {
48
- const match = line . match ( / ( # + ) \s * ( .* ?) # * \s * $ / ) ;
49
- const level = match [ 1 ] . length ;
50
+ let level = NaN ;
51
+ let title = null ;
52
+
53
+ // Check for:
54
+ // 1. ATX-style headers: ## My Header
55
+ //
56
+ // 2. Setext-style headers:
57
+ // a) Level 1 header: My Header
58
+ // =========
59
+ //
60
+ // b) Level 2 header: My Header
61
+ // ---------
62
+ //
63
+ // Edge cases that do not count as headers:
64
+ // i) Horizontal rule ("Underline" preceded by empty line):
65
+ //
66
+ // Some paragraph 1
67
+ // <empty line>
68
+ // -----
69
+ // Some paragraph 2
70
+ //
71
+ // ii) Two or more horizontal rules:
72
+ //
73
+ // Some paragraph 1
74
+ //
75
+ // -----
76
+ // -----
77
+ // -----
78
+ // Some paragraph 2
79
+
80
+ if ( trimmed . startsWith ( "#" ) ) {
81
+ const match = trimmed . match ( / ( # + ) \s * ( .* ?) # * \s * $ / ) ;
82
+ level = match [ 1 ] . length ;
83
+ title = match [ 2 ] . trim ( ) ;
84
+ } else if ( previous != null && previous . length > 0 && trimmed . length > 0 ) {
85
+ if ( trimmed . match ( / [ ^ = ] / g) == null ) {
86
+ level = 1 ;
87
+ title = previous ;
88
+ } else if ( trimmed . match ( / [ ^ - ] / g) == null && previous . match ( / [ ^ - ] / g) != null ) {
89
+ level = 2 ;
90
+ title = previous ;
91
+ }
92
+ }
93
+
94
+ if ( ! isNaN ( level ) && title != null ) {
50
95
if ( isNaN ( topLevel ) ) {
51
96
topLevel = level ;
52
97
}
@@ -55,12 +100,15 @@ class MarkdownToc {
55
100
continue ;
56
101
}
57
102
58
- const title = match [ 2 ] . trim ( ) ;
59
103
const link = title . toLocaleLowerCase ( )
60
104
. replace ( / \s / g, "-" )
61
105
. replace ( / [ ^ A - Z a - z 0 - 9 - ] / g, "" ) ;
62
106
const menu = `${ " " . repeat ( level - topLevel ) } - [${ title } ](#${ link } )` ;
63
107
menus . push ( menu ) ;
108
+
109
+ previous = null ;
110
+ } else {
111
+ previous = trimmed ;
64
112
}
65
113
}
66
114
0 commit comments