diff options
Diffstat (limited to 'vendor/github.com/yuin/goldmark/parser/fcode_block.go')
| -rw-r--r-- | vendor/github.com/yuin/goldmark/parser/fcode_block.go | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/vendor/github.com/yuin/goldmark/parser/fcode_block.go b/vendor/github.com/yuin/goldmark/parser/fcode_block.go new file mode 100644 index 0000000..e51a35a --- /dev/null +++ b/vendor/github.com/yuin/goldmark/parser/fcode_block.go | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | package parser | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | |||
| 6 | "github.com/yuin/goldmark/ast" | ||
| 7 | "github.com/yuin/goldmark/text" | ||
| 8 | "github.com/yuin/goldmark/util" | ||
| 9 | ) | ||
| 10 | |||
| 11 | type fencedCodeBlockParser struct { | ||
| 12 | } | ||
| 13 | |||
| 14 | var defaultFencedCodeBlockParser = &fencedCodeBlockParser{} | ||
| 15 | |||
| 16 | // NewFencedCodeBlockParser returns a new BlockParser that | ||
| 17 | // parses fenced code blocks. | ||
| 18 | func NewFencedCodeBlockParser() BlockParser { | ||
| 19 | return defaultFencedCodeBlockParser | ||
| 20 | } | ||
| 21 | |||
| 22 | type fenceData struct { | ||
| 23 | char byte | ||
| 24 | indent int | ||
| 25 | length int | ||
| 26 | node ast.Node | ||
| 27 | } | ||
| 28 | |||
| 29 | var fencedCodeBlockInfoKey = NewContextKey() | ||
| 30 | |||
| 31 | func (b *fencedCodeBlockParser) Trigger() []byte { | ||
| 32 | return []byte{'~', '`'} | ||
| 33 | } | ||
| 34 | |||
| 35 | func (b *fencedCodeBlockParser) Open(parent ast.Node, reader text.Reader, pc Context) (ast.Node, State) { | ||
| 36 | line, segment := reader.PeekLine() | ||
| 37 | pos := pc.BlockOffset() | ||
| 38 | if pos < 0 || (line[pos] != '`' && line[pos] != '~') { | ||
| 39 | return nil, NoChildren | ||
| 40 | } | ||
| 41 | findent := pos | ||
| 42 | fenceChar := line[pos] | ||
| 43 | i := pos | ||
| 44 | for ; i < len(line) && line[i] == fenceChar; i++ { | ||
| 45 | } | ||
| 46 | oFenceLength := i - pos | ||
| 47 | if oFenceLength < 3 { | ||
| 48 | return nil, NoChildren | ||
| 49 | } | ||
| 50 | var info *ast.Text | ||
| 51 | if i < len(line)-1 { | ||
| 52 | rest := line[i:] | ||
| 53 | left := util.TrimLeftSpaceLength(rest) | ||
| 54 | right := util.TrimRightSpaceLength(rest) | ||
| 55 | if left < len(rest)-right { | ||
| 56 | infoStart, infoStop := segment.Start-segment.Padding+i+left, segment.Stop-right | ||
| 57 | value := rest[left : len(rest)-right] | ||
| 58 | if fenceChar == '`' && bytes.IndexByte(value, '`') > -1 { | ||
| 59 | return nil, NoChildren | ||
| 60 | } else if infoStart != infoStop { | ||
| 61 | info = ast.NewTextSegment(text.NewSegment(infoStart, infoStop)) | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | node := ast.NewFencedCodeBlock(info) | ||
| 66 | pc.Set(fencedCodeBlockInfoKey, &fenceData{fenceChar, findent, oFenceLength, node}) | ||
| 67 | return node, NoChildren | ||
| 68 | |||
| 69 | } | ||
| 70 | |||
| 71 | func (b *fencedCodeBlockParser) Continue(node ast.Node, reader text.Reader, pc Context) State { | ||
| 72 | line, segment := reader.PeekLine() | ||
| 73 | fdata := pc.Get(fencedCodeBlockInfoKey).(*fenceData) | ||
| 74 | |||
| 75 | w, pos := util.IndentWidth(line, reader.LineOffset()) | ||
| 76 | if w < 4 { | ||
| 77 | i := pos | ||
| 78 | for ; i < len(line) && line[i] == fdata.char; i++ { | ||
| 79 | } | ||
| 80 | length := i - pos | ||
| 81 | if length >= fdata.length && util.IsBlank(line[i:]) { | ||
| 82 | newline := 1 | ||
| 83 | if line[len(line)-1] != '\n' { | ||
| 84 | newline = 0 | ||
| 85 | } | ||
| 86 | reader.Advance(segment.Stop - segment.Start - newline + segment.Padding) | ||
| 87 | return Close | ||
| 88 | } | ||
| 89 | } | ||
| 90 | pos, padding := util.IndentPositionPadding(line, reader.LineOffset(), segment.Padding, fdata.indent) | ||
| 91 | if pos < 0 { | ||
| 92 | pos = util.FirstNonSpacePosition(line) | ||
| 93 | if pos < 0 { | ||
| 94 | pos = 0 | ||
| 95 | } | ||
| 96 | padding = 0 | ||
| 97 | } | ||
| 98 | seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding) | ||
| 99 | // if code block line starts with a tab, keep a tab as it is. | ||
| 100 | if padding != 0 { | ||
| 101 | preserveLeadingTabInCodeBlock(&seg, reader, fdata.indent) | ||
| 102 | } | ||
| 103 | node.Lines().Append(seg) | ||
| 104 | reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding) | ||
| 105 | return Continue | NoChildren | ||
| 106 | } | ||
| 107 | |||
| 108 | func (b *fencedCodeBlockParser) Close(node ast.Node, reader text.Reader, pc Context) { | ||
| 109 | fdata := pc.Get(fencedCodeBlockInfoKey).(*fenceData) | ||
| 110 | if fdata.node == node { | ||
| 111 | pc.Set(fencedCodeBlockInfoKey, nil) | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | func (b *fencedCodeBlockParser) CanInterruptParagraph() bool { | ||
| 116 | return true | ||
| 117 | } | ||
| 118 | |||
| 119 | func (b *fencedCodeBlockParser) CanAcceptIndentedLine() bool { | ||
| 120 | return false | ||
| 121 | } | ||
