markdown code measurement

This commit is contained in:
Gavin Nishizawa 2022-11-07 15:18:16 -08:00
parent d271f37f33
commit c83a55f26b
3 changed files with 83 additions and 29 deletions

View file

@ -16,6 +16,10 @@
font-size: 1em;
}
.md {
tab-size: 8;
}
/* based on https://github.com/sindresorhus/github-markdown-css */
@media (prefers-color-scheme: dark) {
.md {
@ -341,15 +345,15 @@
margin-left: 0;
}
.md tt,
/* .md tt,
.md code {
font-size: 12px;
}
} */
.md pre {
margin-top: 0;
margin-bottom: 0;
font-size: 12px;
/* font-size: 12px; */
word-wrap: normal;
}
@ -635,7 +639,7 @@
.md tt {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
/* font-size: 85%; */
background-color: var(--color-neutral-muted);
border-radius: 6px;
}
@ -675,8 +679,8 @@
.md pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
/* font-size: 85%;
line-height: 1.45; */
background-color: var(--color-canvas-subtle);
border-radius: 6px;
}

View file

@ -4,6 +4,7 @@ import (
"bytes"
"math"
"strings"
"unicode"
"unicode/utf8"
"github.com/PuerkitoBio/goquery"
@ -39,6 +40,9 @@ const (
Padding_pre = 16
MarginBottom_pre = 16
PaddingTopBottom_code_em = 0.2
PaddingLeftRight_code_em = 0.4
PaddingLR_blockquote_em = 1.
MarginBottom_blockquote = 16
BorderLeft_blockquote_em = 0.25
@ -185,6 +189,11 @@ func hasAncestorElement(n *html.Node, elType string) bool {
// measures node dimensions to match rendering with styles in github-markdown.css
func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (width, height, marginTop, marginBottom float64) {
var parentElementType string
if n.Parent != nil && n.Parent.Type == html.ElementNode {
parentElementType = n.Parent.Data
}
switch n.Type {
case html.TextNode:
if strings.TrimSpace(n.Data) == "" {
@ -196,12 +205,19 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
spaceRune, _ := utf8.DecodeRuneInString(" ")
// measure will not include leading or trailing whitespace, so we have to add in the space width
spaceWidth := ruler.atlases[font].glyph(spaceRune).advance
tabWidth := 8 * spaceWidth
str := n.Data
hasCodeParent := n.Parent != nil && n.Parent.Type == html.ElementNode && (n.Parent.Data == "pre" || n.Parent.Data == "code")
if !hasCodeParent {
str = strings.ReplaceAll(n.Data, "\n", " ")
htmlWhitespace := true
switch parentElementType {
case "pre", "code":
htmlWhitespace = false
}
if htmlWhitespace {
str = strings.ReplaceAll(str, "\n", " ")
str = strings.ReplaceAll(str, "\t", " ")
if strings.HasPrefix(str, " ") {
str = strings.TrimPrefix(str, " ")
if hasPrev(n) {
@ -214,14 +230,38 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
spaceWidths += spaceWidth
}
}
} else {
isNotSpace := func(r rune) bool {
return !unicode.IsSpace(r)
}
startIndex := strings.IndexFunc(str, isNotSpace)
endIndex := strings.LastIndexFunc(str, isNotSpace)
if startIndex != -1 && endIndex != -1 {
for i, r := range str {
// skip over runes in middle
if i >= startIndex && i <= endIndex {
continue
}
// measure width of leading/trailing whitespace
switch r {
case ' ':
spaceWidths += spaceWidth
case '\t':
spaceWidths += tabWidth
}
}
str = str[startIndex : endIndex+1]
}
}
w, h := ruler.MeasurePrecise(font, str)
w += spaceWidths
// fmt.Printf("%d:%s width %v height %v fontStyle %s\n", depth, n.Data, w, h, font.Style)
if h > 0 && h < MarkdownLineHeightPx {
h = MarkdownLineHeightPx
}
return w, h, 0, 0
// fmt.Printf("%d:'%s' width %v (%v) height %v fontStyle %s fontSize %v family %v\n", depth, n.Data, w, w+spaceWidths, h, font.Style, font.Size, font.Family)
return w + spaceWidths, h, 0, 0
case html.ElementNode:
// fmt.Printf("%d: %v node\n", depth, n.Data)
switch n.Data {
@ -237,7 +277,8 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
case "b", "strong":
font.Style = d2fonts.FONT_STYLE_BOLD
case "pre", "code":
// TODO monospaced font
font.Family = d2fonts.SourceCodePro
font.Style = d2fonts.FONT_STYLE_REGULAR
}
if n.FirstChild != nil {
@ -284,10 +325,10 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
switch n.Data {
case "blockquote":
width += float64(font.Size) * (2*PaddingLR_blockquote_em + BorderLeft_blockquote_em)
width += (2*PaddingLR_blockquote_em + BorderLeft_blockquote_em) * float64(font.Size)
marginBottom = go2.Max(marginBottom, MarginBottom_blockquote)
case "p":
if n.Parent != nil && n.Parent.Type == html.ElementNode && n.Parent.Data == "li" {
if parentElementType == "li" {
marginTop = go2.Max(marginTop, MarginTop_li_p)
}
marginBottom = go2.Max(marginBottom, MarginBottom_p)
@ -296,7 +337,7 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
marginBottom = go2.Max(marginBottom, MarginBottom_h)
switch n.Data {
case "h1", "h2":
height += float64(HeaderToFontSize[n.Data]) * PaddingBottom_h1_h2_em
height += PaddingBottom_h1_h2_em * float64(font.Size)
}
case "li":
width += PaddingLeft_ul_ol
@ -314,11 +355,20 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
width += 2 * Padding_pre
height += 2 * Padding_pre
marginBottom = go2.Max(marginBottom, MarginBottom_pre)
case "code":
if parentElementType != "pre" {
width += 2 * PaddingLeftRight_code_em * float64(font.Size)
height += 2 * PaddingTopBottom_code_em * float64(font.Size)
}
case "hr":
height += Height_hr
marginTop = go2.Max(marginTop, MarginTopBottom_hr)
marginBottom = go2.Max(marginBottom, MarginTopBottom_hr)
}
if height > 0 && height < MarkdownLineHeightPx {
height = MarkdownLineHeightPx
}
// fmt.Printf("%d:%s width %v height %v mt %v mb %v\n", depth, n.Data, width, height, marginTop, marginBottom)
}
return width, height, marginTop, marginBottom

View file

@ -13,7 +13,7 @@ func testTodo(t *testing.T) {
skip: false,
name: "md_code_inline",
script: `md: |md
` + "`" + "code`" + `
` + "`code`" + `
|
a -> md -> b
`,