@@ -11,7 +11,6 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
11
11
"entering reference: %s, %s, %s, %s" , state , startLine , _endLine , silent
12
12
)
13
13
14
- lines = 0
15
14
pos = state .bMarks [startLine ] + state .tShift [startLine ]
16
15
maximum = state .eMarks [startLine ]
17
16
nextLine = startLine + 1
@@ -22,51 +21,9 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
22
21
if state .src [pos ] != "[" :
23
22
return False
24
23
25
- # Simple check to quickly interrupt scan on [link](url) at the start of line.
26
- # Can be useful on practice: https:#github.com/markdown-it/markdown-it/issues/54
27
- while pos < maximum :
28
- # /* ] */ /* \ */ /* : */
29
- if state .src [pos ] == "]" and state .src [pos - 1 ] != "\\ " :
30
- if pos + 1 == maximum :
31
- return False
32
- if state .src [pos + 1 ] != ":" :
33
- return False
34
- break
35
- pos += 1
36
-
37
- endLine = state .lineMax
38
-
39
- # jump line-by-line until empty one or EOF
40
- terminatorRules = state .md .block .ruler .getRules ("reference" )
24
+ string = state .src [pos : maximum + 1 ]
41
25
42
- oldParentType = state .parentType
43
- state .parentType = "reference"
44
-
45
- while nextLine < endLine and not state .isEmpty (nextLine ):
46
- # this would be a code block normally, but after paragraph
47
- # it's considered a lazy continuation regardless of what's there
48
- if state .sCount [nextLine ] - state .blkIndent > 3 :
49
- nextLine += 1
50
- continue
51
-
52
- # quirk for blockquotes, this line should already be checked by that rule
53
- if state .sCount [nextLine ] < 0 :
54
- nextLine += 1
55
- continue
56
-
57
- # Some tags can terminate paragraph without empty line.
58
- terminate = False
59
- for terminatorRule in terminatorRules :
60
- if terminatorRule (state , nextLine , endLine , True ):
61
- terminate = True
62
- break
63
-
64
- if terminate :
65
- break
66
-
67
- nextLine += 1
68
-
69
- string = state .getLines (startLine , nextLine , state .blkIndent , False ).strip ()
26
+ # string = state.getLines(startLine, nextLine, state.blkIndent, False).strip()
70
27
maximum = len (string )
71
28
72
29
labelEnd = None
@@ -79,11 +36,20 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
79
36
labelEnd = pos
80
37
break
81
38
elif ch == 0x0A : # /* \n */
82
- lines += 1
39
+ if (lineContent := getNextLine (state , nextLine )) is not None :
40
+ string += lineContent
41
+ maximum = len (string )
42
+ nextLine += 1
83
43
elif ch == 0x5C : # /* \ */
84
44
pos += 1
85
- if pos < maximum and charCodeAt (string , pos ) == 0x0A :
86
- lines += 1
45
+ if (
46
+ pos < maximum
47
+ and charCodeAt (string , pos ) == 0x0A
48
+ and (lineContent := getNextLine (state , nextLine )) is not None
49
+ ):
50
+ string += lineContent
51
+ maximum = len (string )
52
+ nextLine += 1
87
53
pos += 1
88
54
89
55
if (
@@ -97,7 +63,10 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
97
63
while pos < maximum :
98
64
ch = charCodeAt (string , pos )
99
65
if ch == 0x0A :
100
- lines += 1
66
+ if (lineContent := getNextLine (state , nextLine )) is not None :
67
+ string += lineContent
68
+ maximum = len (string )
69
+ nextLine += 1
101
70
elif isSpace (ch ):
102
71
pass
103
72
else :
@@ -106,28 +75,30 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
106
75
107
76
# [label]: destination 'title'
108
77
# ^^^^^^^^^^^ parse this
109
- res = state .md .helpers .parseLinkDestination (string , pos , maximum )
110
- if not res .ok :
78
+ destRes = state .md .helpers .parseLinkDestination (string , pos , maximum )
79
+ if not destRes .ok :
111
80
return False
112
81
113
- href = state .md .normalizeLink (res .str )
82
+ href = state .md .normalizeLink (destRes .str )
114
83
if not state .md .validateLink (href ):
115
84
return False
116
85
117
- pos = res .pos
118
- lines += res .lines
86
+ pos = destRes .pos
119
87
120
88
# save cursor state, we could require to rollback later
121
89
destEndPos = pos
122
- destEndLineNo = lines
90
+ destEndLineNo = nextLine
123
91
124
92
# [label]: destination 'title'
125
93
# ^^^ skipping those spaces
126
94
start = pos
127
95
while pos < maximum :
128
96
ch = charCodeAt (string , pos )
129
97
if ch == 0x0A :
130
- lines += 1
98
+ if (lineContent := getNextLine (state , nextLine )) is not None :
99
+ string += lineContent
100
+ maximum = len (string )
101
+ nextLine += 1
131
102
elif isSpace (ch ):
132
103
pass
133
104
else :
@@ -136,15 +107,23 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
136
107
137
108
# [label]: destination 'title'
138
109
# ^^^^^^^ parse this
139
- res = state .md .helpers .parseLinkTitle (string , pos , maximum )
140
- if pos < maximum and start != pos and res .ok :
141
- title = res .str
142
- pos = res .pos
143
- lines += res .lines
110
+ titleRes = state .md .helpers .parseLinkTitle (string , pos , maximum , None )
111
+ while titleRes .can_continue :
112
+ if (lineContent := getNextLine (state , nextLine )) is None :
113
+ break
114
+ string += lineContent
115
+ pos = maximum
116
+ maximum = len (string )
117
+ nextLine += 1
118
+ titleRes = state .md .helpers .parseLinkTitle (string , pos , maximum , titleRes )
119
+
120
+ if pos < maximum and start != pos and titleRes .ok :
121
+ title = titleRes .str
122
+ pos = titleRes .pos
144
123
else :
145
124
title = ""
146
125
pos = destEndPos
147
- lines = destEndLineNo
126
+ nextLine = destEndLineNo
148
127
149
128
# skip trailing spaces until the rest of the line
150
129
while pos < maximum :
@@ -158,7 +137,7 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
158
137
# but it could still be a valid reference if we roll back
159
138
title = ""
160
139
pos = destEndPos
161
- lines = destEndLineNo
140
+ nextLine = destEndLineNo
162
141
while pos < maximum :
163
142
ch = charCodeAt (string , pos )
164
143
if not isSpace (ch ):
@@ -181,7 +160,7 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
181
160
if "references" not in state .env :
182
161
state .env ["references" ] = {}
183
162
184
- state .line = startLine + lines + 1
163
+ state .line = nextLine
185
164
186
165
# note, this is not part of markdown-it JS, but is useful for renderers
187
166
if state .md .options .get ("inline_definitions" , False ):
@@ -210,6 +189,47 @@ def reference(state: StateBlock, startLine: int, _endLine: int, silent: bool) ->
210
189
}
211
190
)
212
191
213
- state .parentType = oldParentType
214
-
215
192
return True
193
+
194
+
195
+ def getNextLine (state : StateBlock , nextLine : int ) -> None | str :
196
+ endLine = state .lineMax
197
+
198
+ if nextLine >= endLine or state .isEmpty (nextLine ):
199
+ # empty line or end of input
200
+ return None
201
+
202
+ isContinuation = False
203
+
204
+ # this would be a code block normally, but after paragraph
205
+ # it's considered a lazy continuation regardless of what's there
206
+ if state .is_code_block (nextLine ):
207
+ isContinuation = True
208
+
209
+ # quirk for blockquotes, this line should already be checked by that rule
210
+ if state .sCount [nextLine ] < 0 :
211
+ isContinuation = True
212
+
213
+ if not isContinuation :
214
+ terminatorRules = state .md .block .ruler .getRules ("reference" )
215
+ oldParentType = state .parentType
216
+ state .parentType = "reference"
217
+
218
+ # Some tags can terminate paragraph without empty line.
219
+ terminate = False
220
+ for terminatorRule in terminatorRules :
221
+ if terminatorRule (state , nextLine , endLine , True ):
222
+ terminate = True
223
+ break
224
+
225
+ state .parentType = oldParentType
226
+
227
+ if terminate :
228
+ # terminated by another block
229
+ return None
230
+
231
+ pos = state .bMarks [nextLine ] + state .tShift [nextLine ]
232
+ maximum = state .eMarks [nextLine ]
233
+
234
+ # max + 1 explicitly includes the newline
235
+ return state .src [pos : maximum + 1 ]
0 commit comments