Skip to content

Commit 4260fa1

Browse files
committed
Added codeblock format and improved rendering performance and FormatWrapper interface
1 parent f021615 commit 4260fa1

File tree

9 files changed

+139
-53
lines changed

9 files changed

+139
-53
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fmt.Println(string(html))
3939
- Indent
4040
- List (ul and ol, including nested lists)
4141
- Text alignment
42+
- Code block
4243

4344
### Embeds
4445
- Image (an inline format)

block_formats.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,48 @@ func (inf *indentFormat) Fmt() *Format {
149149
func (inf *indentFormat) HasFormat(o *Op) bool {
150150
return o.Attrs["indent"] == inf.in
151151
}
152+
153+
// code block
154+
type codeBlockFormat struct {
155+
o *Op
156+
}
157+
158+
func (cf *codeBlockFormat) Fmt() *Format {
159+
return &Format{
160+
Place: Tag,
161+
Block: true,
162+
}
163+
}
164+
165+
func (cf *codeBlockFormat) HasFormat(o *Op) bool {
166+
return false // Only a wrapper.
167+
}
168+
169+
// codeBlockFormat implements the FormatWrapper interface.
170+
func (*codeBlockFormat) Wrap() (string, string) {
171+
return "<pre>", "\n</pre>"
172+
}
173+
174+
// codeBlockFormat implements the FormatWrapper interface.
175+
func (*codeBlockFormat) Open(open []*Format, _ *Op) bool {
176+
// If there is a code block already open, no need to open another.
177+
for i := range open {
178+
if open[i].Place == Tag && open[i].Val == "<pre>" {
179+
return false
180+
}
181+
}
182+
return true
183+
}
184+
185+
// codeBlockFormat implements the FormatWrapper interface.
186+
func (cf *codeBlockFormat) Close(_ []*Format, o *Op, doingBlock bool) bool {
187+
if doingBlock && !o.HasAttr("code-block") {
188+
return true
189+
}
190+
// We are simply adding another line to the code block. The previous line should end with
191+
// a "\n" though all instances of "\n" are stripped out by the split from the start.
192+
if !doingBlock {
193+
o.Data = "\n" + o.Data
194+
}
195+
return false
196+
}

inline_formats.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ type linkFormat struct {
7171
func (*linkFormat) Fmt() *Format { return new(Format) } // Only a wrapper.
7272

7373
func (lf *linkFormat) HasFormat(*Op) bool {
74-
return false
74+
return false // Only a wrapper.
7575
}
7676

7777
func (lf *linkFormat) Wrap() (string, string) {

render.go

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -62,44 +62,27 @@ func RenderExtended(ops []byte, customFormats func(string, *Op) Formatter) (html
6262
// Open the a block element, write its body, and close it to move on only when the ending "\n" of the block is reached.
6363
if strings.IndexByte(o.Data, '\n') != -1 {
6464

65-
if o.Data == "\n" { // Write a block element and flush the temporary buffer.
65+
// Extract text from between the block-terminating line feeds and write each part as its own Op.
66+
split := strings.Split(o.Data, "\n")
6667

67-
// Avoid empty paragraphs and "\n" in the output.
68-
if tempBuf.Len() == 0 {
69-
o.Data = "<br>"
70-
} else {
71-
o.Data = ""
72-
}
73-
74-
o.writeBlock(fs, tempBuf, finalBuf, fms)
75-
76-
} else { // Extract the block-terminating line feeds and write each part as its own Op.
77-
78-
split := strings.Split(o.Data, "\n")
68+
for i := range split {
7969

80-
for i := range split {
70+
o.Data = split[i]
8171

82-
o.Data = split[i]
72+
// If the current o.Data still has an "\n" following (its not the last in split), then it ends a block.
73+
if i < len(split)-1 {
8374

84-
// If the current o.Data still has an "\n" following (its not the last in split), then it ends a block.
85-
if i < len(split)-1 {
75+
o.writeBlock(fs, tempBuf, finalBuf, fms)
8676

87-
// Avoid having empty paragraphs.
88-
if tempBuf.Len() == 0 && o.Data == "" {
89-
o.Data = "<br>"
90-
}
77+
} else if o.Data != "" { // If the last element in split is just "" then the last character in the rawOp is "\n".
9178

92-
o.writeBlock(fs, tempBuf, finalBuf, fms)
93-
94-
} else if o.Data != "" { // If the last element in split is just "" then the last character in the rawOp is "\n".
95-
o.writeInline(fs, tempBuf, fms)
96-
}
79+
o.writeInline(fs, tempBuf, fms)
9780

9881
}
9982

10083
}
10184

102-
} else { // We are just adding stuff inline.
85+
} else {
10386
o.writeInline(fs, tempBuf, fms)
10487
}
10588

@@ -154,19 +137,6 @@ type Op struct {
154137
func (o *Op) writeBlock(fs *formatState, tempBuf *bytes.Buffer, finalBuf *bytes.Buffer, newFms []*Format) {
155138

156139
// Close the inline formats opened within the block to the tempBuf and block formats of wrappers to finalBuf.
157-
//wrapFs := &formatState{make([]*Format, 0, 2)}
158-
//inlineFs := &formatState{make([]*Format, 0, 2)}
159-
//for i := range fs.open {
160-
// if fs.open[i].wrap {
161-
// wrapFs.add(fs.open[i])
162-
// } else {
163-
// inlineFs.add(fs.open[i])
164-
// }
165-
//}
166-
//wrapFs.closePrevious(finalBuf, o, true)
167-
//inlineFs.closePrevious(tempBuf, o, true)
168-
//fs.open = append(wrapFs.open, inlineFs.open...) // There may be something left open.
169-
//fs.closePrevious(tempBuf, o, true)
170140
closedTemp := &formatState{}
171141

172142
for i := len(fs.open) - 1; i >= 0; i-- { // Start with the last format opened.
@@ -208,11 +178,6 @@ func (o *Op) writeBlock(fs *formatState, tempBuf *bytes.Buffer, finalBuf *bytes.
208178
style string
209179
}
210180

211-
// At least a format from the Op.Type should be set.
212-
if len(newFms) == 0 {
213-
return
214-
}
215-
216181
// Merge all formats into a single tag.
217182
for i := range newFms {
218183
fm := newFms[i]
@@ -222,9 +187,7 @@ func (o *Op) writeBlock(fs *formatState, tempBuf *bytes.Buffer, finalBuf *bytes.
222187
switch fm.Place {
223188
case Tag:
224189
// If an opening tag is not specified by the Op insert type, it may be specified by an attribute.
225-
if v != "" {
226-
block.tagName = v // Override whatever value is set.
227-
}
190+
block.tagName = v // Override whatever value is set.
228191
case Class:
229192
block.classes = append(block.classes, v)
230193
case Style:
@@ -239,6 +202,11 @@ func (o *Op) writeBlock(fs *formatState, tempBuf *bytes.Buffer, finalBuf *bytes.
239202
}
240203
}
241204

205+
// Avoid empty paragraphs and "\n" in the output for text blocks.
206+
if o.Data == "" && block.tagName == "p" && tempBuf.Len() == 0 {
207+
o.Data = "<br>"
208+
}
209+
242210
if block.tagName != "" {
243211
finalBuf.WriteByte('<')
244212
finalBuf.WriteString(block.tagName)
@@ -258,9 +226,6 @@ func (o *Op) writeBlock(fs *formatState, tempBuf *bytes.Buffer, finalBuf *bytes.
258226
closeTag(finalBuf, block.tagName)
259227
}
260228

261-
// Write out the closes by FormatWrapper formats, starting from the last written.
262-
//fs.closePrevious(finalBuf, o, true)
263-
264229
tempBuf.Reset()
265230

266231
}
@@ -368,6 +333,8 @@ func (o *Op) getFormatter(keyword string, customFormats func(string, *Op) Format
368333
sf.t = "sub"
369334
}
370335
return sf
336+
case "code-block":
337+
return &codeBlockFormat{o}
371338
}
372339

373340
return nil

render_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func TestSimple(t *testing.T) {
5757

5858
func TestRender(t *testing.T) {
5959

60-
pairNames := []string{"ops1", "nested", "ordering", "list1", "list2", "list3", "list4", "indent"}
60+
pairNames := []string{"ops1", "nested", "ordering", "list1", "list2", "list3", "list4", "indent", "code1", "code2"}
6161

6262
for _, n := range pairNames {
6363

tests/code1.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<pre>some code
2+
and more
3+
</pre><p>plain text</p><pre>more code
4+
</pre>

tests/code1.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[
2+
{
3+
"insert": "some code"
4+
},
5+
{
6+
"attributes": {
7+
"code-block": true
8+
},
9+
"insert": "\n"
10+
},
11+
{
12+
"insert": " and more"
13+
},
14+
{
15+
"attributes": {
16+
"code-block": true
17+
},
18+
"insert": "\n"
19+
},
20+
{
21+
"insert": "plain text\nmore code"
22+
},
23+
{
24+
"attributes": {
25+
"code-block": true
26+
},
27+
"insert": "\n"
28+
}
29+
]

tests/code2.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<pre>
2+
some code
3+
and more
4+
</pre><p>plain text</p><pre>more code
5+
</pre>

tests/code2.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[
2+
{
3+
"attributes": {
4+
"code-block": true
5+
},
6+
"insert": "\n"
7+
},
8+
{
9+
"insert": "some code"
10+
},
11+
{
12+
"attributes": {
13+
"code-block": true
14+
},
15+
"insert": "\n"
16+
},
17+
{
18+
"insert": " and more"
19+
},
20+
{
21+
"attributes": {
22+
"code-block": true
23+
},
24+
"insert": "\n"
25+
},
26+
{
27+
"insert": "plain text\nmore code"
28+
},
29+
{
30+
"attributes": {
31+
"code-block": true
32+
},
33+
"insert": "\n"
34+
}
35+
]

0 commit comments

Comments
 (0)