@@ -90,6 +90,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
90
90
, textRE = / ^ [ ^ # ! \[ \] * _ \\ < > ` " ' ( ~ : ] + /
91
91
, fencedCodeRE = new RegExp ( "^(" + ( modeCfg . fencedCodeBlocks === true ? "~~~+|```+" : modeCfg . fencedCodeBlocks ) +
92
92
")[ \\t]*([\\w+#\-]*)" )
93
+ , linkDefRE = / ^ \s * \[ [ ^ \] ] + ?\] : \s * \S + ( \s * \S * \s * ) ? $ / // naive link-definition
93
94
, punctuation = / [ ! \" # $ % & \' ( ) * + , \- \. \/ : ; < = > ? @ \[ \\ \] ^ _ ` { | } ~ — ] /
94
95
, expandedTab = " " // CommonMark specifies tab as 4 spaces
95
96
@@ -110,6 +111,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
110
111
// Blocks
111
112
112
113
function blankLine ( state ) {
114
+ state . hr = false ;
113
115
// Reset linkTitle state
114
116
state . linkTitle = false ;
115
117
// Reset EM state
@@ -137,16 +139,18 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
137
139
138
140
function blockNormal ( stream , state ) {
139
141
var sol = stream . sol ( ) ;
142
+ var prevLineLineIsEmpty = lineIsEmpty ( state . prevLine ) ;
143
+ var prevLineIsIndentedCode = state . indentedCode ;
144
+ var prevLineIsHr = state . hr ;
145
+ var prevLineIsList = state . list !== false ;
146
+ var maxNonCodeIndentation = ( state . listStack [ state . listStack . length - 1 ] || 0 ) + 3 ;
140
147
141
- var prevLineIsList = state . list !== false ,
142
- prevLineIsIndentedCode = state . indentedCode ;
143
-
148
+ state . hr = false ;
144
149
state . indentedCode = false ;
145
150
146
- var lineIndentation ;
151
+ var lineIndentation = state . indentation ;
147
152
// compute once per line (on first token)
148
153
if ( state . indentationDiff === null ) {
149
- lineIndentation = state . indentation ;
150
154
state . indentationDiff = state . indentation ;
151
155
if ( prevLineIsList ) {
152
156
state . list = null ;
@@ -168,8 +172,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
168
172
}
169
173
}
170
174
175
+ var isHr = ( state . list === false || prevLineIsHr || prevLineLineIsEmpty ) &&
176
+ state . indentation <= maxNonCodeIndentation && stream . match ( hrRE ) ;
177
+
171
178
var match = null ;
172
- if ( state . indentationDiff >= 4 && ( prevLineIsIndentedCode || lineIsEmpty ( state . prevLine ) ) ) {
179
+ if ( state . indentationDiff >= 4 && ( prevLineIsIndentedCode || prevLineLineIsEmpty ) ) {
173
180
stream . skipToEnd ( ) ;
174
181
state . indentedCode = true ;
175
182
return tokenTypes . code ;
@@ -180,23 +187,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
180
187
if ( modeCfg . highlightFormatting ) state . formatting = "header" ;
181
188
state . f = state . inline ;
182
189
return getType ( state ) ;
183
- } else if ( ! lineIsEmpty ( state . prevLine ) && ! state . quote && ! prevLineIsList &&
184
- ! prevLineIsIndentedCode && ( match = stream . match ( setextHeaderRE ) ) ) {
185
- state . header = match [ 0 ] . charAt ( 0 ) == '=' ? 1 : 2 ;
186
- if ( modeCfg . highlightFormatting ) state . formatting = "header" ;
187
- state . f = state . inline ;
188
- return getType ( state ) ;
189
190
} else if ( stream . eat ( '>' ) ) {
190
191
state . quote = sol ? 1 : state . quote + 1 ;
191
192
if ( modeCfg . highlightFormatting ) state . formatting = "quote" ;
192
193
stream . eatSpace ( ) ;
193
194
return getType ( state ) ;
194
- } else if ( stream . peek ( ) === '[' ) {
195
- return switchInline ( stream , state , footnoteLink ) ;
196
- } else if ( stream . match ( hrRE , true ) ) {
197
- state . hr = true ;
198
- return tokenTypes . hr ;
199
- } else if ( ! state . quote && ( match = stream . match ( listRE ) ) ) {
195
+ } else if ( ! isHr && ! state . quote && ( match = stream . match ( listRE ) ) ) {
200
196
var listType = match [ 1 ] ? "ol" : "ul" ;
201
197
202
198
state . indentation = lineIndentation + stream . current ( ) . length ;
@@ -220,6 +216,35 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
220
216
if ( modeCfg . highlightFormatting ) state . formatting = "code-block" ;
221
217
state . code = - 1
222
218
return getType ( state ) ;
219
+ // SETEXT has lowest block-scope precedence after HR, so check it after
220
+ // the others (code, blockquote, list...)
221
+ } else if (
222
+ // if setext set, indicates line after ---/===
223
+ state . setext || (
224
+ // line before ---/===
225
+ ! state . quote && state . list === false && ! state . code && ! isHr &&
226
+ ! prevLineIsList && ! linkDefRE . test ( stream . string ) &&
227
+ ( match = stream . lookAhead ( 1 ) ) && ( match = match . match ( setextHeaderRE ) )
228
+ )
229
+ ) {
230
+ if ( ! state . setext ) {
231
+ state . header = match [ 0 ] . charAt ( 0 ) == '=' ? 1 : 2 ;
232
+ state . setext = state . header ;
233
+ } else {
234
+ state . header = state . setext ;
235
+ // has no effect on type so we can reset it now
236
+ state . setext = 0 ;
237
+ stream . skipToEnd ( ) ;
238
+ if ( modeCfg . highlightFormatting ) state . formatting = "header" ;
239
+ }
240
+ state . f = state . inline ;
241
+ return getType ( state ) ;
242
+ } else if ( isHr ) {
243
+ stream . skipToEnd ( ) ;
244
+ state . hr = true ;
245
+ return tokenTypes . hr ;
246
+ } else if ( stream . peek ( ) === '[' ) {
247
+ return switchInline ( stream , state , footnoteLink ) ;
223
248
}
224
249
225
250
return switchInline ( stream , state , state . inline ) ;
@@ -703,6 +728,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
703
728
em : false ,
704
729
strong : false ,
705
730
header : 0 ,
731
+ setext : 0 ,
706
732
hr : false ,
707
733
taskList : false ,
708
734
list : false ,
@@ -741,6 +767,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
741
767
strikethrough : s . strikethrough ,
742
768
emoji : s . emoji ,
743
769
header : s . header ,
770
+ setext : s . setext ,
744
771
hr : s . hr ,
745
772
taskList : s . taskList ,
746
773
list : s . list ,
@@ -760,9 +787,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
760
787
state . formatting = false ;
761
788
762
789
if ( stream != state . thisLine ) {
763
- // Reset state.header and state.hr
790
+ // Reset state.header
764
791
state . header = 0 ;
765
- state . hr = false ;
766
792
767
793
if ( stream . match ( / ^ \s * $ / , true ) ) {
768
794
blankLine ( state ) ;
0 commit comments