mirror of
https://github.com/osmarks/mycorrhiza.git
synced 2024-12-12 13:30:26 +00:00
Add img tag
This commit is contained in:
parent
9ad22b5cf8
commit
3620c6e9b5
140
markup/img.go
Normal file
140
markup/img.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package markup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var imgRe = regexp.MustCompile(`^img\s+{`)
|
||||||
|
|
||||||
|
func MatchesImg(line string) bool {
|
||||||
|
return imgRe.MatchString(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
type imgEntry struct {
|
||||||
|
path string
|
||||||
|
sizeH string
|
||||||
|
sizeV string
|
||||||
|
desc string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Img struct {
|
||||||
|
entries []imgEntry
|
||||||
|
inDesc bool
|
||||||
|
hyphaName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (img *Img) Process(line string) (shouldGoBackToNormal bool) {
|
||||||
|
if img.inDesc {
|
||||||
|
rightBraceIndex := strings.IndexRune(line, '}')
|
||||||
|
if cnt := len(img.entries); rightBraceIndex == -1 && cnt != 0 {
|
||||||
|
img.entries[cnt-1].desc += "\n" + line
|
||||||
|
} else if rightBraceIndex != -1 && cnt != 0 {
|
||||||
|
img.entries[cnt-1].desc += "\n" + line[:rightBraceIndex]
|
||||||
|
img.inDesc = false
|
||||||
|
}
|
||||||
|
if strings.Count(line, "}") > 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if s := strings.TrimSpace(line); s != "" {
|
||||||
|
if s[0] == '}' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
img.parseStartOfEntry(line)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImgFromFirstLine(line, hyphaName string) Img {
|
||||||
|
img := Img{
|
||||||
|
hyphaName: hyphaName,
|
||||||
|
entries: make([]imgEntry, 0),
|
||||||
|
}
|
||||||
|
line = line[strings.IndexRune(line, '{'):]
|
||||||
|
if len(line) == 1 { // if { only
|
||||||
|
} else {
|
||||||
|
line = line[1:] // Drop the {
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func (img *Img) canonicalPathFor(path string) string {
|
||||||
|
path = strings.TrimSpace(path)
|
||||||
|
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
|
||||||
|
return path
|
||||||
|
} else {
|
||||||
|
return "/binary/" + xclCanonicalName(img.hyphaName, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (img *Img) parseStartOfEntry(line string) (entry imgEntry, followedByDesc bool) {
|
||||||
|
pipeIndex := strings.IndexRune(line, '|')
|
||||||
|
if pipeIndex == -1 { // If no : in string
|
||||||
|
entry.path = img.canonicalPathFor(line)
|
||||||
|
} else {
|
||||||
|
entry.path = img.canonicalPathFor(line[:pipeIndex])
|
||||||
|
line = strings.TrimPrefix(line, line[:pipeIndex+1])
|
||||||
|
|
||||||
|
var (
|
||||||
|
leftBraceIndex = strings.IndexRune(line, '{')
|
||||||
|
rightBraceIndex = strings.IndexRune(line, '}')
|
||||||
|
dimensions string
|
||||||
|
)
|
||||||
|
|
||||||
|
if leftBraceIndex == -1 {
|
||||||
|
dimensions = line
|
||||||
|
} else {
|
||||||
|
dimensions = line[:leftBraceIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeH, sizeV := parseDimensions(dimensions)
|
||||||
|
entry.sizeH = sizeH
|
||||||
|
entry.sizeV = sizeV
|
||||||
|
|
||||||
|
if leftBraceIndex != -1 && rightBraceIndex == -1 {
|
||||||
|
img.inDesc = true
|
||||||
|
followedByDesc = true
|
||||||
|
entry.desc = strings.TrimPrefix(line, line[:leftBraceIndex+1])
|
||||||
|
} else if leftBraceIndex != -1 && rightBraceIndex != -1 {
|
||||||
|
entry.desc = line[leftBraceIndex+1 : rightBraceIndex]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.entries = append(img.entries, entry)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDimensions(dimensions string) (sizeH, sizeV string) {
|
||||||
|
xIndex := strings.IndexRune(dimensions, '*')
|
||||||
|
if xIndex == -1 { // If no x in dimensions
|
||||||
|
sizeH = strings.TrimSpace(dimensions)
|
||||||
|
} else {
|
||||||
|
sizeH = strings.TrimSpace(dimensions[:xIndex])
|
||||||
|
sizeV = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1]))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (img Img) ToHtml() (html string) {
|
||||||
|
for _, entry := range img.entries {
|
||||||
|
html += fmt.Sprintf(`<figure>
|
||||||
|
<img src="%s" width="%s" height="%s">
|
||||||
|
`, entry.path, entry.sizeH, entry.sizeV)
|
||||||
|
if entry.desc != "" {
|
||||||
|
html += ` <figcaption>`
|
||||||
|
for i, line := range strings.Split(entry.desc, "\n") {
|
||||||
|
if line != "" {
|
||||||
|
if i > 0 {
|
||||||
|
html += `<br>`
|
||||||
|
}
|
||||||
|
html += ParagraphToHtml(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html += `</figcaption>`
|
||||||
|
}
|
||||||
|
html += `</figure>`
|
||||||
|
}
|
||||||
|
return `<section class="img-gallery">
|
||||||
|
` + html + `
|
||||||
|
</section>`
|
||||||
|
}
|
47
markup/img_test.go
Normal file
47
markup/img_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package markup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseStartOfEntry(t *testing.T) {
|
||||||
|
img := ImgFromFirstLine("img {", "h")
|
||||||
|
tests := []struct {
|
||||||
|
line string
|
||||||
|
entry imgEntry
|
||||||
|
followedByDesc bool
|
||||||
|
}{
|
||||||
|
{"apple", imgEntry{"apple", "", "", ""}, false},
|
||||||
|
{"pear:", imgEntry{"pear", "", "", ""}, false},
|
||||||
|
{"яблоко: 30*60", imgEntry{"яблоко", "30", "60", ""}, false},
|
||||||
|
{"груша : 65 ", imgEntry{"груша", "65", "", ""}, false},
|
||||||
|
{"жеронимо : 30 { full desc }", imgEntry{"жеронимо", "30", "", " full desc "}, false},
|
||||||
|
{"жорно жованна : *5555 {partial description", imgEntry{"жорно_жованна", "", "5555", "partial description"}, true},
|
||||||
|
{"иноске : {full}", imgEntry{"иноске", "", "", "full"}, false},
|
||||||
|
{"j:{partial", imgEntry{"j", "", "", "partial"}, true},
|
||||||
|
}
|
||||||
|
for _, triplet := range tests {
|
||||||
|
entry, followedByDesc := img.parseStartOfEntry(triplet.line)
|
||||||
|
if entry != triplet.entry || followedByDesc != triplet.followedByDesc {
|
||||||
|
t.Error(fmt.Sprintf("%q:%q != %q; %v != %v", triplet.line, entry, triplet.entry, followedByDesc, triplet.followedByDesc))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseDimensions(t *testing.T) {
|
||||||
|
tests := [][]string{
|
||||||
|
{"500", "500", ""},
|
||||||
|
{"3em", "3em", ""},
|
||||||
|
{"500*", "500", ""},
|
||||||
|
{"*500", "", "500"},
|
||||||
|
{"800*520", "800", "520"},
|
||||||
|
{"17%*5rem", "17%", "5rem"},
|
||||||
|
}
|
||||||
|
for _, triplet := range tests {
|
||||||
|
sizeH, sizeV := parseDimensions(triplet[0])
|
||||||
|
if sizeH != triplet[1] || sizeV != triplet[2] {
|
||||||
|
t.Error(sizeH, "*", sizeV, " != ", triplet[1], "*", triplet[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,8 @@ type GemLexerState struct {
|
|||||||
// Line id
|
// Line id
|
||||||
id int
|
id int
|
||||||
buf string
|
buf string
|
||||||
|
// Temporaries
|
||||||
|
img Img
|
||||||
}
|
}
|
||||||
|
|
||||||
type Line struct {
|
type Line struct {
|
||||||
@ -99,6 +101,8 @@ func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
|
|||||||
|
|
||||||
// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.
|
// Beware! Usage of goto. Some may say it is considered evil but in this case it helped to make a better-structured code.
|
||||||
switch state.where {
|
switch state.where {
|
||||||
|
case "img":
|
||||||
|
goto imgState
|
||||||
case "pre":
|
case "pre":
|
||||||
goto preformattedState
|
goto preformattedState
|
||||||
case "list":
|
case "list":
|
||||||
@ -107,6 +111,13 @@ func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
|
|||||||
goto normalState
|
goto normalState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imgState:
|
||||||
|
if shouldGoBackToNormal := state.img.Process(line); shouldGoBackToNormal {
|
||||||
|
state.where = ""
|
||||||
|
addLine(state.img)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
preformattedState:
|
preformattedState:
|
||||||
switch {
|
switch {
|
||||||
case startsWith("```"):
|
case startsWith("```"):
|
||||||
@ -178,6 +189,9 @@ normalState:
|
|||||||
addLine(parseTransclusion(line, state.name))
|
addLine(parseTransclusion(line, state.name))
|
||||||
case line == "----":
|
case line == "----":
|
||||||
*ast = append(*ast, Line{id: -1, contents: "<hr/>"})
|
*ast = append(*ast, Line{id: -1, contents: "<hr/>"})
|
||||||
|
case MatchesImg(line):
|
||||||
|
state.where = "img"
|
||||||
|
state.img = ImgFromFirstLine(line, state.name)
|
||||||
default:
|
default:
|
||||||
addLine(fmt.Sprintf("<p id='%d'>%s</p>", state.id, ParagraphToHtml(line)))
|
addLine(fmt.Sprintf("<p id='%d'>%s</p>", state.id, ParagraphToHtml(line)))
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ func TestLex(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i, e := range ast {
|
for i, e := range ast {
|
||||||
if e != expectedAst[i] {
|
if !reflect.DeepEqual(e, expectedAst[i]) {
|
||||||
t.Error("Mismatch when lexing", name, "\nExpected:", expectedAst[i], "\nGot:", e)
|
t.Error(fmt.Sprintf("Expected: %q\nGot:%q", expectedAst[i], e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ func TestLex(t *testing.T) {
|
|||||||
{7, "<p id='7'>more text</p>"},
|
{7, "<p id='7'>more text</p>"},
|
||||||
{8, `<p><a id='8' class='wikilink_internal' href="/page/Pear">some link</a></p>`},
|
{8, `<p><a id='8' class='wikilink_internal' href="/page/Pear">some link</a></p>`},
|
||||||
{9, `<ul id='9'>
|
{9, `<ul id='9'>
|
||||||
<li>li\n"+</li>
|
<li>lin"+</li>
|
||||||
</ul>`},
|
</ul>`},
|
||||||
{10, `<pre id='10' alt='alt text goes here' class='codeblock'><code>=> preformatted text
|
{10, `<pre id='10' alt='alt text goes here' class='codeblock'><code>=> preformatted text
|
||||||
where markup is not lexed</code></pre>`},
|
where markup is not lexed</code></pre>`},
|
||||||
@ -51,7 +51,17 @@ where markup is not lexed</code></pre>`},
|
|||||||
{12, "<p id='12'>text</p>"},
|
{12, "<p id='12'>text</p>"},
|
||||||
{13, `<pre id='13' alt='' class='codeblock'><code>()
|
{13, `<pre id='13' alt='' class='codeblock'><code>()
|
||||||
/\</code></pre>`},
|
/\</code></pre>`},
|
||||||
// More thorough testing of xclusions is done in xclusion_test.go
|
|
||||||
{14, Transclusion{"apple", 1, 3}},
|
{14, Transclusion{"apple", 1, 3}},
|
||||||
|
{15, Img{
|
||||||
|
hyphaName: "Apple",
|
||||||
|
inDesc: false,
|
||||||
|
entries: []imgEntry{
|
||||||
|
{"hypha1", "", "", ""},
|
||||||
|
{"hypha2", "", "", ""},
|
||||||
|
{"hypha3", "60", "", ""},
|
||||||
|
{"hypha4", "", "", " line1\nline2\n"},
|
||||||
|
{"hypha5", "", "", "\nstate of minnesota\n"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ func TestParagraphToHtml(t *testing.T) {
|
|||||||
{"Embedded //italic//", "Embedded <em>italic</em>"},
|
{"Embedded //italic//", "Embedded <em>italic</em>"},
|
||||||
{"double //italian// //text//", "double <em>italian</em> <em>text</em>"},
|
{"double //italian// //text//", "double <em>italian</em> <em>text</em>"},
|
||||||
{"it has `mono`", "it has <code>mono</code>"},
|
{"it has `mono`", "it has <code>mono</code>"},
|
||||||
{"this is a left **bold", "this is a left <strong>bold"},
|
{"this is a left **bold", "this is a left <strong>bold</strong>"},
|
||||||
{"this line has a ,comma, two of them", "this line has a ,comma, two of them"},
|
{"this line has a ,comma, two of them", "this line has a ,comma, two of them"},
|
||||||
{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."},
|
{"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."},
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ func Parse(ast []Line, from, to int, state GemParserState) (html string) {
|
|||||||
switch v := line.contents.(type) {
|
switch v := line.contents.(type) {
|
||||||
case Transclusion:
|
case Transclusion:
|
||||||
html += Transclude(v, state)
|
html += Transclude(v, state)
|
||||||
|
case Img:
|
||||||
|
html += v.ToHtml()
|
||||||
case string:
|
case string:
|
||||||
html += v
|
html += v
|
||||||
}
|
}
|
||||||
|
14
markup/testdata/test.myco
vendored
14
markup/testdata/test.myco
vendored
@ -22,3 +22,17 @@ text
|
|||||||
/\
|
/\
|
||||||
```
|
```
|
||||||
<= Apple : 1..3
|
<= Apple : 1..3
|
||||||
|
|
||||||
|
img {
|
||||||
|
hypha1
|
||||||
|
hypha2:
|
||||||
|
hypha3: 60
|
||||||
|
hypha4: { line1
|
||||||
|
line2
|
||||||
|
} this is ignored
|
||||||
|
|
||||||
|
hypha5: {
|
||||||
|
state of minnesota
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 2e2e3a70f9de67b54f912dd9fdaa04497bf795b0
|
Subproject commit a54be905923b10524d74435fb62dbdd9a0aac06a
|
@ -33,6 +33,8 @@ p code {background-color:#eee; padding: .1rem .3rem; border-radius: .25rem; font
|
|||||||
.binary-container_with-video video,
|
.binary-container_with-video video,
|
||||||
.binary-container_with-audio audio {width: 100%}
|
.binary-container_with-audio audio {width: 100%}
|
||||||
.navi-title a {text-decoration:none;}
|
.navi-title a {text-decoration:none;}
|
||||||
|
.img-gallery img { max-width: 100%; }
|
||||||
|
figure { margin: 0; }
|
||||||
|
|
||||||
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
|
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
|
||||||
nav ul li {list-style-type:none;margin-right:1rem;}
|
nav ul li {list-style-type:none;margin-right:1rem;}
|
||||||
|
@ -55,37 +55,39 @@ p code {background-color:#eee; padding: .1rem .3rem; border-radius: .25rem; font
|
|||||||
.binary-container_with-video video,
|
.binary-container_with-video video,
|
||||||
.binary-container_with-audio audio {width: 100%}
|
.binary-container_with-audio audio {width: 100%}
|
||||||
.navi-title a {text-decoration:none;}
|
.navi-title a {text-decoration:none;}
|
||||||
|
.img-gallery img { max-width: 100%; }
|
||||||
|
figure { margin: 0; }
|
||||||
|
|
||||||
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
|
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
|
||||||
nav ul li {list-style-type:none;margin-right:1rem;}
|
nav ul li {list-style-type:none;margin-right:1rem;}
|
||||||
|
|
||||||
#new-name {width:100%;}
|
#new-name {width:100%;}
|
||||||
`)
|
`)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
}
|
}
|
||||||
|
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
func WriteDefaultCSS(qq422016 qtio422016.Writer) {
|
func WriteDefaultCSS(qq422016 qtio422016.Writer) {
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
StreamDefaultCSS(qw422016)
|
StreamDefaultCSS(qw422016)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
}
|
}
|
||||||
|
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
func DefaultCSS() string {
|
func DefaultCSS() string {
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
WriteDefaultCSS(qb422016)
|
WriteDefaultCSS(qb422016)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
return qs422016
|
return qs422016
|
||||||
//line templates/css.qtpl:41
|
//line templates/css.qtpl:43
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user