summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content.go104
-rw-r--r--parse_test.go64
-rw-r--r--write_test.go2
3 files changed, 118 insertions, 52 deletions
diff --git a/content.go b/content.go
index 971e9e3..c2ba347 100644
--- a/content.go
+++ b/content.go
@@ -18,9 +18,18 @@ type Block interface {
// Args returns the block arguments.
Args() []string
+
WriteIndent(w io.Writer, n int) error
}
+// ContainerBlock represents a content block that contains only other blocks.
+type ContainerBlock interface {
+ Block
+
+ // Children returns the child blocks.
+ Children() []Block
+}
+
// ContentBlock represents a block that holds other content blocks.
type ContentBlock struct {
name string
@@ -190,8 +199,7 @@ func parseTextFormat(p *Parser, block *TokenBlock, format string) (TextContents,
if parser := GetTextContentParser(format); parser != nil {
return parser(p, block)
}
- r, err := parseContentRaw(p, block)
- return TextPreContents{r.Contents}, err
+ return parseTextRaw(p, block)
}
// TextContents represents the textual contents of a text block.
@@ -327,13 +335,60 @@ func parseTextPre(p *Parser, block *TokenBlock) (TextContents, error) {
return TextPreContents{strings.Join(lines, "\n")}, err
}
+// TextRawContents represents raw (unesacped) contents of a text or raw block.
+type TextRawContents struct {
+ // Text is the raw content.
+ Text string
+}
+
+// WriteIndent writes the raw content indented by n tabs.
+func (t TextRawContents) WriteIndent(w io.Writer, n int) error {
+ ss := strings.Split(t.Text, "\n")
+ for _, s := range ss {
+ if err := writeIndent(w, s, n); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func parseTextRaw(p *Parser, block *TokenBlock) (TextContents, error) {
+ var lines []string
+ var ls []string
+ var err error
+ for err == nil {
+ if !p.Empty() && p.Indent() <= block.Indent() {
+ break
+ }
+ token := p.RawText()
+ if text, ok := token.(*TokenRawText); ok {
+ if len(ls) > 0 {
+ lines = append(lines, ls...)
+ ls = ls[:0]
+ }
+ lines = append(lines, text.Text)
+ } else if _, ok := token.(*TokenEmptyLine); ok && len(lines) > 0 {
+ ls = append(ls, "")
+ }
+ err = p.Next()
+ }
+
+ return TextRawContents{strings.Join(lines, "\n")}, err
+}
+
// RawBlock represents a "raw" content block.
type RawBlock struct {
// Syntax is the syntax of the block contents (first word of block argument)
Syntax string
// Contents is the raw content.
- Contents string
+ Contents TextRawContents
+}
+
+// NewRawBlock creates a new RawBlock.
+func NewRawBlock(syntax, contents string) *RawBlock {
+ return &RawBlock{syntax, TextRawContents{contents}}
}
// Name returns the block name "raw".
@@ -356,14 +411,7 @@ func (r *RawBlock) WriteIndent(w io.Writer, n int) error {
if err := writeIndent(w, s, n); err != nil {
return err
}
- if r.Contents != "" {
- ss := strings.Split(r.Contents, "\n")
- for _, s := range ss {
- if err := writeIndent(w, s, n+1); err != nil {
- return err
- }
- }
- }
+ r.Contents.WriteIndent(w, n+1)
return nil
}
@@ -372,33 +420,16 @@ func parseContentRaw(p *Parser, block *TokenBlock) (*RawBlock, error) {
if len(block.Args) > 0 {
arg = block.Args[0]
}
- rb := &RawBlock{arg, ""}
+ rb := &RawBlock{arg, TextRawContents{}}
if err := p.Next(); err != nil {
return rb, err
}
- var lines []string
- var ls []string
- var err error
- for err == nil {
- if !p.Empty() && p.Indent() <= block.Indent() {
- break
- }
-
- token := p.RawText()
- if text, ok := token.(*TokenRawText); ok {
- if len(ls) > 0 {
- lines = append(lines, ls...)
- ls = ls[:0]
- }
- lines = append(lines, text.Text)
- } else if _, ok := token.(*TokenEmptyLine); ok && len(lines) > 0 {
- ls = append(ls, "")
- }
- err = p.Next()
+ tc, err := parseTextRaw(p, block)
+ if tc != nil {
+ rb.Contents = tc.(TextRawContents)
}
- rb.Contents = strings.Join(lines, "\n")
return rb, err
}
@@ -467,8 +498,8 @@ func (t *TableBlock) WriteIndent(w io.Writer, n int) error {
return nil
}
-// Rows returns the table's rows.
-func (t *TableBlock) Rows() []Block {
+// Children returns the table's rows.
+func (t *TableBlock) Children() []Block {
return t.rows
}
@@ -560,6 +591,11 @@ type EmbedBlock struct {
Description string
}
+// NewEmbedBlock creates a new EmbedBlock.
+func NewEmbedBlock(typ, url, desc string) *EmbedBlock {
+ return &EmbedBlock{typ, url, desc}
+}
+
// Name returns the block name "embed".
func (e *EmbedBlock) Name() string { return "embed" }
diff --git a/parse_test.go b/parse_test.go
index 3fbf0f6..fc0ef87 100644
--- a/parse_test.go
+++ b/parse_test.go
@@ -125,6 +125,19 @@ var parseTests = map[string]*Document{
},
},
+ "content\n\ttext __nonexistent__\n\t\tqwe zxc\n\n\t\tasd\\n123": &Document{
+ Content: &ContentBlock{
+ name: "content",
+ args: nil,
+ children: []Block{
+ &TextBlock{
+ Format: "__nonexistent__",
+ Contents: TextRawContents{"qwe zxc\n\nasd\\n123"},
+ },
+ },
+ },
+ },
+
"content\n\tnosuchblock\n\tsection test\n": &Document{
Content: &ContentBlock{
name: "content",
@@ -360,7 +373,7 @@ content
},
&RawBlock{
Syntax: "text/plain",
- Contents: "of various \\n features",
+ Contents: TextRawContents{"of various \\n features"},
},
&SectionBlock{ContentBlock{
name: "section",
@@ -462,7 +475,7 @@ func TestParse(t *testing.T) {
t.Fatalf("ParseDocument(%q): error: %v", k, err)
}
if !documentEqual(d, v) {
- t.Fatalf("ParseDocument(%q):\nexpected:\n%#v\n got:\n%#v", k, v, d)
+ t.Fatalf("ParseDocument(%q):\nexpected:\n%#v\n got:\n%#v\n\n%#v\n%#v", k, v, d, v.Content.Children()[0], d.Content.Children()[0])
}
})
}
@@ -591,15 +604,9 @@ func contentBlockEqual(a, b *ContentBlock) bool {
if a.Name() != b.Name() {
return false
}
- aa, ba := a.Args(), b.Args()
- if len(aa) != len(ba) {
+ if !argsEqual(a.Args(), b.Args()) {
return false
}
- for i := range aa {
- if aa[i] != ba[i] {
- return false
- }
- }
ca, cb := a.Children(), b.Children()
if len(ca) != len(cb) {
return false
@@ -612,12 +619,24 @@ func contentBlockEqual(a, b *ContentBlock) bool {
return true
}
+func argsEqual(a, b []string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i := range a {
+ if a[i] != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
func sectionBlockEqual(a, b *SectionBlock) bool {
- return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+ return a.Title() == b.Title() && contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
}
func textBlockEqual(a, b *TextBlock) bool {
- if a.Format != b.Format {
+ if !argsEqual(a.Args(), b.Args()) {
return false
}
return textContentsEqual(a.Contents, b.Contents)
@@ -639,7 +658,14 @@ func textContentsEqual(a, b TextContents) bool {
}
return textPreContentsEqual(va, vb)
- default:
+ case TextRawContents:
+ vb, ok := b.(TextRawContents)
+ if !ok {
+ return false
+ }
+ return textRawContentsEqual(va, vb)
+
+ default: // handle unknown contents, e.g. text fmt
return reflect.TypeOf(a) == reflect.TypeOf(b) && reflect.DeepEqual(a, b)
}
}
@@ -660,16 +686,20 @@ func textPreContentsEqual(a, b TextPreContents) bool {
return a == b
}
+func textRawContentsEqual(a, b TextRawContents) bool {
+ return a.Text == b.Text
+}
+
func rawBlockEqual(a, b *RawBlock) bool {
- return *a == *b
+ return argsEqual(a.Args(), b.Args()) && textRawContentsEqual(a.Contents, b.Contents)
}
func listBlockEqual(a, b *ListBlock) bool {
- return contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
+ return a.Ordered() == b.Ordered() && contentBlockEqual(&a.ContentBlock, &b.ContentBlock)
}
func tableBlockEqual(a, b *TableBlock) bool {
- ra, rb := a.Rows(), b.Rows()
+ ra, rb := a.Children(), b.Children()
if len(ra) != len(rb) {
return false
}
@@ -678,7 +708,7 @@ func tableBlockEqual(a, b *TableBlock) bool {
return false
}
}
- return true
+ return argsEqual(a.Args(), b.Args())
}
func rowBlockEqual(a, b *RowBlock) bool {
@@ -690,5 +720,5 @@ func headerBlockEqual(a, b *HeaderBlock) bool {
}
func embedBlockEqual(a, b *EmbedBlock) bool {
- return *a == *b
+ return argsEqual(a.Args(), b.Args())
}
diff --git a/write_test.go b/write_test.go
index fc13459..bf829a0 100644
--- a/write_test.go
+++ b/write_test.go
@@ -102,7 +102,7 @@ content
},
&RawBlock{
Syntax: "text/plain",
- Contents: "of various \\n features",
+ Contents: TextRawContents{"of various \\n features"},
},
&SectionBlock{ContentBlock{
name: "section",