1# fences (``` lang, ~~~ lang)
2import logging
3
4from .state_block import StateBlock
5
6LOGGER = logging.getLogger(__name__)
7
8
9def fence(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
10 LOGGER.debug("entering fence: %s, %s, %s, %s", state, startLine, endLine, silent)
11
12 haveEndMarker = False
13 pos = state.bMarks[startLine] + state.tShift[startLine]
14 maximum = state.eMarks[startLine]
15
16 if state.is_code_block(startLine):
17 return False
18
19 if pos + 3 > maximum:
20 return False
21
22 marker = state.src[pos]
23
24 if marker not in ("~", "`"):
25 return False
26
27 # scan marker length
28 mem = pos
29 pos = state.skipCharsStr(pos, marker)
30
31 length = pos - mem
32
33 if length < 3:
34 return False
35
36 markup = state.src[mem:pos]
37 params = state.src[pos:maximum]
38
39 if marker == "`" and marker in params:
40 return False
41
42 # Since start is found, we can report success here in validation mode
43 if silent:
44 return True
45
46 # search end of block
47 nextLine = startLine
48
49 while True:
50 nextLine += 1
51 if nextLine >= endLine:
52 # unclosed block should be autoclosed by end of document.
53 # also block seems to be autoclosed by end of parent
54 break
55
56 pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
57 maximum = state.eMarks[nextLine]
58
59 if pos < maximum and state.sCount[nextLine] < state.blkIndent:
60 # non-empty line with negative indent should stop the list:
61 # - ```
62 # test
63 break
64
65 try:
66 if state.src[pos] != marker:
67 continue
68 except IndexError:
69 break
70
71 if state.is_code_block(nextLine):
72 continue
73
74 pos = state.skipCharsStr(pos, marker)
75
76 # closing code fence must be at least as long as the opening one
77 if pos - mem < length:
78 continue
79
80 # make sure tail has spaces only
81 pos = state.skipSpaces(pos)
82
83 if pos < maximum:
84 continue
85
86 haveEndMarker = True
87 # found!
88 break
89
90 # If a fence has heading spaces, they should be removed from its inner block
91 length = state.sCount[startLine]
92
93 state.line = nextLine + (1 if haveEndMarker else 0)
94
95 token = state.push("fence", "code", 0)
96 token.info = params
97 token.content = state.getLines(startLine + 1, nextLine, length, True)
98 token.markup = markup
99 token.map = [startLine, state.line]
100
101 return True