Source File
typographer.go
Belonging Package
github.com/yuin/goldmark/extension
package extension
import (
gast
)
var uncloseCounterKey = parser.NewContextKey()
type unclosedCounter struct {
Single int
Double int
}
func ( *unclosedCounter) () {
.Single = 0
.Double = 0
}
func ( parser.Context) *unclosedCounter {
:= .Get(uncloseCounterKey)
if == nil {
= &unclosedCounter{}
.Set(uncloseCounterKey, )
}
return .(*unclosedCounter)
}
type TypographerConfig struct {
Substitutions [][]byte
}
func () [][]byte {
:= make([][]byte, typographicPunctuationMax)
[LeftSingleQuote] = []byte("‘")
[RightSingleQuote] = []byte("’")
[LeftDoubleQuote] = []byte("“")
[RightDoubleQuote] = []byte("”")
[EnDash] = []byte("–")
[EmDash] = []byte("—")
[Ellipsis] = []byte("…")
[LeftAngleQuote] = []byte("«")
[RightAngleQuote] = []byte("»")
[Apostrophe] = []byte("’")
return
}
func ( *TypographerConfig) ( parser.OptionName, interface{}) {
switch {
case optTypographicSubstitutions:
.Substitutions = .([][]byte)
}
}
type TypographerOption interface {
parser.Option
SetTypographerOption(*TypographerConfig)
}
const optTypographicSubstitutions parser.OptionName = "TypographicSubstitutions"
type TypographicSubstitutions map[TypographicPunctuation][]byte
type withTypographicSubstitutions struct {
value [][]byte
}
func ( *withTypographicSubstitutions) ( *parser.Config) {
.Options[optTypographicSubstitutions] = .value
}
func ( *withTypographicSubstitutions) ( *TypographerConfig) {
.Substitutions = .value
}
func ( map[TypographicPunctuation][]byte) TypographerOption {
:= newDefaultSubstitutions()
for , := range {
[] =
}
return &withTypographicSubstitutions{}
}
type typographerDelimiterProcessor struct {
}
func ( *typographerDelimiterProcessor) ( byte) bool {
return == '\'' || == '"'
}
func ( *typographerDelimiterProcessor) (, *parser.Delimiter) bool {
return .Char == .Char
}
func ( *typographerDelimiterProcessor) ( int) gast.Node {
return nil
}
var defaultTypographerDelimiterProcessor = &typographerDelimiterProcessor{}
type typographerParser struct {
TypographerConfig
}
func ( ...TypographerOption) parser.InlineParser {
:= &typographerParser{
TypographerConfig: TypographerConfig{
Substitutions: newDefaultSubstitutions(),
},
}
for , := range {
.SetTypographerOption(&.TypographerConfig)
}
return
}
func ( *typographerParser) () []byte {
return []byte{'\'', '"', '-', '.', '<', '>'}
}
func ( *typographerParser) ( gast.Node, text.Reader, parser.Context) gast.Node {
, := .PeekLine()
:= [0]
if len() > 2 {
if == '-' {
if .Substitutions[EmDash] != nil && [1] == '-' && [2] == '-' { // ---
:= gast.NewString(.Substitutions[EmDash])
.SetCode(true)
.Advance(3)
return
}
} else if == '.' {
if .Substitutions[Ellipsis] != nil && [1] == '.' && [2] == '.' { // ...
:= gast.NewString(.Substitutions[Ellipsis])
.SetCode(true)
.Advance(3)
return
}
return nil
}
}
if len() > 1 {
if == '<' {
if .Substitutions[LeftAngleQuote] != nil && [1] == '<' { // <<
:= gast.NewString(.Substitutions[LeftAngleQuote])
.SetCode(true)
.Advance(2)
return
}
return nil
} else if == '>' {
if .Substitutions[RightAngleQuote] != nil && [1] == '>' { // >>
:= gast.NewString(.Substitutions[RightAngleQuote])
.SetCode(true)
.Advance(2)
return
}
return nil
} else if .Substitutions[EnDash] != nil && == '-' && [1] == '-' { // --
:= gast.NewString(.Substitutions[EnDash])
.SetCode(true)
.Advance(2)
return
}
}
if == '\'' || == '"' {
:= .PrecendingCharacter()
:= parser.ScanDelimiter(, , 1, defaultTypographerDelimiterProcessor)
if == nil {
return nil
}
:= getUnclosedCounter()
if == '\'' {
if len() > 2 && (([1] == 'v' && [2] == 'e') || ([1] == 'l' && [2] == 'l') || ([1] == 'r' && [2] == 'e')) && (len() < 4 || util.IsPunct([3]) || util.IsSpace([3])) {
= RightSingleQuote
}
if == LeftSingleQuote {
.Single++
}
:= gast.NewString(.Substitutions[])
.SetCode(true)
.Advance(1)
return
}
if .Substitutions[RightSingleQuote] != nil && .Single > 0 {
:= .CanClose && !.CanOpen
:= .CanClose && .CanOpen && len() > 1 && ([1] == ',' || [1] == '.' || [1] == '!' || [1] == '?') && (len() == 2 || (len() > 2 && util.IsPunct([2]) || util.IsSpace([2])))
if || {
:= gast.NewString(.Substitutions[RightSingleQuote])
.SetCode(true)
.Advance(1)
.Single--
return
}
}
}
if == '"' {
if .Substitutions[LeftDoubleQuote] != nil && .CanOpen && !.CanClose {
:= gast.NewString(.Substitutions[LeftDoubleQuote])
.SetCode(true)
.Advance(1)
.Double++
return
}
if .Substitutions[RightDoubleQuote] != nil && .Double > 0 {
:= .CanClose && !.CanOpen
:= .CanClose && .CanOpen && len() > 1 && ([1] == ',' || [1] == '.' || [1] == '!' || [1] == '?') && (len() == 2 || (len() > 2 && util.IsPunct([2]) || util.IsSpace([2])))
if len() > 1 && [1] == '"' && unicode.IsDigit() {
return nil
}
:= gast.NewString(.Substitutions[RightDoubleQuote])
.SetCode(true)
.Advance(1)
.Double--
return
}
}
}
}
return nil
}
func ( *typographerParser) ( gast.Node, parser.Context) {
getUnclosedCounter().Reset()
}
type typographer struct {
options []TypographerOption
}
var Typographer = &typographer{}
func ( ...TypographerOption) goldmark.Extender {
return &typographer{
options: ,
}
}
func ( *typographer) ( goldmark.Markdown) {
.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewTypographerParser(.options...), 9999),
))
![]() |
The pages are generated with Golds v0.3.2-preview. (GOOS=darwin GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |