# Listsimportloggingfrom.state_blockimportStateBlockfrom..common.utilsimportisSpaceLOGGER=logging.getLogger(__name__)# Search `[-+*][\n ]`, returns next pos after marker on success# or -1 on fail.
[文档]defskipBulletListMarker(state:StateBlock,startLine:int):pos=state.bMarks[startLine]+state.tShift[startLine]maximum=state.eMarks[startLine]marker=state.srcCharCode[pos]pos+=1# Check bullet /* * */ /* - */ /* + */ifmarker!=0x2Aandmarker!=0x2Dandmarker!=0x2B:return-1ifpos<maximum:ch=state.srcCharCode[pos]ifnotisSpace(ch):# " -test " - is not a list itemreturn-1returnpos
# Search `\d+[.)][\n ]`, returns next pos after marker on success# or -1 on fail.
[文档]defskipOrderedListMarker(state:StateBlock,startLine:int):start=state.bMarks[startLine]+state.tShift[startLine]pos=startmaximum=state.eMarks[startLine]# List marker should have at least 2 chars (digit + dot)ifpos+1>=maximum:return-1ch=state.srcCharCode[pos]pos+=1# /* 0 */ /* 9 */ifch<0x30orch>0x39:return-1whileTrue:# EOL -> failifpos>=maximum:return-1ch=state.srcCharCode[pos]pos+=1# /* 0 */ /* 9 */ifch>=0x30andch<=0x39:# List marker should have no more than 9 digits# (prevents integer overflow in browsers)ifpos-start>=10:return-1continue# found valid marker: /* ) */ /* . */ifch==0x29orch==0x2E:breakreturn-1ifpos<maximum:ch=state.srcCharCode[pos]ifnotisSpace(ch):# " 1.test " - is not a list itemreturn-1returnpos
[文档]deflist_block(state:StateBlock,startLine:int,endLine:int,silent:bool):LOGGER.debug("entering list: %s, %s, %s, %s",state,startLine,endLine,silent)isTerminatingParagraph=Falsetight=True# if it's indented more than 3 spaces, it should be a code blockifstate.sCount[startLine]-state.blkIndent>=4:returnFalse# Special case:# - item 1# - item 2# - item 3# - item 4# - this one is a paragraph continuationif(state.listIndent>=0andstate.sCount[startLine]-state.listIndent>=4andstate.sCount[startLine]<state.blkIndent):returnFalse# limit conditions when list can interrupt# a paragraph (validation mode only)ifsilentandstate.parentType=="paragraph":# Next list item should still terminate previous list item## This code can fail if plugins use blkIndent as well as lists,# but I hope the spec gets fixed long before that happens.#ifstate.tShift[startLine]>=state.blkIndent:isTerminatingParagraph=True# Detect list type and position after markerposAfterMarker=skipOrderedListMarker(state,startLine)ifposAfterMarker>=0:isOrdered=Truestart=state.bMarks[startLine]+state.tShift[startLine]markerValue=int(state.src[start:posAfterMarker-1])# If we're starting a new ordered list right after# a paragraph, it should start with 1.ifisTerminatingParagraphandmarkerValue!=1:returnFalseelse:posAfterMarker=skipBulletListMarker(state,startLine)ifposAfterMarker>=0:isOrdered=Falseelse:returnFalse# If we're starting a new unordered list right after# a paragraph, first line should not be empty.ifisTerminatingParagraph:ifstate.skipSpaces(posAfterMarker)>=state.eMarks[startLine]:returnFalse# We should terminate list on style change. Remember first one to compare.markerCharCode=state.srcCharCode[posAfterMarker-1]# For validation mode we can terminate immediatelyifsilent:returnTrue# Start listlistTokIdx=len(state.tokens)ifisOrdered:token=state.push("ordered_list_open","ol",1)ifmarkerValue!=1:token.attrs={"start":markerValue}else:token=state.push("bullet_list_open","ul",1)token.map=listLines=[startLine,0]token.markup=chr(markerCharCode)## Iterate list items#nextLine=startLineprevEmptyEnd=FalseterminatorRules=state.md.block.ruler.getRules("list")oldParentType=state.parentTypestate.parentType="list"whilenextLine<endLine:pos=posAfterMarkermaximum=state.eMarks[nextLine]initial=offset=(state.sCount[nextLine]+posAfterMarker-(state.bMarks[startLine]+state.tShift[startLine]))whilepos<maximum:ch=state.srcCharCode[pos]ifch==0x09:offset+=4-(offset+state.bsCount[nextLine])%4elifch==0x20:offset+=1else:breakpos+=1contentStart=posifcontentStart>=maximum:# trimming space in "- \n 3" case, indent is 1 hereindentAfterMarker=1else:indentAfterMarker=offset-initial# If we have more than 4 spaces, the indent is 1# (the rest is just indented code block)ifindentAfterMarker>4:indentAfterMarker=1# " - test"# ^^^^^ - calculating total length of this thingindent=initial+indentAfterMarker# Run subparser & write tokenstoken=state.push("list_item_open","li",1)token.markup=chr(markerCharCode)token.map=itemLines=[startLine,0]# change current state, then restore it after parser subcalloldTight=state.tightoldTShift=state.tShift[startLine]oldSCount=state.sCount[startLine]# - example list# ^ listIndent position will be here# ^ blkIndent position will be here#oldListIndent=state.listIndentstate.listIndent=state.blkIndentstate.blkIndent=indentstate.tight=Truestate.tShift[startLine]=contentStart-state.bMarks[startLine]state.sCount[startLine]=offsetifcontentStart>=maximumandstate.isEmpty(startLine+1):# workaround for this case# (list item is empty, list terminates before "foo"):# ~~~~~~~~# -## foo# ~~~~~~~~state.line=min(state.line+2,endLine)else:# NOTE in list.js this was:# state.md.block.tokenize(state, startLine, endLine, True)# but tokeniz does not take the final parameterstate.md.block.tokenize(state,startLine,endLine)# If any of list item is tight, mark list as tightif(notstate.tight)orprevEmptyEnd:tight=False# Item become loose if finish with empty line,# but we should filter last element, because it means list finishprevEmptyEnd=(state.line-startLine)>1andstate.isEmpty(state.line-1)state.blkIndent=state.listIndentstate.listIndent=oldListIndentstate.tShift[startLine]=oldTShiftstate.sCount[startLine]=oldSCountstate.tight=oldTighttoken=state.push("list_item_close","li",-1)token.markup=chr(markerCharCode)nextLine=startLine=state.lineitemLines[1]=nextLineifnextLine>=endLine:breakcontentStart=state.bMarks[startLine]## Try to check if list is terminated or continued.#ifstate.sCount[nextLine]<state.blkIndent:break# if it's indented more than 3 spaces, it should be a code blockifstate.sCount[startLine]-state.blkIndent>=4:break# fail if terminating block foundterminate=FalseforterminatorRuleinterminatorRules:ifterminatorRule(state,nextLine,endLine,True):terminate=Truebreakifterminate:break# fail if list has another typeifisOrdered:posAfterMarker=skipOrderedListMarker(state,nextLine)ifposAfterMarker<0:breakelse:posAfterMarker=skipBulletListMarker(state,nextLine)ifposAfterMarker<0:breakifmarkerCharCode!=state.srcCharCode[posAfterMarker-1]:break# Finalize listifisOrdered:token=state.push("ordered_list_close","ol",-1)else:token=state.push("bullet_list_close","ul",-1)token.markup=chr(markerCharCode)listLines[1]=nextLinestate.line=nextLinestate.parentType=oldParentType# mark paragraphs tight if needediftight:markTightParagraphs(state,listTokIdx)returnTrue