loading up the forgejo repo on tangled to test page performance
at forgejo 153 lines 3.7 kB view raw
1// Copyright 2022 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package math 5 6import ( 7 "bytes" 8 9 "github.com/yuin/goldmark/ast" 10 "github.com/yuin/goldmark/parser" 11 "github.com/yuin/goldmark/text" 12) 13 14type inlineParser struct { 15 start []byte 16 end []byte 17} 18 19var defaultInlineDollarParser = &inlineParser{ 20 start: []byte{'$'}, 21 end: []byte{'$'}, 22} 23 24var defaultDualDollarParser = &inlineParser{ 25 start: []byte{'$', '$'}, 26 end: []byte{'$', '$'}, 27} 28 29// NewInlineDollarParser returns a new inline parser 30func NewInlineDollarParser() parser.InlineParser { 31 return defaultInlineDollarParser 32} 33 34func NewInlineDualDollarParser() parser.InlineParser { 35 return defaultDualDollarParser 36} 37 38var defaultInlineBracketParser = &inlineParser{ 39 start: []byte{'\\', '('}, 40 end: []byte{'\\', ')'}, 41} 42 43// NewInlineDollarParser returns a new inline parser 44func NewInlineBracketParser() parser.InlineParser { 45 return defaultInlineBracketParser 46} 47 48// Trigger triggers this parser on $ or \ 49func (parser *inlineParser) Trigger() []byte { 50 return parser.start 51} 52 53func isPunctuation(b byte) bool { 54 return b == '.' || b == '!' || b == '?' || b == ',' || b == ';' || b == ':' 55} 56 57func isBracket(b byte) bool { 58 return b == ')' 59} 60 61func isAlphanumeric(b byte) bool { 62 return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') 63} 64 65// Parse parses the current line and returns a result of parsing. 66func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node { 67 line, _ := block.PeekLine() 68 69 if !bytes.HasPrefix(line, parser.start) { 70 // We'll catch this one on the next time round 71 return nil 72 } 73 74 precedingCharacter := block.PrecendingCharacter() 75 if precedingCharacter < 256 && (isAlphanumeric(byte(precedingCharacter)) || isPunctuation(byte(precedingCharacter))) { 76 // need to exclude things like `a$` from being considered a start 77 return nil 78 } 79 80 // move the opener marker point at the start of the text 81 opener := len(parser.start) 82 83 // Now look for an ending line 84 ender := opener 85 for { 86 pos := bytes.Index(line[ender:], parser.end) 87 if pos < 0 { 88 return nil 89 } 90 91 ender += pos 92 93 // Now we want to check the character at the end of our parser section 94 // that is ender + len(parser.end) and check if char before ender is '\' 95 pos = ender + len(parser.end) 96 if len(line) <= pos { 97 break 98 } 99 suceedingCharacter := line[pos] 100 // check valid ending character 101 if !isPunctuation(suceedingCharacter) && 102 (suceedingCharacter != ' ') && 103 (suceedingCharacter != '\n') && 104 !isBracket(suceedingCharacter) { 105 return nil 106 } 107 if line[ender-1] != '\\' { 108 break 109 } 110 111 // move the pointer onwards 112 ender += len(parser.end) 113 } 114 115 block.Advance(opener) 116 _, pos := block.Position() 117 var node ast.Node 118 if parser == defaultDualDollarParser { 119 node = NewInlineBlock() 120 } else { 121 node = NewInline() 122 } 123 segment := pos.WithStop(pos.Start + ender - opener) 124 node.AppendChild(node, ast.NewRawTextSegment(segment)) 125 block.Advance(ender - opener + len(parser.end)) 126 127 if parser == defaultDualDollarParser { 128 trimBlock(&(node.(*InlineBlock)).Inline, block) 129 } else { 130 trimBlock(node.(*Inline), block) 131 } 132 return node 133} 134 135func trimBlock(node *Inline, block text.Reader) { 136 if node.IsBlank(block.Source()) { 137 return 138 } 139 140 // trim first space and last space 141 first := node.FirstChild().(*ast.Text) 142 if first.Segment.IsEmpty() || block.Source()[first.Segment.Start] != ' ' { 143 return 144 } 145 146 last := node.LastChild().(*ast.Text) 147 if last.Segment.IsEmpty() || block.Source()[last.Segment.Stop-1] != ' ' { 148 return 149 } 150 151 first.Segment = first.Segment.WithStart(first.Segment.Start + 1) 152 last.Segment = last.Segment.WithStop(last.Segment.Stop - 1) 153}