fence.py 2.5 KB

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