diff options
-rw-r--r-- | content.go | 104 | ||||
-rw-r--r-- | parse_test.go | 64 | ||||
-rw-r--r-- | write_test.go | 2 |
3 files changed, 118 insertions, 52 deletions
@@ -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", |