summaryrefslogblamecommitdiffstats
path: root/vendor/gitlab.com/golang-commonmark/markdown/replacements.go
blob: 8d67af6eec95549515bb38a8c1ad23543a400828 (plain) (tree)



































































































































































































































                                                                                                   
// Copyright 2015 The Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package markdown

import "strings"

func exclquest(b byte) bool {
	return b == '!' || b == '?'
}

func byteToLower(b byte) byte {
	if b >= 'A' && b <= 'Z' {
		return b - 'A' + 'a'
	}
	return b
}

var replChar = [256]bool{
	'(': true,
	'!': true,
	'+': true,
	',': true,
	'-': true,
	'.': true,
	'?': true,
}

func performReplacements(s string) string {
	var ss []string

	start := 0
	for i := 0; i < len(s); i++ {
		b := s[i]

		if replChar[b] {

		outer:
			switch b {
			case '(':
				if i+2 >= len(s) {
					break
				}

				b2 := s[i+1]

				b2 = byteToLower(b2)
				switch b2 {
				case 'c', 'r', 'p':
					if s[i+2] != ')' {
						break outer
					}
					switch b2 {
					case 'c':
						if start < i {
							ss = append(ss, s[start:i])
						}
						ss = append(ss, "©")
					case 'r':
						if start < i {
							ss = append(ss, s[start:i])
						}
						ss = append(ss, "®")
					case 'p':
						if start < i {
							ss = append(ss, s[start:i])
						}
						ss = append(ss, "§")
					}
					i += 2
					start = i + 1
					continue

				case 't':
					if i+3 >= len(s) {
						break outer
					}
					if s[i+3] != ')' || byteToLower(s[i+2]) != 'm' {
						break outer
					}
					if start < i {
						ss = append(ss, s[start:i])
					}
					ss = append(ss, "™")
					i += 3
					start = i + 1
					continue
				default:
					break outer
				}

			case '+':
				if i+1 >= len(s) || s[i+1] != '-' {
					break
				}
				if start < i {
					ss = append(ss, s[start:i])
				}
				ss = append(ss, "±")
				i++
				start = i + 1
				continue

			case '.':
				if i+1 >= len(s) || s[i+1] != '.' {
					break
				}

				j := i + 2
				for j < len(s) && s[j] == '.' {
					j++
				}
				if start < i {
					ss = append(ss, s[start:i])
				}
				if i == 0 || !(s[i-1] == '?' || s[i-1] == '!') {
					ss = append(ss, "…")
				} else {
					ss = append(ss, "..")
				}
				i = j - 1
				start = i + 1
				continue

			case '?', '!':
				if i+3 >= len(s) {
					break
				}
				if !(exclquest(s[i+1]) && exclquest(s[i+2]) && exclquest(s[i+3])) {
					break
				}
				if start < i {
					ss = append(ss, s[start:i])
				}
				ss = append(ss, s[i:i+3])
				j := i + 3
				for j < len(s) && exclquest(s[j]) {
					j++
				}
				i = j - 1
				start = i + 1
				continue

			case ',':
				if i+1 >= len(s) || s[i+1] != ',' {
					break
				}
				if start < i {
					ss = append(ss, s[start:i])
				}
				ss = append(ss, ",")
				j := i + 2
				for j < len(s) && s[j] == ',' {
					j++
				}
				i = j - 1
				start = i + 1
				continue

			case '-':
				if i+1 >= len(s) || s[i+1] != '-' {
					break
				}
				if i+2 >= len(s) || s[i+2] != '-' {
					if start < i {
						ss = append(ss, s[start:i])
					}
					ss = append(ss, "–")
					i++
					start = i + 1
					continue
				}
				if i+3 >= len(s) || s[i+3] != '-' {
					if start < i {
						ss = append(ss, s[start:i])
					}
					ss = append(ss, "—")
					i += 2
					start = i + 1
					continue
				}

				j := i + 3
				for j < len(s) && s[j] == '-' {
					j++
				}
				if start < i {
					ss = append(ss, s[start:i])
				}
				ss = append(ss, s[i:j])
				i = j - 1
				start = i + 1
				continue
			}
		}
	}
	if ss == nil {
		return s
	}
	if start < len(s) {
		ss = append(ss, s[start:])
	}
	return strings.Join(ss, "")
}

func ruleReplacements(s *StateCore) {
	if !s.Md.Typographer {
		return
	}

	insideLink := false
	for _, tok := range s.Tokens {
		if tok, ok := tok.(*Inline); ok {
			for _, itok := range tok.Children {
				switch itok := itok.(type) {
				case *LinkOpen:
					insideLink = true
				case *LinkClose:
					insideLink = false
				case *Text:
					if !insideLink {
						itok.Content = performReplacements(itok.Content)
					}
				}
			}
		}
	}
}