diff --git a/cmd/root.go b/cmd/root.go
index 5b007424..ffefc09e 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -24,6 +24,7 @@ import (
"github.com/blackstork-io/fabric/cmd/fabctx"
"github.com/blackstork-io/fabric/cmd/internal/multilog"
"github.com/blackstork-io/fabric/cmd/internal/telemetry"
+ "github.com/blackstork-io/fabric/pkg/utils/slogutil"
)
var (
@@ -147,7 +148,7 @@ var rootCmd = &cobra.Command{
}
var logger *slog.Logger
if env.otelpEnabled || rawArgs.debug {
- logger = slog.New(multilog.Handler{
+ handler = multilog.Handler{
Level: level,
Handlers: []slog.Handler{
handler,
@@ -156,10 +157,10 @@ var rootCmd = &cobra.Command{
otelslog.WithVersion(version),
),
},
- })
- } else {
- logger = slog.New(handler)
+ }
}
+ handler = slogutil.NewSourceRewriter(handler)
+ logger = slog.New(handler)
logger = logger.With("command", cmd.Name())
slog.SetDefault(logger)
slog.SetLogLoggerLevel(slog.LevelDebug)
diff --git a/examples/plugins/basic/content_greeting.go b/examples/plugins/basic/content_greeting.go
index e5937008..5f312f7e 100644
--- a/examples/plugins/basic/content_greeting.go
+++ b/examples/plugins/basic/content_greeting.go
@@ -36,8 +36,6 @@ func renderGreetingMessage(ctx context.Context, params *plugin.ProvideContentPar
// that it exists, non-null and non-empty, with whitespace trimmed
name := params.Args.GetAttrVal("name").AsString()
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: fmt.Sprintf("Hello, %s!", name),
- },
+ Content: plugin.NewElementFromMarkdown(fmt.Sprintf("Hello, %s!", name)),
}, nil
}
diff --git a/go.mod b/go.mod
index 6b28ed09..a1540177 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/Masterminds/sprig/v3 v3.2.3
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
+ github.com/blackstork-io/goldmark-markdown v0.1.3
github.com/elastic/go-elasticsearch/v8 v8.14.0
github.com/evanphx/go-hclog-slog v0.0.0-20240717231540-be48fc4c4df5
github.com/gobwas/glob v0.2.3
@@ -34,7 +35,7 @@ require (
github.com/testcontainers/testcontainers-go/modules/elasticsearch v0.32.0
github.com/testcontainers/testcontainers-go/modules/postgres v0.32.0
github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.46
- github.com/yuin/goldmark v1.7.1
+ github.com/yuin/goldmark v1.7.4
github.com/zclconf/go-cty v1.14.4
go.opentelemetry.io/contrib/bridges/otelslog v0.4.0
go.opentelemetry.io/contrib/instrumentation/host v0.51.0
diff --git a/go.sum b/go.sum
index c984f559..58f7d77f 100644
--- a/go.sum
+++ b/go.sum
@@ -41,6 +41,8 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
+github.com/blackstork-io/goldmark-markdown v0.1.3 h1:L8s779mSocytvXAMhypOxqnQFrTUKQlyUjI4ZR+Ni9Q=
+github.com/blackstork-io/goldmark-markdown v0.1.3/go.mod h1:0r4jctdZOy9oirGo/NikVBdPL4h+3TSs3e2hYRkGEs8=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
@@ -304,8 +306,8 @@ github.com/wundergraph/graphql-go-tools/v2 v2.0.0-rc.46/go.mod h1:YCJyt5TSr4luj4
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
-github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
+github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
diff --git a/internal/builtin/content_blockquote.go b/internal/builtin/content_blockquote.go
index e7988927..9af61e81 100644
--- a/internal/builtin/content_blockquote.go
+++ b/internal/builtin/content_blockquote.go
@@ -40,8 +40,6 @@ func genBlockQuoteContent(ctx context.Context, params *plugin.ProvideContentPara
}
text = "> " + strings.ReplaceAll(text, "\n", "\n> ")
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: text,
- },
+ Content: plugin.NewElementFromMarkdown(text),
}, nil
}
diff --git a/internal/builtin/content_blockquote_test.go b/internal/builtin/content_blockquote_test.go
index f80976df..2837d9b3 100644
--- a/internal/builtin/content_blockquote_test.go
+++ b/internal/builtin/content_blockquote_test.go
@@ -61,11 +61,10 @@ func (s *BlockQuoteTestSuite) TestCallBlockquote() {
},
})
s.Empty(diags)
- s.Equal(&plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: "> Hello World!",
- },
- }, content)
+ s.Equal(
+ plugindata.String("> Hello World!"),
+ content.Content.AsData().(plugindata.Map)["markdown"],
+ )
}
func (s *BlockQuoteTestSuite) TestCallBlockquoteMultiline() {
@@ -80,11 +79,10 @@ func (s *BlockQuoteTestSuite) TestCallBlockquoteMultiline() {
},
})
s.Empty(diags)
- s.Equal(&plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: "> Hello\n> World\n> for you!",
- },
- }, content)
+ s.Equal(
+ plugindata.String("> Hello\n> World\n> for you!"),
+ content.Content.AsData().(plugindata.Map)["markdown"],
+ )
}
func (s *BlockQuoteTestSuite) TestCallBlockquoteMultilineDoubleNewline() {
@@ -99,9 +97,8 @@ func (s *BlockQuoteTestSuite) TestCallBlockquoteMultilineDoubleNewline() {
},
})
s.Empty(diags)
- s.Equal(&plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: "> Hello\n> World\n> \n> for you!",
- },
- }, content)
+ s.Equal(
+ plugindata.String("> Hello\n> World\n> \n> for you!"),
+ content.Content.AsData().(plugindata.Map)["markdown"],
+ )
}
diff --git a/internal/builtin/content_code.go b/internal/builtin/content_code.go
index abcab048..f142c6bb 100644
--- a/internal/builtin/content_code.go
+++ b/internal/builtin/content_code.go
@@ -50,8 +50,6 @@ func genCodeContent(ctx context.Context, params *plugin.ProvideContentParams) (*
}
text = fmt.Sprintf("```%s\n%s\n```", lang.AsString(), text)
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: text,
- },
+ Content: plugin.NewElementFromMarkdown(text),
}, nil
}
diff --git a/internal/builtin/content_code_test.go b/internal/builtin/content_code_test.go
index dcf3daa3..5c5bdbf6 100644
--- a/internal/builtin/content_code_test.go
+++ b/internal/builtin/content_code_test.go
@@ -56,11 +56,10 @@ func (s *CodeTestSuite) TestCallCodeDefault() {
},
})
s.Empty(diags)
- s.Equal(&plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: "```\nHello World!\n```",
- },
- }, content)
+ s.Equal(
+ plugindata.String("```\nHello World!\n```"),
+ content.Content.AsData().(plugindata.Map)["markdown"],
+ )
}
func (s *CodeTestSuite) TestCallCodeWithLanguage() {
@@ -78,9 +77,8 @@ func (s *CodeTestSuite) TestCallCodeWithLanguage() {
},
})
s.Empty(diags)
- s.Equal(&plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: "```json\n{\"hello\": \"world\"}\n```",
- },
- }, content)
+ s.Equal(
+ plugindata.String("```json\n{\"hello\": \"world\"}\n```"),
+ content.Content.AsData().(plugindata.Map)["markdown"],
+ )
}
diff --git a/internal/builtin/content_frontmatter.go b/internal/builtin/content_frontmatter.go
index f2b73f3d..a64db3cd 100644
--- a/internal/builtin/content_frontmatter.go
+++ b/internal/builtin/content_frontmatter.go
@@ -105,9 +105,7 @@ func genFrontMatterContent(ctx context.Context, params *plugin.ProvideContentPar
}}
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: result,
- },
+ Content: plugin.NewElementFromMarkdown(result),
Location: &plugin.Location{
Index: 1,
Effect: plugin.LocationEffectBefore,
diff --git a/internal/builtin/content_image.go b/internal/builtin/content_image.go
index 95e361a5..16261877 100644
--- a/internal/builtin/content_image.go
+++ b/internal/builtin/content_image.go
@@ -70,9 +70,7 @@ func genImageContent(ctx context.Context, params *plugin.ProvideContentParams) (
srcStr = strings.TrimSpace(strings.ReplaceAll(srcStr, "\n", ""))
altStr = strings.TrimSpace(strings.ReplaceAll(altStr, "\n", ""))
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: fmt.Sprintf("![%s](%s)", altStr, srcStr),
- },
+ Content: plugin.NewElementFromMarkdown(fmt.Sprintf("![%s](%s)", altStr, srcStr)),
}, nil
}
diff --git a/internal/builtin/content_list.go b/internal/builtin/content_list.go
index 245a8e77..2164c05c 100644
--- a/internal/builtin/content_list.go
+++ b/internal/builtin/content_list.go
@@ -97,9 +97,7 @@ func genListContent(ctx context.Context, params *plugin.ProvideContentParams) (*
}}
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: result,
- },
+ Content: plugin.NewElementFromMarkdown(result),
}, nil
}
diff --git a/internal/builtin/content_table.go b/internal/builtin/content_table.go
index 37893ceb..6acbf8b4 100644
--- a/internal/builtin/content_table.go
+++ b/internal/builtin/content_table.go
@@ -112,9 +112,7 @@ func genTableContent(ctx context.Context, params *plugin.ProvideContentParams) (
}}
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: result,
- },
+ Content: plugin.NewElementFromMarkdown(result),
}, nil
}
diff --git a/internal/builtin/content_text.go b/internal/builtin/content_text.go
index 6c677da5..ac9f0759 100644
--- a/internal/builtin/content_text.go
+++ b/internal/builtin/content_text.go
@@ -55,9 +55,7 @@ func genTextContent(ctx context.Context, params *plugin.ProvideContentParams) (*
}}
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: text,
- },
+ Content: plugin.NewElementFromMarkdown(text),
}, nil
}
diff --git a/internal/builtin/content_title.go b/internal/builtin/content_title.go
index 035551de..3560718d 100644
--- a/internal/builtin/content_title.go
+++ b/internal/builtin/content_title.go
@@ -105,9 +105,7 @@ func genTitleContent(ctx context.Context, params *plugin.ProvideContentParams) (
text = strings.ReplaceAll(text, "\n", " ")
text = strings.Repeat("#", int(titleSize)+1) + " " + text
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: text,
- },
+ Content: plugin.NewElementFromMarkdown(text),
}, nil
}
diff --git a/internal/builtin/content_toc.go b/internal/builtin/content_toc.go
index 3d925c63..d2c687df 100644
--- a/internal/builtin/content_toc.go
+++ b/internal/builtin/content_toc.go
@@ -113,9 +113,7 @@ func genTOC(ctx context.Context, params *plugin.ProvideContentParams) (*plugin.C
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: titles.render(0, args.ordered),
- },
+ Content: plugin.NewElementFromMarkdown(titles.render(0, args.ordered)),
}, nil
}
@@ -177,7 +175,7 @@ func extractTitles(section *plugin.ContentSection) []string {
if meta == nil || meta.Plugin != Name || meta.Provider != "title" {
continue
}
- titles = append(titles, content.Markdown)
+ titles = append(titles, string(content.AsMarkdownSrc()))
}
}
return titles
diff --git a/internal/elastic/data_elasticsearch_test.go b/internal/elastic/data_elasticsearch_test.go
index ff7c1fae..4edd6988 100644
--- a/internal/elastic/data_elasticsearch_test.go
+++ b/internal/elastic/data_elasticsearch_test.go
@@ -8,6 +8,7 @@ import (
"log/slog"
"net/http"
"os"
+ "strings"
"testing"
es "github.com/elastic/go-elasticsearch/v8"
@@ -51,8 +52,14 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.ctx, "docker.io/elasticsearch:8.9.0",
elasticsearch.WithPassword("password123"),
)
+ if err != nil {
+ if strings.Contains(err.Error(), "Cannot connect to the Docker daemon") {
+ s.T().Skip("Docker not available for integration tests")
+ } else {
+ s.Require().NoError(err, "failed to start elasticsearch container")
+ }
+ }
- s.Require().NoError(err, "failed to start elasticsearch container")
s.container = container
client, err := es.NewClient(es.Config{
Addresses: []string{
diff --git a/internal/microsoft/content_azure_openai_text.go b/internal/microsoft/content_azure_openai_text.go
index d06e3b8c..20c83630 100644
--- a/internal/microsoft/content_azure_openai_text.go
+++ b/internal/microsoft/content_azure_openai_text.go
@@ -101,9 +101,7 @@ func genOpenAIText(loader AzureOpenaiClientLoadFn) plugin.ProvideContentFunc {
}}
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: result,
- },
+ Content: plugin.NewElementFromMarkdown(result),
}, nil
}
}
diff --git a/internal/openai/content_openai_text.go b/internal/openai/content_openai_text.go
index 14d09506..56d30bd8 100644
--- a/internal/openai/content_openai_text.go
+++ b/internal/openai/content_openai_text.go
@@ -79,9 +79,7 @@ func genOpenAIText(loader ClientLoadFn) plugin.ProvideContentFunc {
}}
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: result,
- },
+ Content: plugin.NewElementFromMarkdown(result),
}, nil
}
}
diff --git a/internal/postgresql/data_postgresql_test.go b/internal/postgresql/data_postgresql_test.go
index eb6e93d4..01f499e6 100644
--- a/internal/postgresql/data_postgresql_test.go
+++ b/internal/postgresql/data_postgresql_test.go
@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"path/filepath"
+ "strings"
"testing"
"time"
@@ -49,7 +50,13 @@ func (s *IntegrationTestSuite) SetupSuite() {
WithOccurrence(2).
WithStartupTimeout(5*time.Second)),
)
- s.Require().NoError(err, "failed to start postgres container")
+ if err != nil {
+ if strings.Contains(err.Error(), "Cannot connect to the Docker daemon") {
+ s.T().Skip("Docker not available for integration tests")
+ } else {
+ s.Require().NoError(err, "failed to start postgres container")
+ }
+ }
s.container = container
connURL, err := container.ConnectionString(s.ctx, "sslmode=disable")
s.Require().NoError(err, "failed to get postgres connection string")
diff --git a/internal/stixview/content_stixview.go b/internal/stixview/content_stixview.go
index 8b99b881..006c60db 100644
--- a/internal/stixview/content_stixview.go
+++ b/internal/stixview/content_stixview.go
@@ -14,6 +14,7 @@ import (
"github.com/blackstork-io/fabric/pkg/diagnostics"
"github.com/blackstork-io/fabric/plugin"
+ "github.com/blackstork-io/fabric/plugin/ast"
"github.com/blackstork-io/fabric/plugin/dataspec"
"github.com/blackstork-io/fabric/plugin/plugindata"
)
@@ -130,7 +131,7 @@ func renderStixView(ctx context.Context, params *plugin.ProvideContentParams) (*
if rctx.Objects == nil && args.StixURL == nil && args.GistID == nil {
return nil, diagnostics.Diag{{
Severity: hcl.DiagError,
- Summary: "Missing arugments",
+ Summary: "Missing arguments",
Detail: "Must provide either stix_url or gist_id or objects",
}}
}
@@ -145,9 +146,9 @@ func renderStixView(ctx context.Context, params *plugin.ProvideContentParams) (*
}
return &plugin.ContentResult{
- Content: &plugin.ContentElement{
- Markdown: buf.String(),
- },
+ Content: plugin.NewElement(
+ ast.HTMLBlock(buf.Bytes()),
+ ),
}, nil
}
diff --git a/internal/stixview/content_stixview_test.go b/internal/stixview/content_stixview_test.go
index 59f8e93e..52deaafd 100644
--- a/internal/stixview/content_stixview_test.go
+++ b/internal/stixview/content_stixview_test.go
@@ -49,6 +49,7 @@ func (s *StixViewTestSuite) TestGistID() {
``,
`
`,
`
`,
+ ``,
}, "\n"), mdprint.PrintString(res.Content))
}
@@ -68,6 +69,7 @@ func (s *StixViewTestSuite) TestStixURL() {
``,
``,
`
`,
+ ``,
}, "\n"), mdprint.PrintString(res.Content))
}
@@ -97,6 +99,7 @@ func (s *StixViewTestSuite) TestAllArgs() {
``,
``,
`
`,
+ ``,
}, "\n"), mdprint.PrintString(res.Content))
}
diff --git a/justfile b/justfile
index bee56388..347f12fa 100644
--- a/justfile
+++ b/justfile
@@ -8,7 +8,7 @@ test-run:
format:
go mod tidy
- ./gen_code.sh
+ ./codegen/format.sh
format-extra: format
gofumpt -w -extra .
diff --git a/pkg/diagnostics/diagnostics.go b/pkg/diagnostics/diagnostics.go
index 951fabd2..2dcbddf4 100644
--- a/pkg/diagnostics/diagnostics.go
+++ b/pkg/diagnostics/diagnostics.go
@@ -8,12 +8,14 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
+
+ "github.com/blackstork-io/fabric/pkg/utils/slogutil"
)
type Diag hcl.Diagnostics // Diagnostics does implement error interface, but not, itself, an error.
func (d Diag) Error() string {
- slog.Debug("Treated diagnostic.Diag as error")
+ slog.Debug("Treated diagnostic.Diag as error", slogutil.SourceOverride(1))
return hcl.Diagnostics(d).Error()
}
diff --git a/pkg/utils/golang.go b/pkg/utils/golang.go
index 2651b100..4222c535 100644
--- a/pkg/utils/golang.go
+++ b/pkg/utils/golang.go
@@ -1,10 +1,12 @@
package utils
-import "reflect"
+import (
+ "reflect"
+)
// Correct version of nil check, works on nil interfaces as well as any other value.
func IsNil(val any) bool {
- // Checking for nil on interface objects is terrble
+ // Checking for nil on interface objects is terrible
// Thanks to: https://stackoverflow.com/a/76595928/4632951
if val == nil {
return true
diff --git a/pkg/utils/slogutil/slogutil.go b/pkg/utils/slogutil/slogutil.go
new file mode 100644
index 00000000..94e61a75
--- /dev/null
+++ b/pkg/utils/slogutil/slogutil.go
@@ -0,0 +1,71 @@
+package slogutil
+
+import (
+ "context"
+ "log/slog"
+ "runtime"
+)
+
+// SourceRewriter is a slog.Handler that rewrites the source of log entries.
+type SourceRewriter struct {
+ slog.Handler
+}
+
+// WithAttrs implements slog.Handler.
+func (sr SourceRewriter) WithAttrs(attrs []slog.Attr) slog.Handler {
+ return SourceRewriter{Handler: sr.Handler.WithAttrs(attrs)}
+}
+
+// WithGroup implements slog.Handler.
+func (sr SourceRewriter) WithGroup(name string) slog.Handler {
+ return SourceRewriter{Handler: sr.Handler.WithGroup(name)}
+}
+
+// NewSourceRewriter returns a new SourceRewriter that wraps the given handler.
+// The returned SourceRewriter applies the SourceOverride commands.
+func NewSourceRewriter(h slog.Handler) SourceRewriter {
+ return SourceRewriter{Handler: h}
+}
+
+const sourceKey = "Source Override. Use SourceRewriter handler to apply."
+
+func (sr SourceRewriter) Handle(ctx context.Context, r slog.Record) error {
+ var attrCountAfterOverride int
+ r.Attrs(func(a slog.Attr) bool {
+ if !(a.Key == sourceKey && a.Value.Kind() == slog.KindUint64) {
+ attrCountAfterOverride++
+ }
+ return true
+ })
+ if attrCountAfterOverride == r.NumAttrs() {
+ return sr.Handler.Handle(ctx, r)
+ }
+ attrs := make([]slog.Attr, 0, attrCountAfterOverride)
+ newRecord := slog.Record{
+ Time: r.Time,
+ Message: r.Message,
+ Level: r.Level,
+ PC: r.PC,
+ }
+ r.Attrs(func(a slog.Attr) bool {
+ if a.Key == sourceKey && a.Value.Kind() == slog.KindUint64 {
+ newRecord.PC = uintptr(a.Value.Uint64())
+ } else {
+ attrs = append(attrs, a)
+ }
+ return true
+ })
+ newRecord.AddAttrs(attrs...)
+ return sr.Handler.Handle(ctx, newRecord)
+}
+
+var _ slog.Handler = SourceRewriter{}
+
+// SourceOverride returns a slog.Attr with the source of the caller at the given offset.
+// SourceOverride(0) is the caller of SourceOverride, SourceOverride(1) is the caller of the caller, etc.
+func SourceOverride(offset int) slog.Attr {
+ var pcs [1]uintptr
+ // skip [runtime.Callers, this function, +offset functions]
+ runtime.Callers(2+offset, pcs[:])
+ return slog.Uint64(sourceKey, uint64(pcs[0]))
+}
diff --git a/plugin/ast/astsrc/astsrc.go b/plugin/ast/astsrc/astsrc.go
new file mode 100644
index 00000000..0bc0fd01
--- /dev/null
+++ b/plugin/ast/astsrc/astsrc.go
@@ -0,0 +1,47 @@
+package astsrc
+
+import (
+ "fmt"
+
+ "github.com/yuin/goldmark/text"
+)
+
+// ASTSource holds the source of the markdown AST (a read-only byte slice).
+type ASTSource []byte
+
+// Append appends bytes to the source and returns corresponding segment.
+func (s *ASTSource) Append(data []byte) text.Segment {
+ start := len(*s)
+ *s = append(*s, data...)
+ return text.NewSegment(start, len(*s))
+}
+
+// AppendMultiple appends multiple byte slices to the source and returns corresponding segments.
+func (s *ASTSource) AppendMultiple(data [][]byte) *text.Segments {
+ values := make([]text.Segment, len(data))
+ for i, segment := range data {
+ values[i] = s.Append(segment)
+ }
+ res := text.NewSegments()
+ res.AppendAll(values)
+ return res
+}
+
+// AppendString appends a string to the source and returns corresponding segment.
+func (s *ASTSource) AppendString(data string) text.Segment {
+ return s.Append([]byte(data))
+}
+
+// Appendf appends formatted string to the source and returns corresponding segment.
+func (s *ASTSource) Appendf(format string, args ...interface{}) text.Segment {
+ start := len(*s)
+ *s = fmt.Appendf(*s, format, args...)
+ return text.NewSegment(start, len(*s))
+}
+
+// AsBytes returns the source as a byte slice.
+// Returned bytes should be treated as read-only or modified with care,
+// ensuring that the offsets are not changed.
+func (s ASTSource) AsBytes() []byte {
+ return s
+}
diff --git a/plugin/ast/builder.go b/plugin/ast/builder.go
new file mode 100644
index 00000000..4297767c
--- /dev/null
+++ b/plugin/ast/builder.go
@@ -0,0 +1,306 @@
+package ast
+
+import (
+ "bytes"
+ "regexp"
+
+ astv1 "github.com/blackstork-io/fabric/plugin/ast/v1"
+)
+
+// inlines
+
+func CodeSpan(code []byte) *astv1.Node_CodeSpan {
+ return &astv1.Node_CodeSpan{
+ CodeSpan: &astv1.CodeSpan{
+ Base: &astv1.BaseNode{
+ Children: []*astv1.Node{{
+ Kind: &astv1.Node_Text{
+ Text: &astv1.Text{
+ Base: &astv1.BaseNode{},
+ Segment: code,
+ Raw: true,
+ },
+ },
+ }},
+ },
+ },
+ }
+}
+
+func emphasis(level int64, children []astv1.InlineContent) *astv1.Node_Emphasis {
+ return &astv1.Node_Emphasis{
+ Emphasis: &astv1.Emphasis{
+ Base: &astv1.BaseNode{
+ Children: astv1.Inlines.ExtendNodes(children, nil),
+ },
+ Level: level,
+ },
+ }
+}
+
+func Emphasis(children ...astv1.InlineContent) *astv1.Node_Emphasis {
+ return emphasis(1, children)
+}
+
+var Italic = Emphasis
+
+func StrongEmphasis(children ...astv1.InlineContent) *astv1.Node_Emphasis {
+ return emphasis(2, children)
+}
+
+var Bold = StrongEmphasis
+
+func Strikethrough(children ...astv1.InlineContent) *astv1.Node_Strikethrough {
+ return &astv1.Node_Strikethrough{
+ Strikethrough: &astv1.Strikethrough{
+ Base: &astv1.BaseNode{
+ Children: astv1.Inlines.ExtendNodes(children, nil),
+ },
+ },
+ }
+}
+
+func Link(text ...astv1.InlineContent) *astv1.LinkOrImage {
+ return &astv1.LinkOrImage{
+ Base: &astv1.BaseNode{
+ Children: astv1.Inlines.ExtendNodes(text, nil),
+ },
+ IsImage: false,
+ }
+}
+
+func Image(alt ...astv1.InlineContent) *astv1.LinkOrImage {
+ return &astv1.LinkOrImage{
+ Base: &astv1.BaseNode{
+ Children: astv1.Inlines.ExtendNodes(alt, nil),
+ },
+ IsImage: true,
+ }
+}
+
+func AutoLink(url string) *astv1.Node_AutoLink {
+ before, after, found := bytes.Cut([]byte(url), []byte("://"))
+ if found {
+ return &astv1.Node_AutoLink{
+ AutoLink: &astv1.AutoLink{
+ Base: &astv1.BaseNode{},
+ Type: astv1.AutoLinkType_AUTO_LINK_TYPE_URL,
+ Protocol: before,
+ Value: after,
+ },
+ }
+ } else {
+ return &astv1.Node_AutoLink{
+ AutoLink: &astv1.AutoLink{
+ Base: &astv1.BaseNode{},
+ Type: astv1.AutoLinkType_AUTO_LINK_TYPE_EMAIL,
+ Value: before,
+ },
+ }
+ }
+}
+
+func AutoLinkEmail(email string) *astv1.Node_AutoLink {
+ return &astv1.Node_AutoLink{
+ AutoLink: &astv1.AutoLink{
+ Base: &astv1.BaseNode{},
+ Type: astv1.AutoLinkType_AUTO_LINK_TYPE_EMAIL,
+ Value: []byte(email),
+ },
+ }
+}
+
+func InlineHTML(html string) *astv1.Node_RawHtml {
+ return &astv1.Node_RawHtml{
+ RawHtml: &astv1.RawHTML{
+ Base: &astv1.BaseNode{},
+ Segments: [][]byte{[]byte(html)},
+ },
+ }
+}
+
+func LineBreak() *astv1.Node_Text {
+ return &astv1.Node_Text{
+ Text: &astv1.Text{
+ Base: &astv1.BaseNode{},
+ Segment: nil,
+ HardLineBreak: true,
+ },
+ }
+}
+
+var textHardBreakRegexp = regexp.MustCompile(`( {2,}\n *|\\\n *)`)
+
+func splitBytes(re *regexp.Regexp, s []byte, n int) [][]byte {
+ if n == 0 {
+ return nil
+ }
+
+ if len(s) == 0 {
+ return [][]byte{nil}
+ }
+
+ matches := re.FindAllIndex(s, n)
+ subBytes := make([][]byte, 0, len(matches))
+
+ beg := 0
+ end := 0
+ for _, match := range matches {
+ if n > 0 && len(subBytes) >= n-1 {
+ break
+ }
+
+ end = match[0]
+ if match[1] != 0 {
+ subBytes = append(subBytes, s[beg:end])
+ }
+ beg = match[1]
+ }
+
+ if end != len(s) {
+ subBytes = append(subBytes, s[beg:])
+ }
+
+ return subBytes
+}
+
+func convertSoftBreaks(txt []byte) (res []*astv1.Text) {
+ split := bytes.Split(txt, []byte("\n"))
+ res = make([]*astv1.Text, 0, len(split))
+ res = append(res, &astv1.Text{
+ Segment: split[0],
+ })
+ for i, s := range split[1:] {
+ res[i].SoftLineBreak = true
+ res = append(res, &astv1.Text{
+ Segment: s,
+ })
+ }
+ return
+}
+
+func convertHardBreaks(txt []byte) (res []*astv1.Text) {
+ split := splitBytes(textHardBreakRegexp, txt, -1)
+ res = make([]*astv1.Text, 0, len(split))
+ res = append(res, convertSoftBreaks(split[0])...)
+ for i, s := range split[1:] {
+ res[i].HardLineBreak = true
+ res = append(res, convertSoftBreaks(s)...)
+ }
+ return
+}
+
+func Text(text string) (res astv1.Inlines) {
+ txt := convertHardBreaks([]byte(text))
+ res = make(astv1.Inlines, len(txt))
+ for i, t := range txt {
+ res[i] = &astv1.Node_Text{
+ Text: t,
+ }
+ }
+ return
+}
+
+// container blocks
+func Blockquote(children ...astv1.BlockContent) *astv1.Node_Blockquote {
+ return &astv1.Node_Blockquote{
+ Blockquote: &astv1.Blockquote{
+ Base: &astv1.BaseNode{
+ Children: astv1.Blocks.ExtendNodes(children, nil),
+ },
+ },
+ }
+}
+
+type listMarker uint32
+
+const (
+ Period listMarker = '.'
+ Paren listMarker = ')'
+ Star listMarker = '*'
+ Plus listMarker = '+'
+ Hyphen listMarker = '-'
+)
+
+func List(marker listMarker) *astv1.Node_List {
+ return &astv1.Node_List{
+ List: &astv1.List{
+ Base: &astv1.BaseNode{},
+ Marker: uint32(marker),
+ },
+ }
+}
+
+func ThematicBreak() *astv1.Node_ThematicBreak {
+ return &astv1.Node_ThematicBreak{
+ ThematicBreak: &astv1.ThematicBreak{
+ Base: &astv1.BaseNode{},
+ },
+ }
+}
+
+func Header(level uint32, children ...astv1.InlineContent) *astv1.Node_Heading {
+ return &astv1.Node_Heading{
+ Heading: &astv1.Heading{
+ Base: &astv1.BaseNode{
+ Children: astv1.Inlines.ExtendNodes(children, nil),
+ },
+ Level: max(1, min(level, 6)),
+ },
+ }
+}
+
+func IndentedCodeBlock(code []byte) *astv1.Node_CodeBlock {
+ return &astv1.Node_CodeBlock{
+ CodeBlock: &astv1.CodeBlock{
+ Base: &astv1.BaseNode{},
+ Lines: bytes.Split(code, []byte("\n")),
+ },
+ }
+}
+
+func FencedCodeBlock(code []byte) *astv1.Node_FencedCodeBlock {
+ return &astv1.Node_FencedCodeBlock{
+ FencedCodeBlock: &astv1.FencedCodeBlock{
+ Base: &astv1.BaseNode{},
+ Info: nil,
+ Lines: bytes.Split(code, []byte("\n")),
+ },
+ }
+}
+
+func HTMLBlock(html []byte) *astv1.Node_HtmlBlock {
+ return &astv1.Node_HtmlBlock{
+ HtmlBlock: &astv1.HTMLBlock{
+ Base: &astv1.BaseNode{},
+ // setting the value to most general type, hopefully renderers don't care
+ Type: astv1.HTMLBlockType_HTML_BLOCK_TYPE_7,
+ Lines: bytes.Split(html, []byte("\n")),
+ },
+ }
+}
+
+func Paragraph(children ...astv1.InlineContent) *astv1.Node_Paragraph {
+ return &astv1.Node_Paragraph{
+ Paragraph: &astv1.Paragraph{
+ Base: &astv1.BaseNode{
+ Children: astv1.Inlines.ExtendNodes(children, nil),
+ },
+ },
+ }
+}
+
+var (
+ AlignLeft = astv1.CellAlignment_CELL_ALIGNMENT_LEFT
+ AlignRight = astv1.CellAlignment_CELL_ALIGNMENT_RIGHT
+ AlignCenter = astv1.CellAlignment_CELL_ALIGNMENT_CENTER
+ AlignNone = astv1.CellAlignment_CELL_ALIGNMENT_NONE
+)
+
+func Table() *astv1.Node_Table {
+ return &astv1.Node_Table{
+ Table: &astv1.Table{
+ Base: &astv1.BaseNode{},
+ },
+ }
+}
diff --git a/plugin/ast/nodes/content_node.go b/plugin/ast/nodes/content_node.go
new file mode 100644
index 00000000..6d57273b
--- /dev/null
+++ b/plugin/ast/nodes/content_node.go
@@ -0,0 +1,53 @@
+package nodes
+
+import "github.com/yuin/goldmark/ast"
+
+type FabricContentNode struct {
+ ast.BaseBlock
+ Meta *ContentMeta
+}
+
+func ToFabricContentNode(node ast.Node) (meta *FabricContentNode) {
+ switch n := node.(type) {
+ case *FabricContentNode:
+ meta = n
+ case *ast.Document:
+ meta = &FabricContentNode{}
+ child := n.FirstChild()
+ for child != nil {
+ c := child
+ child = child.NextSibling()
+ meta.AppendChild(meta, c)
+ }
+ case nil:
+ // meta is nil
+ default:
+ meta = &FabricContentNode{}
+ meta.AppendChild(meta, n)
+ }
+ return
+}
+
+// Dump implements ast.Node.
+func (m *FabricContentNode) Dump(source []byte, level int) {
+ var kv map[string]string
+ if m == nil || m.Meta == nil {
+ kv = map[string]string{
+ "meta": "nil",
+ }
+ } else {
+ kv = map[string]string{
+ "meta.provider": m.Meta.Provider,
+ "meta.plugin": m.Meta.Plugin,
+ "meta.version": m.Meta.Version,
+ }
+ }
+ ast.DumpHelper(m, source, level, kv, nil)
+}
+
+// Kind implements ast.Node.
+func (m *FabricContentNode) Kind() ast.NodeKind {
+ return ContentNodeKind
+}
+
+var _ ast.Node = &FabricContentNode{}
diff --git a/plugin/ast/nodes/custom_nodes.go b/plugin/ast/nodes/custom_nodes.go
new file mode 100644
index 00000000..283246fa
--- /dev/null
+++ b/plugin/ast/nodes/custom_nodes.go
@@ -0,0 +1,78 @@
+package nodes
+
+import (
+ "github.com/yuin/goldmark/ast"
+ "google.golang.org/protobuf/types/known/anypb"
+)
+
+var (
+ ContentNodeKind = ast.NewNodeKind("FabricContentNode")
+ CustomBlockKind = ast.NewNodeKind("FabricCustomBlock")
+ CustomInlineKind = ast.NewNodeKind("FabricCustomInline")
+)
+
+func NewCustomNode(isInline bool, data *anypb.Any) ast.Node {
+ if isInline {
+ return &CustomInline{
+ Data: data,
+ }
+ } else {
+ return &CustomBlock{
+ Data: data,
+ }
+ }
+}
+
+type CustomInline struct {
+ ast.BaseInline
+ Data *anypb.Any
+}
+
+var _ ast.Node = &CustomInline{}
+
+// Kind implements ast.Node.
+func (o *CustomInline) Kind() ast.NodeKind {
+ return CustomInlineKind
+}
+
+// Dump implements ast.Node.
+func (o *CustomInline) Dump(source []byte, level int) {
+ var kv map[string]string
+ if o == nil || o.Data == nil {
+ kv = map[string]string{
+ "other": "nil",
+ }
+ } else {
+ kv = map[string]string{
+ "other.TypeUrl": o.Data.GetTypeUrl(),
+ }
+ }
+ ast.DumpHelper(o, source, level, kv, nil)
+}
+
+type CustomBlock struct {
+ ast.BaseBlock
+ Data *anypb.Any
+}
+
+var _ ast.Node = &CustomBlock{}
+
+// Kind implements ast.Node.
+func (o *CustomBlock) Kind() ast.NodeKind {
+ return CustomBlockKind
+}
+
+// Dump implements ast.Node.
+func (o *CustomBlock) Dump(source []byte, level int) {
+ var kv map[string]string
+ if o == nil || o.Data == nil {
+ kv = map[string]string{
+ "other": "nil",
+ }
+ } else {
+ kv = map[string]string{
+ "other.TypeUrl": o.Data.GetTypeUrl(),
+ }
+ }
+ ast.DumpHelper(o, source, level, kv, nil)
+}
diff --git a/plugin/ast/nodes/metadata.go b/plugin/ast/nodes/metadata.go
new file mode 100644
index 00000000..bdbdce49
--- /dev/null
+++ b/plugin/ast/nodes/metadata.go
@@ -0,0 +1,20 @@
+package nodes
+
+import "github.com/blackstork-io/fabric/plugin/plugindata"
+
+type ContentMeta struct {
+ Provider string
+ Plugin string
+ Version string
+}
+
+func (meta *ContentMeta) AsData() plugindata.Data {
+ if meta == nil {
+ return nil
+ }
+ return plugindata.Map{
+ "provider": plugindata.String(meta.Provider),
+ "plugin": plugindata.String(meta.Plugin),
+ "version": plugindata.String(meta.Version),
+ }
+}
diff --git a/plugin/ast/v1/ast.pb.go b/plugin/ast/v1/ast.pb.go
new file mode 100644
index 00000000..4c662af5
--- /dev/null
+++ b/plugin/ast/v1/ast.pb.go
@@ -0,0 +1,3085 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.34.2
+// protoc (unknown)
+// source: ast/v1/ast.proto
+
+package astv1
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ anypb "google.golang.org/protobuf/types/known/anypb"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type HTMLBlockType int32
+
+const (
+ HTMLBlockType_HTML_BLOCK_TYPE_UNSPECIFIED HTMLBlockType = 0
+ HTMLBlockType_HTML_BLOCK_TYPE_1 HTMLBlockType = 1
+ HTMLBlockType_HTML_BLOCK_TYPE_2 HTMLBlockType = 2
+ HTMLBlockType_HTML_BLOCK_TYPE_3 HTMLBlockType = 3
+ HTMLBlockType_HTML_BLOCK_TYPE_4 HTMLBlockType = 4
+ HTMLBlockType_HTML_BLOCK_TYPE_5 HTMLBlockType = 5
+ HTMLBlockType_HTML_BLOCK_TYPE_6 HTMLBlockType = 6
+ HTMLBlockType_HTML_BLOCK_TYPE_7 HTMLBlockType = 7
+)
+
+// Enum value maps for HTMLBlockType.
+var (
+ HTMLBlockType_name = map[int32]string{
+ 0: "HTML_BLOCK_TYPE_UNSPECIFIED",
+ 1: "HTML_BLOCK_TYPE_1",
+ 2: "HTML_BLOCK_TYPE_2",
+ 3: "HTML_BLOCK_TYPE_3",
+ 4: "HTML_BLOCK_TYPE_4",
+ 5: "HTML_BLOCK_TYPE_5",
+ 6: "HTML_BLOCK_TYPE_6",
+ 7: "HTML_BLOCK_TYPE_7",
+ }
+ HTMLBlockType_value = map[string]int32{
+ "HTML_BLOCK_TYPE_UNSPECIFIED": 0,
+ "HTML_BLOCK_TYPE_1": 1,
+ "HTML_BLOCK_TYPE_2": 2,
+ "HTML_BLOCK_TYPE_3": 3,
+ "HTML_BLOCK_TYPE_4": 4,
+ "HTML_BLOCK_TYPE_5": 5,
+ "HTML_BLOCK_TYPE_6": 6,
+ "HTML_BLOCK_TYPE_7": 7,
+ }
+)
+
+func (x HTMLBlockType) Enum() *HTMLBlockType {
+ p := new(HTMLBlockType)
+ *p = x
+ return p
+}
+
+func (x HTMLBlockType) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (HTMLBlockType) Descriptor() protoreflect.EnumDescriptor {
+ return file_ast_v1_ast_proto_enumTypes[0].Descriptor()
+}
+
+func (HTMLBlockType) Type() protoreflect.EnumType {
+ return &file_ast_v1_ast_proto_enumTypes[0]
+}
+
+func (x HTMLBlockType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use HTMLBlockType.Descriptor instead.
+func (HTMLBlockType) EnumDescriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{0}
+}
+
+type AutoLinkType int32
+
+const (
+ AutoLinkType_AUTO_LINK_TYPE_UNSPECIFIED AutoLinkType = 0
+ AutoLinkType_AUTO_LINK_TYPE_EMAIL AutoLinkType = 1
+ AutoLinkType_AUTO_LINK_TYPE_URL AutoLinkType = 2
+)
+
+// Enum value maps for AutoLinkType.
+var (
+ AutoLinkType_name = map[int32]string{
+ 0: "AUTO_LINK_TYPE_UNSPECIFIED",
+ 1: "AUTO_LINK_TYPE_EMAIL",
+ 2: "AUTO_LINK_TYPE_URL",
+ }
+ AutoLinkType_value = map[string]int32{
+ "AUTO_LINK_TYPE_UNSPECIFIED": 0,
+ "AUTO_LINK_TYPE_EMAIL": 1,
+ "AUTO_LINK_TYPE_URL": 2,
+ }
+)
+
+func (x AutoLinkType) Enum() *AutoLinkType {
+ p := new(AutoLinkType)
+ *p = x
+ return p
+}
+
+func (x AutoLinkType) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (AutoLinkType) Descriptor() protoreflect.EnumDescriptor {
+ return file_ast_v1_ast_proto_enumTypes[1].Descriptor()
+}
+
+func (AutoLinkType) Type() protoreflect.EnumType {
+ return &file_ast_v1_ast_proto_enumTypes[1]
+}
+
+func (x AutoLinkType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use AutoLinkType.Descriptor instead.
+func (AutoLinkType) EnumDescriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{1}
+}
+
+type CellAlignment int32
+
+const (
+ CellAlignment_CELL_ALIGNMENT_UNSPECIFIED CellAlignment = 0
+ CellAlignment_CELL_ALIGNMENT_LEFT CellAlignment = 1
+ CellAlignment_CELL_ALIGNMENT_RIGHT CellAlignment = 2
+ CellAlignment_CELL_ALIGNMENT_CENTER CellAlignment = 3
+ CellAlignment_CELL_ALIGNMENT_NONE CellAlignment = 4
+)
+
+// Enum value maps for CellAlignment.
+var (
+ CellAlignment_name = map[int32]string{
+ 0: "CELL_ALIGNMENT_UNSPECIFIED",
+ 1: "CELL_ALIGNMENT_LEFT",
+ 2: "CELL_ALIGNMENT_RIGHT",
+ 3: "CELL_ALIGNMENT_CENTER",
+ 4: "CELL_ALIGNMENT_NONE",
+ }
+ CellAlignment_value = map[string]int32{
+ "CELL_ALIGNMENT_UNSPECIFIED": 0,
+ "CELL_ALIGNMENT_LEFT": 1,
+ "CELL_ALIGNMENT_RIGHT": 2,
+ "CELL_ALIGNMENT_CENTER": 3,
+ "CELL_ALIGNMENT_NONE": 4,
+ }
+)
+
+func (x CellAlignment) Enum() *CellAlignment {
+ p := new(CellAlignment)
+ *p = x
+ return p
+}
+
+func (x CellAlignment) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (CellAlignment) Descriptor() protoreflect.EnumDescriptor {
+ return file_ast_v1_ast_proto_enumTypes[2].Descriptor()
+}
+
+func (CellAlignment) Type() protoreflect.EnumType {
+ return &file_ast_v1_ast_proto_enumTypes[2]
+}
+
+func (x CellAlignment) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use CellAlignment.Descriptor instead.
+func (CellAlignment) EnumDescriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{2}
+}
+
+type Attribute struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Name []byte `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // Types that are assignable to Value:
+ //
+ // *Attribute_Bytes
+ // *Attribute_Str
+ Value isAttribute_Value `protobuf_oneof:"value"`
+}
+
+func (x *Attribute) Reset() {
+ *x = Attribute{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Attribute) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Attribute) ProtoMessage() {}
+
+func (x *Attribute) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Attribute.ProtoReflect.Descriptor instead.
+func (*Attribute) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Attribute) GetName() []byte {
+ if x != nil {
+ return x.Name
+ }
+ return nil
+}
+
+func (m *Attribute) GetValue() isAttribute_Value {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (x *Attribute) GetBytes() []byte {
+ if x, ok := x.GetValue().(*Attribute_Bytes); ok {
+ return x.Bytes
+ }
+ return nil
+}
+
+func (x *Attribute) GetStr() string {
+ if x, ok := x.GetValue().(*Attribute_Str); ok {
+ return x.Str
+ }
+ return ""
+}
+
+type isAttribute_Value interface {
+ isAttribute_Value()
+}
+
+type Attribute_Bytes struct {
+ Bytes []byte `protobuf:"bytes,2,opt,name=bytes,proto3,oneof"`
+}
+
+type Attribute_Str struct {
+ Str string `protobuf:"bytes,3,opt,name=str,proto3,oneof"`
+}
+
+func (*Attribute_Bytes) isAttribute_Value() {}
+
+func (*Attribute_Str) isAttribute_Value() {}
+
+type BaseNode struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Children []*Node `protobuf:"bytes,1,rep,name=children,proto3" json:"children,omitempty"`
+ Attributes []*Attribute `protobuf:"bytes,2,rep,name=attributes,proto3" json:"attributes,omitempty"`
+ // value meaningful only for blocks
+ BlankPreviousLines bool `protobuf:"varint,3,opt,name=blank_previous_lines,json=blankPreviousLines,proto3" json:"blank_previous_lines,omitempty"`
+}
+
+func (x *BaseNode) Reset() {
+ *x = BaseNode{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *BaseNode) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BaseNode) ProtoMessage() {}
+
+func (x *BaseNode) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use BaseNode.ProtoReflect.Descriptor instead.
+func (*BaseNode) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *BaseNode) GetChildren() []*Node {
+ if x != nil {
+ return x.Children
+ }
+ return nil
+}
+
+func (x *BaseNode) GetAttributes() []*Attribute {
+ if x != nil {
+ return x.Attributes
+ }
+ return nil
+}
+
+func (x *BaseNode) GetBlankPreviousLines() bool {
+ if x != nil {
+ return x.BlankPreviousLines
+ }
+ return false
+}
+
+type Node struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Types that are assignable to Kind:
+ //
+ // *Node_Document
+ // *Node_TextBlock
+ // *Node_Paragraph
+ // *Node_Heading
+ // *Node_ThematicBreak
+ // *Node_CodeBlock
+ // *Node_FencedCodeBlock
+ // *Node_Blockquote
+ // *Node_List
+ // *Node_ListItem
+ // *Node_HtmlBlock
+ // *Node_Text
+ // *Node_String_
+ // *Node_CodeSpan
+ // *Node_Emphasis
+ // *Node_LinkOrImage
+ // *Node_AutoLink
+ // *Node_RawHtml
+ // *Node_Table
+ // *Node_TableRow
+ // *Node_TableCell
+ // *Node_TaskCheckbox
+ // *Node_Strikethrough
+ // *Node_ContentNode
+ // *Node_Custom
+ Kind isNode_Kind `protobuf_oneof:"kind"`
+}
+
+func (x *Node) Reset() {
+ *x = Node{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Node) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Node) ProtoMessage() {}
+
+func (x *Node) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Node.ProtoReflect.Descriptor instead.
+func (*Node) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{2}
+}
+
+func (m *Node) GetKind() isNode_Kind {
+ if m != nil {
+ return m.Kind
+ }
+ return nil
+}
+
+func (x *Node) GetDocument() *Document {
+ if x, ok := x.GetKind().(*Node_Document); ok {
+ return x.Document
+ }
+ return nil
+}
+
+func (x *Node) GetTextBlock() *TextBlock {
+ if x, ok := x.GetKind().(*Node_TextBlock); ok {
+ return x.TextBlock
+ }
+ return nil
+}
+
+func (x *Node) GetParagraph() *Paragraph {
+ if x, ok := x.GetKind().(*Node_Paragraph); ok {
+ return x.Paragraph
+ }
+ return nil
+}
+
+func (x *Node) GetHeading() *Heading {
+ if x, ok := x.GetKind().(*Node_Heading); ok {
+ return x.Heading
+ }
+ return nil
+}
+
+func (x *Node) GetThematicBreak() *ThematicBreak {
+ if x, ok := x.GetKind().(*Node_ThematicBreak); ok {
+ return x.ThematicBreak
+ }
+ return nil
+}
+
+func (x *Node) GetCodeBlock() *CodeBlock {
+ if x, ok := x.GetKind().(*Node_CodeBlock); ok {
+ return x.CodeBlock
+ }
+ return nil
+}
+
+func (x *Node) GetFencedCodeBlock() *FencedCodeBlock {
+ if x, ok := x.GetKind().(*Node_FencedCodeBlock); ok {
+ return x.FencedCodeBlock
+ }
+ return nil
+}
+
+func (x *Node) GetBlockquote() *Blockquote {
+ if x, ok := x.GetKind().(*Node_Blockquote); ok {
+ return x.Blockquote
+ }
+ return nil
+}
+
+func (x *Node) GetList() *List {
+ if x, ok := x.GetKind().(*Node_List); ok {
+ return x.List
+ }
+ return nil
+}
+
+func (x *Node) GetListItem() *ListItem {
+ if x, ok := x.GetKind().(*Node_ListItem); ok {
+ return x.ListItem
+ }
+ return nil
+}
+
+func (x *Node) GetHtmlBlock() *HTMLBlock {
+ if x, ok := x.GetKind().(*Node_HtmlBlock); ok {
+ return x.HtmlBlock
+ }
+ return nil
+}
+
+func (x *Node) GetText() *Text {
+ if x, ok := x.GetKind().(*Node_Text); ok {
+ return x.Text
+ }
+ return nil
+}
+
+func (x *Node) GetString_() *String {
+ if x, ok := x.GetKind().(*Node_String_); ok {
+ return x.String_
+ }
+ return nil
+}
+
+func (x *Node) GetCodeSpan() *CodeSpan {
+ if x, ok := x.GetKind().(*Node_CodeSpan); ok {
+ return x.CodeSpan
+ }
+ return nil
+}
+
+func (x *Node) GetEmphasis() *Emphasis {
+ if x, ok := x.GetKind().(*Node_Emphasis); ok {
+ return x.Emphasis
+ }
+ return nil
+}
+
+func (x *Node) GetLinkOrImage() *LinkOrImage {
+ if x, ok := x.GetKind().(*Node_LinkOrImage); ok {
+ return x.LinkOrImage
+ }
+ return nil
+}
+
+func (x *Node) GetAutoLink() *AutoLink {
+ if x, ok := x.GetKind().(*Node_AutoLink); ok {
+ return x.AutoLink
+ }
+ return nil
+}
+
+func (x *Node) GetRawHtml() *RawHTML {
+ if x, ok := x.GetKind().(*Node_RawHtml); ok {
+ return x.RawHtml
+ }
+ return nil
+}
+
+func (x *Node) GetTable() *Table {
+ if x, ok := x.GetKind().(*Node_Table); ok {
+ return x.Table
+ }
+ return nil
+}
+
+func (x *Node) GetTableRow() *TableRow {
+ if x, ok := x.GetKind().(*Node_TableRow); ok {
+ return x.TableRow
+ }
+ return nil
+}
+
+func (x *Node) GetTableCell() *TableCell {
+ if x, ok := x.GetKind().(*Node_TableCell); ok {
+ return x.TableCell
+ }
+ return nil
+}
+
+func (x *Node) GetTaskCheckbox() *TaskCheckbox {
+ if x, ok := x.GetKind().(*Node_TaskCheckbox); ok {
+ return x.TaskCheckbox
+ }
+ return nil
+}
+
+func (x *Node) GetStrikethrough() *Strikethrough {
+ if x, ok := x.GetKind().(*Node_Strikethrough); ok {
+ return x.Strikethrough
+ }
+ return nil
+}
+
+func (x *Node) GetContentNode() *FabricContentNode {
+ if x, ok := x.GetKind().(*Node_ContentNode); ok {
+ return x.ContentNode
+ }
+ return nil
+}
+
+func (x *Node) GetCustom() *CustomNode {
+ if x, ok := x.GetKind().(*Node_Custom); ok {
+ return x.Custom
+ }
+ return nil
+}
+
+type isNode_Kind interface {
+ isNode_Kind()
+}
+
+type Node_Document struct {
+ // Blocks
+ Document *Document `protobuf:"bytes,1,opt,name=document,proto3,oneof"`
+}
+
+type Node_TextBlock struct {
+ TextBlock *TextBlock `protobuf:"bytes,5,opt,name=text_block,json=textBlock,proto3,oneof"`
+}
+
+type Node_Paragraph struct {
+ Paragraph *Paragraph `protobuf:"bytes,6,opt,name=paragraph,proto3,oneof"`
+}
+
+type Node_Heading struct {
+ Heading *Heading `protobuf:"bytes,7,opt,name=heading,proto3,oneof"`
+}
+
+type Node_ThematicBreak struct {
+ ThematicBreak *ThematicBreak `protobuf:"bytes,8,opt,name=thematic_break,json=thematicBreak,proto3,oneof"`
+}
+
+type Node_CodeBlock struct {
+ CodeBlock *CodeBlock `protobuf:"bytes,9,opt,name=code_block,json=codeBlock,proto3,oneof"`
+}
+
+type Node_FencedCodeBlock struct {
+ FencedCodeBlock *FencedCodeBlock `protobuf:"bytes,10,opt,name=fenced_code_block,json=fencedCodeBlock,proto3,oneof"`
+}
+
+type Node_Blockquote struct {
+ Blockquote *Blockquote `protobuf:"bytes,11,opt,name=blockquote,proto3,oneof"`
+}
+
+type Node_List struct {
+ List *List `protobuf:"bytes,12,opt,name=list,proto3,oneof"`
+}
+
+type Node_ListItem struct {
+ ListItem *ListItem `protobuf:"bytes,13,opt,name=list_item,json=listItem,proto3,oneof"`
+}
+
+type Node_HtmlBlock struct {
+ HtmlBlock *HTMLBlock `protobuf:"bytes,14,opt,name=html_block,json=htmlBlock,proto3,oneof"`
+}
+
+type Node_Text struct {
+ // inlines
+ Text *Text `protobuf:"bytes,15,opt,name=text,proto3,oneof"`
+}
+
+type Node_String_ struct {
+ String_ *String `protobuf:"bytes,16,opt,name=string,proto3,oneof"`
+}
+
+type Node_CodeSpan struct {
+ CodeSpan *CodeSpan `protobuf:"bytes,17,opt,name=code_span,json=codeSpan,proto3,oneof"`
+}
+
+type Node_Emphasis struct {
+ Emphasis *Emphasis `protobuf:"bytes,18,opt,name=emphasis,proto3,oneof"`
+}
+
+type Node_LinkOrImage struct {
+ LinkOrImage *LinkOrImage `protobuf:"bytes,19,opt,name=link_or_image,json=linkOrImage,proto3,oneof"`
+}
+
+type Node_AutoLink struct {
+ AutoLink *AutoLink `protobuf:"bytes,20,opt,name=auto_link,json=autoLink,proto3,oneof"`
+}
+
+type Node_RawHtml struct {
+ RawHtml *RawHTML `protobuf:"bytes,21,opt,name=raw_html,json=rawHtml,proto3,oneof"`
+}
+
+type Node_Table struct {
+ // Github Flavored Markdown
+ // blocks
+ Table *Table `protobuf:"bytes,22,opt,name=table,proto3,oneof"`
+}
+
+type Node_TableRow struct {
+ TableRow *TableRow `protobuf:"bytes,23,opt,name=table_row,json=tableRow,proto3,oneof"`
+}
+
+type Node_TableCell struct {
+ TableCell *TableCell `protobuf:"bytes,24,opt,name=table_cell,json=tableCell,proto3,oneof"`
+}
+
+type Node_TaskCheckbox struct {
+ // inline
+ TaskCheckbox *TaskCheckbox `protobuf:"bytes,25,opt,name=task_checkbox,json=taskCheckbox,proto3,oneof"`
+}
+
+type Node_Strikethrough struct {
+ Strikethrough *Strikethrough `protobuf:"bytes,26,opt,name=strikethrough,proto3,oneof"`
+}
+
+type Node_ContentNode struct {
+ // Root of the plugin-rendered data
+ ContentNode *FabricContentNode `protobuf:"bytes,254,opt,name=content_node,json=contentNode,proto3,oneof"`
+}
+
+type Node_Custom struct {
+ // Custom node types can be serialized using this
+ Custom *CustomNode `protobuf:"bytes,255,opt,name=custom,proto3,oneof"`
+}
+
+func (*Node_Document) isNode_Kind() {}
+
+func (*Node_TextBlock) isNode_Kind() {}
+
+func (*Node_Paragraph) isNode_Kind() {}
+
+func (*Node_Heading) isNode_Kind() {}
+
+func (*Node_ThematicBreak) isNode_Kind() {}
+
+func (*Node_CodeBlock) isNode_Kind() {}
+
+func (*Node_FencedCodeBlock) isNode_Kind() {}
+
+func (*Node_Blockquote) isNode_Kind() {}
+
+func (*Node_List) isNode_Kind() {}
+
+func (*Node_ListItem) isNode_Kind() {}
+
+func (*Node_HtmlBlock) isNode_Kind() {}
+
+func (*Node_Text) isNode_Kind() {}
+
+func (*Node_String_) isNode_Kind() {}
+
+func (*Node_CodeSpan) isNode_Kind() {}
+
+func (*Node_Emphasis) isNode_Kind() {}
+
+func (*Node_LinkOrImage) isNode_Kind() {}
+
+func (*Node_AutoLink) isNode_Kind() {}
+
+func (*Node_RawHtml) isNode_Kind() {}
+
+func (*Node_Table) isNode_Kind() {}
+
+func (*Node_TableRow) isNode_Kind() {}
+
+func (*Node_TableCell) isNode_Kind() {}
+
+func (*Node_TaskCheckbox) isNode_Kind() {}
+
+func (*Node_Strikethrough) isNode_Kind() {}
+
+func (*Node_ContentNode) isNode_Kind() {}
+
+func (*Node_Custom) isNode_Kind() {}
+
+type Document struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *Document) Reset() {
+ *x = Document{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Document) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Document) ProtoMessage() {}
+
+func (x *Document) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Document.ProtoReflect.Descriptor instead.
+func (*Document) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *Document) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type TextBlock struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *TextBlock) Reset() {
+ *x = TextBlock{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TextBlock) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TextBlock) ProtoMessage() {}
+
+func (x *TextBlock) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TextBlock.ProtoReflect.Descriptor instead.
+func (*TextBlock) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *TextBlock) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type Paragraph struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *Paragraph) Reset() {
+ *x = Paragraph{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Paragraph) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Paragraph) ProtoMessage() {}
+
+func (x *Paragraph) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Paragraph.ProtoReflect.Descriptor instead.
+func (*Paragraph) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *Paragraph) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type Heading struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Level uint32 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"`
+}
+
+func (x *Heading) Reset() {
+ *x = Heading{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Heading) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Heading) ProtoMessage() {}
+
+func (x *Heading) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Heading.ProtoReflect.Descriptor instead.
+func (*Heading) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *Heading) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *Heading) GetLevel() uint32 {
+ if x != nil {
+ return x.Level
+ }
+ return 0
+}
+
+type ThematicBreak struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *ThematicBreak) Reset() {
+ *x = ThematicBreak{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ThematicBreak) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ThematicBreak) ProtoMessage() {}
+
+func (x *ThematicBreak) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[7]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ThematicBreak.ProtoReflect.Descriptor instead.
+func (*ThematicBreak) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *ThematicBreak) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type CodeBlock struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Lines [][]byte `protobuf:"bytes,2,rep,name=lines,proto3" json:"lines,omitempty"`
+}
+
+func (x *CodeBlock) Reset() {
+ *x = CodeBlock{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CodeBlock) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeBlock) ProtoMessage() {}
+
+func (x *CodeBlock) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[8]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeBlock.ProtoReflect.Descriptor instead.
+func (*CodeBlock) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *CodeBlock) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *CodeBlock) GetLines() [][]byte {
+ if x != nil {
+ return x.Lines
+ }
+ return nil
+}
+
+type FencedCodeBlock struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Info *Text `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"`
+ Lines [][]byte `protobuf:"bytes,3,rep,name=lines,proto3" json:"lines,omitempty"`
+}
+
+func (x *FencedCodeBlock) Reset() {
+ *x = FencedCodeBlock{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *FencedCodeBlock) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FencedCodeBlock) ProtoMessage() {}
+
+func (x *FencedCodeBlock) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[9]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FencedCodeBlock.ProtoReflect.Descriptor instead.
+func (*FencedCodeBlock) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *FencedCodeBlock) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *FencedCodeBlock) GetInfo() *Text {
+ if x != nil {
+ return x.Info
+ }
+ return nil
+}
+
+func (x *FencedCodeBlock) GetLines() [][]byte {
+ if x != nil {
+ return x.Lines
+ }
+ return nil
+}
+
+type Blockquote struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *Blockquote) Reset() {
+ *x = Blockquote{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Blockquote) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Blockquote) ProtoMessage() {}
+
+func (x *Blockquote) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Blockquote.ProtoReflect.Descriptor instead.
+func (*Blockquote) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *Blockquote) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type List struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Marker uint32 `protobuf:"varint,2,opt,name=marker,proto3" json:"marker,omitempty"`
+ IsTight bool `protobuf:"varint,3,opt,name=is_tight,json=isTight,proto3" json:"is_tight,omitempty"`
+ Start uint32 `protobuf:"varint,4,opt,name=start,proto3" json:"start,omitempty"`
+}
+
+func (x *List) Reset() {
+ *x = List{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *List) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*List) ProtoMessage() {}
+
+func (x *List) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[11]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use List.ProtoReflect.Descriptor instead.
+func (*List) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *List) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *List) GetMarker() uint32 {
+ if x != nil {
+ return x.Marker
+ }
+ return 0
+}
+
+func (x *List) GetIsTight() bool {
+ if x != nil {
+ return x.IsTight
+ }
+ return false
+}
+
+func (x *List) GetStart() uint32 {
+ if x != nil {
+ return x.Start
+ }
+ return 0
+}
+
+type ListItem struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
+}
+
+func (x *ListItem) Reset() {
+ *x = ListItem{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *ListItem) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListItem) ProtoMessage() {}
+
+func (x *ListItem) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[12]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListItem.ProtoReflect.Descriptor instead.
+func (*ListItem) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *ListItem) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *ListItem) GetOffset() int64 {
+ if x != nil {
+ return x.Offset
+ }
+ return 0
+}
+
+type HTMLBlock struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Type HTMLBlockType `protobuf:"varint,2,opt,name=type,proto3,enum=ast.v1.HTMLBlockType" json:"type,omitempty"`
+ Lines [][]byte `protobuf:"bytes,3,rep,name=lines,proto3" json:"lines,omitempty"`
+ ClosureLine []byte `protobuf:"bytes,4,opt,name=closure_line,json=closureLine,proto3" json:"closure_line,omitempty"`
+}
+
+func (x *HTMLBlock) Reset() {
+ *x = HTMLBlock{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *HTMLBlock) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HTMLBlock) ProtoMessage() {}
+
+func (x *HTMLBlock) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[13]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use HTMLBlock.ProtoReflect.Descriptor instead.
+func (*HTMLBlock) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *HTMLBlock) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *HTMLBlock) GetType() HTMLBlockType {
+ if x != nil {
+ return x.Type
+ }
+ return HTMLBlockType_HTML_BLOCK_TYPE_UNSPECIFIED
+}
+
+func (x *HTMLBlock) GetLines() [][]byte {
+ if x != nil {
+ return x.Lines
+ }
+ return nil
+}
+
+func (x *HTMLBlock) GetClosureLine() []byte {
+ if x != nil {
+ return x.ClosureLine
+ }
+ return nil
+}
+
+type Text struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Segment []byte `protobuf:"bytes,2,opt,name=segment,proto3" json:"segment,omitempty"`
+ SoftLineBreak bool `protobuf:"varint,3,opt,name=soft_line_break,json=softLineBreak,proto3" json:"soft_line_break,omitempty"`
+ HardLineBreak bool `protobuf:"varint,4,opt,name=hard_line_break,json=hardLineBreak,proto3" json:"hard_line_break,omitempty"`
+ Raw bool `protobuf:"varint,5,opt,name=raw,proto3" json:"raw,omitempty"`
+}
+
+func (x *Text) Reset() {
+ *x = Text{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Text) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Text) ProtoMessage() {}
+
+func (x *Text) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[14]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Text.ProtoReflect.Descriptor instead.
+func (*Text) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{14}
+}
+
+func (x *Text) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *Text) GetSegment() []byte {
+ if x != nil {
+ return x.Segment
+ }
+ return nil
+}
+
+func (x *Text) GetSoftLineBreak() bool {
+ if x != nil {
+ return x.SoftLineBreak
+ }
+ return false
+}
+
+func (x *Text) GetHardLineBreak() bool {
+ if x != nil {
+ return x.HardLineBreak
+ }
+ return false
+}
+
+func (x *Text) GetRaw() bool {
+ if x != nil {
+ return x.Raw
+ }
+ return false
+}
+
+type String struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
+ Raw bool `protobuf:"varint,3,opt,name=raw,proto3" json:"raw,omitempty"`
+ Code bool `protobuf:"varint,4,opt,name=code,proto3" json:"code,omitempty"`
+}
+
+func (x *String) Reset() {
+ *x = String{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *String) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*String) ProtoMessage() {}
+
+func (x *String) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[15]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use String.ProtoReflect.Descriptor instead.
+func (*String) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{15}
+}
+
+func (x *String) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *String) GetValue() []byte {
+ if x != nil {
+ return x.Value
+ }
+ return nil
+}
+
+func (x *String) GetRaw() bool {
+ if x != nil {
+ return x.Raw
+ }
+ return false
+}
+
+func (x *String) GetCode() bool {
+ if x != nil {
+ return x.Code
+ }
+ return false
+}
+
+type CodeSpan struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *CodeSpan) Reset() {
+ *x = CodeSpan{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CodeSpan) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CodeSpan) ProtoMessage() {}
+
+func (x *CodeSpan) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[16]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CodeSpan.ProtoReflect.Descriptor instead.
+func (*CodeSpan) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{16}
+}
+
+func (x *CodeSpan) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type Emphasis struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Level int64 `protobuf:"varint,2,opt,name=level,proto3" json:"level,omitempty"`
+}
+
+func (x *Emphasis) Reset() {
+ *x = Emphasis{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Emphasis) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Emphasis) ProtoMessage() {}
+
+func (x *Emphasis) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[17]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Emphasis.ProtoReflect.Descriptor instead.
+func (*Emphasis) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{17}
+}
+
+func (x *Emphasis) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *Emphasis) GetLevel() int64 {
+ if x != nil {
+ return x.Level
+ }
+ return 0
+}
+
+type LinkOrImage struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Destination []byte `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"`
+ Title []byte `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
+ IsImage bool `protobuf:"varint,4,opt,name=is_image,json=isImage,proto3" json:"is_image,omitempty"`
+}
+
+func (x *LinkOrImage) Reset() {
+ *x = LinkOrImage{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[18]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *LinkOrImage) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LinkOrImage) ProtoMessage() {}
+
+func (x *LinkOrImage) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[18]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use LinkOrImage.ProtoReflect.Descriptor instead.
+func (*LinkOrImage) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{18}
+}
+
+func (x *LinkOrImage) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *LinkOrImage) GetDestination() []byte {
+ if x != nil {
+ return x.Destination
+ }
+ return nil
+}
+
+func (x *LinkOrImage) GetTitle() []byte {
+ if x != nil {
+ return x.Title
+ }
+ return nil
+}
+
+func (x *LinkOrImage) GetIsImage() bool {
+ if x != nil {
+ return x.IsImage
+ }
+ return false
+}
+
+type AutoLink struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Type AutoLinkType `protobuf:"varint,2,opt,name=type,proto3,enum=ast.v1.AutoLinkType" json:"type,omitempty"`
+ Protocol []byte `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
+ Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (x *AutoLink) Reset() {
+ *x = AutoLink{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[19]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *AutoLink) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AutoLink) ProtoMessage() {}
+
+func (x *AutoLink) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[19]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use AutoLink.ProtoReflect.Descriptor instead.
+func (*AutoLink) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{19}
+}
+
+func (x *AutoLink) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *AutoLink) GetType() AutoLinkType {
+ if x != nil {
+ return x.Type
+ }
+ return AutoLinkType_AUTO_LINK_TYPE_UNSPECIFIED
+}
+
+func (x *AutoLink) GetProtocol() []byte {
+ if x != nil {
+ return x.Protocol
+ }
+ return nil
+}
+
+func (x *AutoLink) GetValue() []byte {
+ if x != nil {
+ return x.Value
+ }
+ return nil
+}
+
+type RawHTML struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Segments [][]byte `protobuf:"bytes,2,rep,name=segments,proto3" json:"segments,omitempty"`
+}
+
+func (x *RawHTML) Reset() {
+ *x = RawHTML{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[20]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *RawHTML) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RawHTML) ProtoMessage() {}
+
+func (x *RawHTML) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[20]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use RawHTML.ProtoReflect.Descriptor instead.
+func (*RawHTML) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{20}
+}
+
+func (x *RawHTML) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *RawHTML) GetSegments() [][]byte {
+ if x != nil {
+ return x.Segments
+ }
+ return nil
+}
+
+type Table struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Alignments []CellAlignment `protobuf:"varint,2,rep,packed,name=alignments,proto3,enum=ast.v1.CellAlignment" json:"alignments,omitempty"`
+}
+
+func (x *Table) Reset() {
+ *x = Table{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[21]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Table) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Table) ProtoMessage() {}
+
+func (x *Table) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[21]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Table.ProtoReflect.Descriptor instead.
+func (*Table) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{21}
+}
+
+func (x *Table) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *Table) GetAlignments() []CellAlignment {
+ if x != nil {
+ return x.Alignments
+ }
+ return nil
+}
+
+type TableRow struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Alignments []CellAlignment `protobuf:"varint,2,rep,packed,name=alignments,proto3,enum=ast.v1.CellAlignment" json:"alignments,omitempty"`
+ IsHeader bool `protobuf:"varint,4,opt,name=is_header,json=isHeader,proto3" json:"is_header,omitempty"`
+}
+
+func (x *TableRow) Reset() {
+ *x = TableRow{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[22]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TableRow) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TableRow) ProtoMessage() {}
+
+func (x *TableRow) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[22]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TableRow.ProtoReflect.Descriptor instead.
+func (*TableRow) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{22}
+}
+
+func (x *TableRow) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *TableRow) GetAlignments() []CellAlignment {
+ if x != nil {
+ return x.Alignments
+ }
+ return nil
+}
+
+func (x *TableRow) GetIsHeader() bool {
+ if x != nil {
+ return x.IsHeader
+ }
+ return false
+}
+
+type TableCell struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ Alignment CellAlignment `protobuf:"varint,2,opt,name=alignment,proto3,enum=ast.v1.CellAlignment" json:"alignment,omitempty"`
+}
+
+func (x *TableCell) Reset() {
+ *x = TableCell{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[23]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TableCell) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TableCell) ProtoMessage() {}
+
+func (x *TableCell) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[23]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TableCell.ProtoReflect.Descriptor instead.
+func (*TableCell) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{23}
+}
+
+func (x *TableCell) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *TableCell) GetAlignment() CellAlignment {
+ if x != nil {
+ return x.Alignment
+ }
+ return CellAlignment_CELL_ALIGNMENT_UNSPECIFIED
+}
+
+type TaskCheckbox struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+ IsChecked bool `protobuf:"varint,2,opt,name=is_checked,json=isChecked,proto3" json:"is_checked,omitempty"`
+}
+
+func (x *TaskCheckbox) Reset() {
+ *x = TaskCheckbox{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[24]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *TaskCheckbox) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TaskCheckbox) ProtoMessage() {}
+
+func (x *TaskCheckbox) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[24]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use TaskCheckbox.ProtoReflect.Descriptor instead.
+func (*TaskCheckbox) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{24}
+}
+
+func (x *TaskCheckbox) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+func (x *TaskCheckbox) GetIsChecked() bool {
+ if x != nil {
+ return x.IsChecked
+ }
+ return false
+}
+
+type Strikethrough struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Base *BaseNode `protobuf:"bytes,1,opt,name=base,proto3" json:"base,omitempty"`
+}
+
+func (x *Strikethrough) Reset() {
+ *x = Strikethrough{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[25]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Strikethrough) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Strikethrough) ProtoMessage() {}
+
+func (x *Strikethrough) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[25]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Strikethrough.ProtoReflect.Descriptor instead.
+func (*Strikethrough) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{25}
+}
+
+func (x *Strikethrough) GetBase() *BaseNode {
+ if x != nil {
+ return x.Base
+ }
+ return nil
+}
+
+type CustomNode struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Indicates that this block is an inline element
+ IsInline bool `protobuf:"varint,1,opt,name=is_inline,json=isInline,proto3" json:"is_inline,omitempty"`
+ Data *anypb.Any `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+ BlankPreviousLines bool `protobuf:"varint,3,opt,name=blank_previous_lines,json=blankPreviousLines,proto3" json:"blank_previous_lines,omitempty"`
+}
+
+func (x *CustomNode) Reset() {
+ *x = CustomNode{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[26]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *CustomNode) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CustomNode) ProtoMessage() {}
+
+func (x *CustomNode) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[26]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CustomNode.ProtoReflect.Descriptor instead.
+func (*CustomNode) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{26}
+}
+
+func (x *CustomNode) GetIsInline() bool {
+ if x != nil {
+ return x.IsInline
+ }
+ return false
+}
+
+func (x *CustomNode) GetData() *anypb.Any {
+ if x != nil {
+ return x.Data
+ }
+ return nil
+}
+
+func (x *CustomNode) GetBlankPreviousLines() bool {
+ if x != nil {
+ return x.BlankPreviousLines
+ }
+ return false
+}
+
+type Metadata struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // ie "blackstork/builtin"
+ Provider string `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"`
+ // ie "title"
+ Plugin string `protobuf:"bytes,2,opt,name=plugin,proto3" json:"plugin,omitempty"`
+ Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"`
+}
+
+func (x *Metadata) Reset() {
+ *x = Metadata{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[27]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Metadata) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Metadata) ProtoMessage() {}
+
+func (x *Metadata) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[27]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Metadata.ProtoReflect.Descriptor instead.
+func (*Metadata) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{27}
+}
+
+func (x *Metadata) GetProvider() string {
+ if x != nil {
+ return x.Provider
+ }
+ return ""
+}
+
+func (x *Metadata) GetPlugin() string {
+ if x != nil {
+ return x.Plugin
+ }
+ return ""
+}
+
+func (x *Metadata) GetVersion() string {
+ if x != nil {
+ return x.Version
+ }
+ return ""
+}
+
+// Root of the plugin-rendered data
+type FabricContentNode struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"`
+ Root *BaseNode `protobuf:"bytes,2,opt,name=root,proto3" json:"root,omitempty"` // direct content, no document node
+}
+
+func (x *FabricContentNode) Reset() {
+ *x = FabricContentNode{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_ast_v1_ast_proto_msgTypes[28]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *FabricContentNode) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FabricContentNode) ProtoMessage() {}
+
+func (x *FabricContentNode) ProtoReflect() protoreflect.Message {
+ mi := &file_ast_v1_ast_proto_msgTypes[28]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FabricContentNode.ProtoReflect.Descriptor instead.
+func (*FabricContentNode) Descriptor() ([]byte, []int) {
+ return file_ast_v1_ast_proto_rawDescGZIP(), []int{28}
+}
+
+func (x *FabricContentNode) GetMetadata() *Metadata {
+ if x != nil {
+ return x.Metadata
+ }
+ return nil
+}
+
+func (x *FabricContentNode) GetRoot() *BaseNode {
+ if x != nil {
+ return x.Root
+ }
+ return nil
+}
+
+var File_ast_v1_ast_proto protoreflect.FileDescriptor
+
+var file_ast_v1_ast_proto_rawDesc = []byte{
+ 0x0a, 0x10, 0x61, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x12, 0x06, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x54, 0x0a, 0x09, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12,
+ 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x73,
+ 0x74, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x08,
+ 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c,
+ 0x64, 0x72, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x73, 0x74,
+ 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72,
+ 0x65, 0x6e, 0x12, 0x31, 0x0a, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
+ 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e,
+ 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69,
+ 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x5f, 0x70,
+ 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x12, 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f,
+ 0x75, 0x73, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x8d, 0x0a, 0x0a, 0x04, 0x4e, 0x6f, 0x64, 0x65,
+ 0x12, 0x2e, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x6f, 0x63, 0x75,
+ 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x12, 0x32, 0x0a, 0x0a, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65,
+ 0x78, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x09, 0x74, 0x65, 0x78, 0x74, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x31, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70,
+ 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x50, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x48, 0x00, 0x52, 0x09, 0x70, 0x61,
+ 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x12, 0x2b, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x69,
+ 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76,
+ 0x31, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x07, 0x68, 0x65, 0x61,
+ 0x64, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x0e, 0x74, 0x68, 0x65, 0x6d, 0x61, 0x74, 0x69, 0x63,
+ 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61,
+ 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x68, 0x65, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x42, 0x72,
+ 0x65, 0x61, 0x6b, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x68, 0x65, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x42,
+ 0x72, 0x65, 0x61, 0x6b, 0x12, 0x32, 0x0a, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x62, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76,
+ 0x31, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x09, 0x63,
+ 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x45, 0x0a, 0x11, 0x66, 0x65, 0x6e, 0x63,
+ 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x0a, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x6e,
+ 0x63, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x0f,
+ 0x66, 0x65, 0x6e, 0x63, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12,
+ 0x34, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x18, 0x0b, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6c, 0x6f,
+ 0x63, 0x6b, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x71, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0c, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73,
+ 0x74, 0x48, 0x00, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x09, 0x6c, 0x69, 0x73,
+ 0x74, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61,
+ 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x48, 0x00,
+ 0x52, 0x08, 0x6c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x32, 0x0a, 0x0a, 0x68, 0x74,
+ 0x6d, 0x6c, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11,
+ 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x54, 0x4d, 0x4c, 0x42, 0x6c, 0x6f, 0x63,
+ 0x6b, 0x48, 0x00, 0x52, 0x09, 0x68, 0x74, 0x6d, 0x6c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x22,
+ 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61,
+ 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x48, 0x00, 0x52, 0x04, 0x74, 0x65,
+ 0x78, 0x74, 0x12, 0x28, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x10, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x09,
+ 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x70, 0x61,
+ 0x6e, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x2e, 0x0a,
+ 0x08, 0x65, 0x6d, 0x70, 0x68, 0x61, 0x73, 0x69, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6d, 0x70, 0x68, 0x61, 0x73, 0x69,
+ 0x73, 0x48, 0x00, 0x52, 0x08, 0x65, 0x6d, 0x70, 0x68, 0x61, 0x73, 0x69, 0x73, 0x12, 0x39, 0x0a,
+ 0x0d, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x72, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x13,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
+ 0x6e, 0x6b, 0x4f, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x4f, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x2f, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x6f,
+ 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73,
+ 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x48, 0x00, 0x52,
+ 0x08, 0x61, 0x75, 0x74, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x2c, 0x0a, 0x08, 0x72, 0x61, 0x77,
+ 0x5f, 0x68, 0x74, 0x6d, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x73,
+ 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x61, 0x77, 0x48, 0x54, 0x4d, 0x4c, 0x48, 0x00, 0x52, 0x07,
+ 0x72, 0x61, 0x77, 0x48, 0x74, 0x6d, 0x6c, 0x12, 0x25, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65,
+ 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e,
+ 0x54, 0x61, 0x62, 0x6c, 0x65, 0x48, 0x00, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2f,
+ 0x0a, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x6f, 0x77, 0x18, 0x17, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62, 0x6c, 0x65,
+ 0x52, 0x6f, 0x77, 0x48, 0x00, 0x52, 0x08, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x12,
+ 0x32, 0x0a, 0x0a, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x65, 0x6c, 0x6c, 0x18, 0x18, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x62,
+ 0x6c, 0x65, 0x43, 0x65, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x43,
+ 0x65, 0x6c, 0x6c, 0x12, 0x3b, 0x0a, 0x0d, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x63, 0x68, 0x65, 0x63,
+ 0x6b, 0x62, 0x6f, 0x78, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x73, 0x74,
+ 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 0x78,
+ 0x48, 0x00, 0x52, 0x0c, 0x74, 0x61, 0x73, 0x6b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 0x78,
+ 0x12, 0x3d, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x69, 0x6b, 0x65, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67,
+ 0x68, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6b, 0x65, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x48, 0x00,
+ 0x52, 0x0d, 0x73, 0x74, 0x72, 0x69, 0x6b, 0x65, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x12,
+ 0x3f, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18,
+ 0xfe, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e,
+ 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64,
+ 0x65, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x64, 0x65,
+ 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x18, 0xff, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f,
+ 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x48, 0x00, 0x52, 0x06, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x42,
+ 0x06, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x30, 0x0a, 0x08, 0x44, 0x6f, 0x63, 0x75, 0x6d,
+ 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e,
+ 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x31, 0x0a, 0x09, 0x54, 0x65, 0x78,
+ 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61,
+ 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x31, 0x0a, 0x09,
+ 0x50, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22,
+ 0x45, 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61,
+ 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76,
+ 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65,
+ 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
+ 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x35, 0x0a, 0x0d, 0x54, 0x68, 0x65, 0x6d, 0x61, 0x74,
+ 0x69, 0x63, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42,
+ 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x47, 0x0a,
+ 0x09, 0x43, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61,
+ 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76,
+ 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65,
+ 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52,
+ 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x6f, 0x0a, 0x0f, 0x46, 0x65, 0x6e, 0x63, 0x65, 0x64,
+ 0x43, 0x6f, 0x64, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12,
+ 0x20, 0x0a, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
+ 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x52, 0x04, 0x69, 0x6e, 0x66,
+ 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c,
+ 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x32, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b,
+ 0x71, 0x75, 0x6f, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73,
+ 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x75, 0x0a, 0x04, 0x4c,
+ 0x69, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e,
+ 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x61, 0x72,
+ 0x6b, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6d, 0x61, 0x72, 0x6b, 0x65,
+ 0x72, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x74, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x54, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05,
+ 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x73, 0x74, 0x61,
+ 0x72, 0x74, 0x22, 0x48, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x24,
+ 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61,
+ 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04,
+ 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x95, 0x01, 0x0a,
+ 0x09, 0x48, 0x54, 0x4d, 0x4c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61,
+ 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76,
+ 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65,
+ 0x12, 0x29, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15,
+ 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x54, 0x4d, 0x4c, 0x42, 0x6c, 0x6f, 0x63,
+ 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c,
+ 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65,
+ 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x5f, 0x6c, 0x69, 0x6e,
+ 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65,
+ 0x4c, 0x69, 0x6e, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x04, 0x54, 0x65, 0x78, 0x74, 0x12, 0x24, 0x0a,
+ 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73,
+ 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62,
+ 0x61, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x26, 0x0a,
+ 0x0f, 0x73, 0x6f, 0x66, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6f, 0x66, 0x74, 0x4c, 0x69, 0x6e, 0x65,
+ 0x42, 0x72, 0x65, 0x61, 0x6b, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x6c, 0x69,
+ 0x6e, 0x65, 0x5f, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
+ 0x68, 0x61, 0x72, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x42, 0x72, 0x65, 0x61, 0x6b, 0x12, 0x10, 0x0a,
+ 0x03, 0x72, 0x61, 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x72, 0x61, 0x77, 0x22,
+ 0x6a, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12,
+ 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x72, 0x61, 0x77, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x03, 0x72, 0x61, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18,
+ 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x30, 0x0a, 0x08, 0x43,
+ 0x6f, 0x64, 0x65, 0x53, 0x70, 0x61, 0x6e, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42,
+ 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x46, 0x0a,
+ 0x08, 0x45, 0x6d, 0x70, 0x68, 0x61, 0x73, 0x69, 0x73, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12,
+ 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05,
+ 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x86, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x6b, 0x4f, 0x72,
+ 0x49, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73,
+ 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64,
+ 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a,
+ 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x74, 0x69,
+ 0x74, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18,
+ 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x8c,
+ 0x01, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x24, 0x0a, 0x04, 0x62,
+ 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e,
+ 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73,
+ 0x65, 0x12, 0x28, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x14, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x4c, 0x69, 0x6e,
+ 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x4b, 0x0a,
+ 0x07, 0x52, 0x61, 0x77, 0x48, 0x54, 0x4d, 0x4c, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e,
+ 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x1a,
+ 0x0a, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c,
+ 0x52, 0x08, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x64, 0x0a, 0x05, 0x54, 0x61,
+ 0x62, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e,
+ 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0a, 0x61, 0x6c, 0x69,
+ 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e,
+ 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x41, 0x6c, 0x69, 0x67, 0x6e,
+ 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73,
+ 0x22, 0x84, 0x01, 0x0a, 0x08, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x6f, 0x77, 0x12, 0x24, 0x0a,
+ 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73,
+ 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62,
+ 0x61, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x0a, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74,
+ 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0a,
+ 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73,
+ 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69,
+ 0x73, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x66, 0x0a, 0x09, 0x54, 0x61, 0x62, 0x6c, 0x65,
+ 0x43, 0x65, 0x6c, 0x6c, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65,
+ 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x09, 0x61, 0x6c,
+ 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
+ 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x65, 0x6c, 0x6c, 0x41, 0x6c, 0x69, 0x67, 0x6e,
+ 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x22,
+ 0x53, 0x0a, 0x0c, 0x54, 0x61, 0x73, 0x6b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x62, 0x6f, 0x78, 0x12,
+ 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e,
+ 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52,
+ 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63,
+ 0x6b, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x43, 0x68, 0x65,
+ 0x63, 0x6b, 0x65, 0x64, 0x22, 0x35, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x69, 0x6b, 0x65, 0x74, 0x68,
+ 0x72, 0x6f, 0x75, 0x67, 0x68, 0x12, 0x24, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73,
+ 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x85, 0x01, 0x0a, 0x0a,
+ 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73,
+ 0x5f, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69,
+ 0x73, 0x49, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74,
+ 0x61, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69,
+ 0x6f, 0x75, 0x73, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x12, 0x62, 0x6c, 0x61, 0x6e, 0x6b, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x4c, 0x69,
+ 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12,
+ 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x70,
+ 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x75,
+ 0x67, 0x69, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x67, 0x0a,
+ 0x11, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4e, 0x6f,
+ 0x64, 0x65, 0x12, 0x2c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+ 0x12, 0x24, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
+ 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x73, 0x65, 0x4e, 0x6f, 0x64, 0x65,
+ 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x74, 0x2a, 0xd1, 0x01, 0x0a, 0x0d, 0x48, 0x54, 0x4d, 0x4c, 0x42,
+ 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x48, 0x54, 0x4d, 0x4c,
+ 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50,
+ 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x4d,
+ 0x4c, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x31, 0x10, 0x01,
+ 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x4d, 0x4c, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x54,
+ 0x59, 0x50, 0x45, 0x5f, 0x32, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x4d, 0x4c, 0x5f,
+ 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x33, 0x10, 0x03, 0x12, 0x15,
+ 0x0a, 0x11, 0x48, 0x54, 0x4d, 0x4c, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x54, 0x59, 0x50,
+ 0x45, 0x5f, 0x34, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x4d, 0x4c, 0x5f, 0x42, 0x4c,
+ 0x4f, 0x43, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x35, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11,
+ 0x48, 0x54, 0x4d, 0x4c, 0x5f, 0x42, 0x4c, 0x4f, 0x43, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
+ 0x36, 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x48, 0x54, 0x4d, 0x4c, 0x5f, 0x42, 0x4c, 0x4f, 0x43,
+ 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x37, 0x10, 0x07, 0x2a, 0x60, 0x0a, 0x0c, 0x41, 0x75,
+ 0x74, 0x6f, 0x4c, 0x69, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x55,
+ 0x54, 0x4f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53,
+ 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x55,
+ 0x54, 0x4f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4d, 0x41,
+ 0x49, 0x4c, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x4c, 0x49, 0x4e,
+ 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x52, 0x4c, 0x10, 0x02, 0x2a, 0x96, 0x01, 0x0a,
+ 0x0d, 0x43, 0x65, 0x6c, 0x6c, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1e,
+ 0x0a, 0x1a, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x41, 0x4c, 0x49, 0x47, 0x4e, 0x4d, 0x45, 0x4e, 0x54,
+ 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17,
+ 0x0a, 0x13, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x41, 0x4c, 0x49, 0x47, 0x4e, 0x4d, 0x45, 0x4e, 0x54,
+ 0x5f, 0x4c, 0x45, 0x46, 0x54, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x45, 0x4c, 0x4c, 0x5f,
+ 0x41, 0x4c, 0x49, 0x47, 0x4e, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x52, 0x49, 0x47, 0x48, 0x54, 0x10,
+ 0x02, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x41, 0x4c, 0x49, 0x47, 0x4e, 0x4d,
+ 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13,
+ 0x43, 0x45, 0x4c, 0x4c, 0x5f, 0x41, 0x4c, 0x49, 0x47, 0x4e, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x4e,
+ 0x4f, 0x4e, 0x45, 0x10, 0x04, 0x42, 0x84, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x73,
+ 0x74, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x41, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
+ 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61,
+ 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69,
+ 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x61, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x3b,
+ 0x61, 0x73, 0x74, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x41, 0x73,
+ 0x74, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x06, 0x41, 0x73, 0x74, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x12,
+ 0x41, 0x73, 0x74, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0xea, 0x02, 0x07, 0x41, 0x73, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_ast_v1_ast_proto_rawDescOnce sync.Once
+ file_ast_v1_ast_proto_rawDescData = file_ast_v1_ast_proto_rawDesc
+)
+
+func file_ast_v1_ast_proto_rawDescGZIP() []byte {
+ file_ast_v1_ast_proto_rawDescOnce.Do(func() {
+ file_ast_v1_ast_proto_rawDescData = protoimpl.X.CompressGZIP(file_ast_v1_ast_proto_rawDescData)
+ })
+ return file_ast_v1_ast_proto_rawDescData
+}
+
+var file_ast_v1_ast_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
+var file_ast_v1_ast_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
+var file_ast_v1_ast_proto_goTypes = []any{
+ (HTMLBlockType)(0), // 0: ast.v1.HTMLBlockType
+ (AutoLinkType)(0), // 1: ast.v1.AutoLinkType
+ (CellAlignment)(0), // 2: ast.v1.CellAlignment
+ (*Attribute)(nil), // 3: ast.v1.Attribute
+ (*BaseNode)(nil), // 4: ast.v1.BaseNode
+ (*Node)(nil), // 5: ast.v1.Node
+ (*Document)(nil), // 6: ast.v1.Document
+ (*TextBlock)(nil), // 7: ast.v1.TextBlock
+ (*Paragraph)(nil), // 8: ast.v1.Paragraph
+ (*Heading)(nil), // 9: ast.v1.Heading
+ (*ThematicBreak)(nil), // 10: ast.v1.ThematicBreak
+ (*CodeBlock)(nil), // 11: ast.v1.CodeBlock
+ (*FencedCodeBlock)(nil), // 12: ast.v1.FencedCodeBlock
+ (*Blockquote)(nil), // 13: ast.v1.Blockquote
+ (*List)(nil), // 14: ast.v1.List
+ (*ListItem)(nil), // 15: ast.v1.ListItem
+ (*HTMLBlock)(nil), // 16: ast.v1.HTMLBlock
+ (*Text)(nil), // 17: ast.v1.Text
+ (*String)(nil), // 18: ast.v1.String
+ (*CodeSpan)(nil), // 19: ast.v1.CodeSpan
+ (*Emphasis)(nil), // 20: ast.v1.Emphasis
+ (*LinkOrImage)(nil), // 21: ast.v1.LinkOrImage
+ (*AutoLink)(nil), // 22: ast.v1.AutoLink
+ (*RawHTML)(nil), // 23: ast.v1.RawHTML
+ (*Table)(nil), // 24: ast.v1.Table
+ (*TableRow)(nil), // 25: ast.v1.TableRow
+ (*TableCell)(nil), // 26: ast.v1.TableCell
+ (*TaskCheckbox)(nil), // 27: ast.v1.TaskCheckbox
+ (*Strikethrough)(nil), // 28: ast.v1.Strikethrough
+ (*CustomNode)(nil), // 29: ast.v1.CustomNode
+ (*Metadata)(nil), // 30: ast.v1.Metadata
+ (*FabricContentNode)(nil), // 31: ast.v1.FabricContentNode
+ (*anypb.Any)(nil), // 32: google.protobuf.Any
+}
+var file_ast_v1_ast_proto_depIdxs = []int32{
+ 5, // 0: ast.v1.BaseNode.children:type_name -> ast.v1.Node
+ 3, // 1: ast.v1.BaseNode.attributes:type_name -> ast.v1.Attribute
+ 6, // 2: ast.v1.Node.document:type_name -> ast.v1.Document
+ 7, // 3: ast.v1.Node.text_block:type_name -> ast.v1.TextBlock
+ 8, // 4: ast.v1.Node.paragraph:type_name -> ast.v1.Paragraph
+ 9, // 5: ast.v1.Node.heading:type_name -> ast.v1.Heading
+ 10, // 6: ast.v1.Node.thematic_break:type_name -> ast.v1.ThematicBreak
+ 11, // 7: ast.v1.Node.code_block:type_name -> ast.v1.CodeBlock
+ 12, // 8: ast.v1.Node.fenced_code_block:type_name -> ast.v1.FencedCodeBlock
+ 13, // 9: ast.v1.Node.blockquote:type_name -> ast.v1.Blockquote
+ 14, // 10: ast.v1.Node.list:type_name -> ast.v1.List
+ 15, // 11: ast.v1.Node.list_item:type_name -> ast.v1.ListItem
+ 16, // 12: ast.v1.Node.html_block:type_name -> ast.v1.HTMLBlock
+ 17, // 13: ast.v1.Node.text:type_name -> ast.v1.Text
+ 18, // 14: ast.v1.Node.string:type_name -> ast.v1.String
+ 19, // 15: ast.v1.Node.code_span:type_name -> ast.v1.CodeSpan
+ 20, // 16: ast.v1.Node.emphasis:type_name -> ast.v1.Emphasis
+ 21, // 17: ast.v1.Node.link_or_image:type_name -> ast.v1.LinkOrImage
+ 22, // 18: ast.v1.Node.auto_link:type_name -> ast.v1.AutoLink
+ 23, // 19: ast.v1.Node.raw_html:type_name -> ast.v1.RawHTML
+ 24, // 20: ast.v1.Node.table:type_name -> ast.v1.Table
+ 25, // 21: ast.v1.Node.table_row:type_name -> ast.v1.TableRow
+ 26, // 22: ast.v1.Node.table_cell:type_name -> ast.v1.TableCell
+ 27, // 23: ast.v1.Node.task_checkbox:type_name -> ast.v1.TaskCheckbox
+ 28, // 24: ast.v1.Node.strikethrough:type_name -> ast.v1.Strikethrough
+ 31, // 25: ast.v1.Node.content_node:type_name -> ast.v1.FabricContentNode
+ 29, // 26: ast.v1.Node.custom:type_name -> ast.v1.CustomNode
+ 4, // 27: ast.v1.Document.base:type_name -> ast.v1.BaseNode
+ 4, // 28: ast.v1.TextBlock.base:type_name -> ast.v1.BaseNode
+ 4, // 29: ast.v1.Paragraph.base:type_name -> ast.v1.BaseNode
+ 4, // 30: ast.v1.Heading.base:type_name -> ast.v1.BaseNode
+ 4, // 31: ast.v1.ThematicBreak.base:type_name -> ast.v1.BaseNode
+ 4, // 32: ast.v1.CodeBlock.base:type_name -> ast.v1.BaseNode
+ 4, // 33: ast.v1.FencedCodeBlock.base:type_name -> ast.v1.BaseNode
+ 17, // 34: ast.v1.FencedCodeBlock.info:type_name -> ast.v1.Text
+ 4, // 35: ast.v1.Blockquote.base:type_name -> ast.v1.BaseNode
+ 4, // 36: ast.v1.List.base:type_name -> ast.v1.BaseNode
+ 4, // 37: ast.v1.ListItem.base:type_name -> ast.v1.BaseNode
+ 4, // 38: ast.v1.HTMLBlock.base:type_name -> ast.v1.BaseNode
+ 0, // 39: ast.v1.HTMLBlock.type:type_name -> ast.v1.HTMLBlockType
+ 4, // 40: ast.v1.Text.base:type_name -> ast.v1.BaseNode
+ 4, // 41: ast.v1.String.base:type_name -> ast.v1.BaseNode
+ 4, // 42: ast.v1.CodeSpan.base:type_name -> ast.v1.BaseNode
+ 4, // 43: ast.v1.Emphasis.base:type_name -> ast.v1.BaseNode
+ 4, // 44: ast.v1.LinkOrImage.base:type_name -> ast.v1.BaseNode
+ 4, // 45: ast.v1.AutoLink.base:type_name -> ast.v1.BaseNode
+ 1, // 46: ast.v1.AutoLink.type:type_name -> ast.v1.AutoLinkType
+ 4, // 47: ast.v1.RawHTML.base:type_name -> ast.v1.BaseNode
+ 4, // 48: ast.v1.Table.base:type_name -> ast.v1.BaseNode
+ 2, // 49: ast.v1.Table.alignments:type_name -> ast.v1.CellAlignment
+ 4, // 50: ast.v1.TableRow.base:type_name -> ast.v1.BaseNode
+ 2, // 51: ast.v1.TableRow.alignments:type_name -> ast.v1.CellAlignment
+ 4, // 52: ast.v1.TableCell.base:type_name -> ast.v1.BaseNode
+ 2, // 53: ast.v1.TableCell.alignment:type_name -> ast.v1.CellAlignment
+ 4, // 54: ast.v1.TaskCheckbox.base:type_name -> ast.v1.BaseNode
+ 4, // 55: ast.v1.Strikethrough.base:type_name -> ast.v1.BaseNode
+ 32, // 56: ast.v1.CustomNode.data:type_name -> google.protobuf.Any
+ 30, // 57: ast.v1.FabricContentNode.metadata:type_name -> ast.v1.Metadata
+ 4, // 58: ast.v1.FabricContentNode.root:type_name -> ast.v1.BaseNode
+ 59, // [59:59] is the sub-list for method output_type
+ 59, // [59:59] is the sub-list for method input_type
+ 59, // [59:59] is the sub-list for extension type_name
+ 59, // [59:59] is the sub-list for extension extendee
+ 0, // [0:59] is the sub-list for field type_name
+}
+
+func init() { file_ast_v1_ast_proto_init() }
+func file_ast_v1_ast_proto_init() {
+ if File_ast_v1_ast_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_ast_v1_ast_proto_msgTypes[0].Exporter = func(v any, i int) any {
+ switch v := v.(*Attribute); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[1].Exporter = func(v any, i int) any {
+ switch v := v.(*BaseNode); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[2].Exporter = func(v any, i int) any {
+ switch v := v.(*Node); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[3].Exporter = func(v any, i int) any {
+ switch v := v.(*Document); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[4].Exporter = func(v any, i int) any {
+ switch v := v.(*TextBlock); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[5].Exporter = func(v any, i int) any {
+ switch v := v.(*Paragraph); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[6].Exporter = func(v any, i int) any {
+ switch v := v.(*Heading); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[7].Exporter = func(v any, i int) any {
+ switch v := v.(*ThematicBreak); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[8].Exporter = func(v any, i int) any {
+ switch v := v.(*CodeBlock); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[9].Exporter = func(v any, i int) any {
+ switch v := v.(*FencedCodeBlock); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[10].Exporter = func(v any, i int) any {
+ switch v := v.(*Blockquote); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[11].Exporter = func(v any, i int) any {
+ switch v := v.(*List); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[12].Exporter = func(v any, i int) any {
+ switch v := v.(*ListItem); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[13].Exporter = func(v any, i int) any {
+ switch v := v.(*HTMLBlock); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[14].Exporter = func(v any, i int) any {
+ switch v := v.(*Text); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[15].Exporter = func(v any, i int) any {
+ switch v := v.(*String); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[16].Exporter = func(v any, i int) any {
+ switch v := v.(*CodeSpan); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[17].Exporter = func(v any, i int) any {
+ switch v := v.(*Emphasis); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[18].Exporter = func(v any, i int) any {
+ switch v := v.(*LinkOrImage); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[19].Exporter = func(v any, i int) any {
+ switch v := v.(*AutoLink); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[20].Exporter = func(v any, i int) any {
+ switch v := v.(*RawHTML); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[21].Exporter = func(v any, i int) any {
+ switch v := v.(*Table); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[22].Exporter = func(v any, i int) any {
+ switch v := v.(*TableRow); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[23].Exporter = func(v any, i int) any {
+ switch v := v.(*TableCell); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[24].Exporter = func(v any, i int) any {
+ switch v := v.(*TaskCheckbox); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[25].Exporter = func(v any, i int) any {
+ switch v := v.(*Strikethrough); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[26].Exporter = func(v any, i int) any {
+ switch v := v.(*CustomNode); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[27].Exporter = func(v any, i int) any {
+ switch v := v.(*Metadata); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[28].Exporter = func(v any, i int) any {
+ switch v := v.(*FabricContentNode); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ file_ast_v1_ast_proto_msgTypes[0].OneofWrappers = []any{
+ (*Attribute_Bytes)(nil),
+ (*Attribute_Str)(nil),
+ }
+ file_ast_v1_ast_proto_msgTypes[2].OneofWrappers = []any{
+ (*Node_Document)(nil),
+ (*Node_TextBlock)(nil),
+ (*Node_Paragraph)(nil),
+ (*Node_Heading)(nil),
+ (*Node_ThematicBreak)(nil),
+ (*Node_CodeBlock)(nil),
+ (*Node_FencedCodeBlock)(nil),
+ (*Node_Blockquote)(nil),
+ (*Node_List)(nil),
+ (*Node_ListItem)(nil),
+ (*Node_HtmlBlock)(nil),
+ (*Node_Text)(nil),
+ (*Node_String_)(nil),
+ (*Node_CodeSpan)(nil),
+ (*Node_Emphasis)(nil),
+ (*Node_LinkOrImage)(nil),
+ (*Node_AutoLink)(nil),
+ (*Node_RawHtml)(nil),
+ (*Node_Table)(nil),
+ (*Node_TableRow)(nil),
+ (*Node_TableCell)(nil),
+ (*Node_TaskCheckbox)(nil),
+ (*Node_Strikethrough)(nil),
+ (*Node_ContentNode)(nil),
+ (*Node_Custom)(nil),
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_ast_v1_ast_proto_rawDesc,
+ NumEnums: 3,
+ NumMessages: 29,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_ast_v1_ast_proto_goTypes,
+ DependencyIndexes: file_ast_v1_ast_proto_depIdxs,
+ EnumInfos: file_ast_v1_ast_proto_enumTypes,
+ MessageInfos: file_ast_v1_ast_proto_msgTypes,
+ }.Build()
+ File_ast_v1_ast_proto = out.File
+ file_ast_v1_ast_proto_rawDesc = nil
+ file_ast_v1_ast_proto_goTypes = nil
+ file_ast_v1_ast_proto_depIdxs = nil
+}
diff --git a/plugin/ast/v1/ast_decoder.go b/plugin/ast/v1/ast_decoder.go
new file mode 100644
index 00000000..e0142f99
--- /dev/null
+++ b/plugin/ast/v1/ast_decoder.go
@@ -0,0 +1,263 @@
+package astv1
+
+import (
+ "fmt"
+ "math"
+
+ "github.com/yuin/goldmark/ast"
+ east "github.com/yuin/goldmark/extension/ast"
+ "github.com/yuin/goldmark/text"
+
+ "github.com/blackstork-io/fabric/plugin/ast/astsrc"
+ "github.com/blackstork-io/fabric/plugin/ast/nodes"
+)
+
+type DecoderOption interface {
+ apply(dec *decoder)
+}
+
+type (
+ AttributeDecoder func(*Attribute) (ast.Attribute, error)
+ NodeDecoder func(*Node) (ast.Node, error)
+)
+
+func (f AttributeDecoder) apply(dec *decoder) {
+ if f != nil {
+ dec.attributeDecoder = f
+ }
+}
+
+func (f NodeDecoder) apply(dec *decoder) {
+ if f != nil {
+ dec.nodeDecoder = f
+ }
+}
+
+func defaultNodeDecoder(node *Node) (ast.Node, error) {
+ return nil, fmt.Errorf("%w: %T", ErrUnsupportedNodeType, node)
+}
+
+func Decode(root *Node, opts ...DecoderOption) (node ast.Node, source astsrc.ASTSource, err error) {
+ defer func() {
+ err = recoverBubbleError(err, recover())
+ }()
+ dec := &decoder{
+ attributeDecoder: DefaultAttributeDecoder,
+ nodeDecoder: defaultNodeDecoder,
+ }
+ for _, opt := range opts {
+ opt.apply(dec)
+ }
+
+ node = dec.decodeNode(root)
+ source = dec.source
+ return
+}
+
+type decoder struct {
+ source astsrc.ASTSource
+ attributeDecoder AttributeDecoder
+ nodeDecoder NodeDecoder
+}
+
+func DefaultAttributeDecoder(attr *Attribute) (res ast.Attribute, err error) {
+ switch val := attr.GetValue().(type) {
+ case nil:
+ case *Attribute_Bytes:
+ res.Value = val.Bytes
+ case *Attribute_Str:
+ res.Value = val.Str
+ default:
+ err = fmt.Errorf("%w: %T", ErrUnsupportedAttributeType, val)
+ return
+ }
+ res.Name = attr.GetName()
+ return
+}
+
+func (d *decoder) decodeBaseNode(base *BaseNode, node ast.Node) {
+ if base == nil {
+ return
+ }
+ for _, encAttr := range base.GetAttributes() {
+ attr, err := d.attributeDecoder(encAttr)
+ if err != nil {
+ bubbleUp(err)
+ }
+ node.SetAttribute(attr.Name, attr.Value)
+ }
+
+ for _, encChild := range base.GetChildren() {
+ node.AppendChild(node, d.decodeNode(encChild))
+ }
+}
+
+func (d *decoder) decodeText(txt *Text) *ast.Text {
+ if txt == nil {
+ return nil
+ }
+ res := ast.NewText()
+ res.Segment = d.source.Append(txt.GetSegment())
+ res.SetSoftLineBreak(txt.GetSoftLineBreak())
+ res.SetHardLineBreak(txt.GetHardLineBreak())
+ res.SetRaw(txt.GetRaw())
+ return res
+}
+
+func (d *decoder) decodeNode(node *Node) (res ast.Node) {
+ var base *BaseNode
+ switch val := node.GetKind().(type) {
+ case *Node_Document:
+ base = val.Document.GetBase()
+ res = ast.NewDocument()
+ res.SetBlankPreviousLines(true)
+ case *Node_TextBlock:
+ base = val.TextBlock.GetBase()
+ res = ast.NewTextBlock()
+ case *Node_Paragraph:
+ base = val.Paragraph.GetBase()
+ res = ast.NewParagraph()
+ case *Node_Heading:
+ base = val.Heading.GetBase()
+ res = ast.NewHeading(int(val.Heading.GetLevel()))
+ case *Node_ThematicBreak:
+ base = val.ThematicBreak.GetBase()
+ res = ast.NewThematicBreak()
+ case *Node_CodeBlock:
+ base = val.CodeBlock.GetBase()
+ codeBlock := ast.NewCodeBlock()
+ codeBlock.SetLines(d.source.AppendMultiple(val.CodeBlock.GetLines()))
+ res = codeBlock
+ case *Node_FencedCodeBlock:
+ base = val.FencedCodeBlock.GetBase()
+ fencedCodeBlock := ast.NewFencedCodeBlock(d.decodeText(val.FencedCodeBlock.GetInfo()))
+ fencedCodeBlock.SetLines(d.source.AppendMultiple(val.FencedCodeBlock.GetLines()))
+ res = fencedCodeBlock
+ case *Node_Blockquote:
+ base = val.Blockquote.GetBase()
+ res = ast.NewBlockquote()
+ case *Node_List:
+ base = val.List.GetBase()
+ marker := val.List.GetMarker()
+ if marker == 0 || marker > math.MaxUint8 {
+ bubbleUp(fmt.Errorf("invalid marker character code: %d", marker))
+ }
+ list := ast.NewList(byte(marker))
+ list.IsTight = val.List.GetIsTight()
+ list.Start = int(val.List.GetStart())
+ res = list
+ case *Node_ListItem:
+ base = val.ListItem.GetBase()
+ res = ast.NewListItem(int(val.ListItem.GetOffset()))
+ case *Node_HtmlBlock:
+ base = val.HtmlBlock.GetBase()
+ htmlBlock := ast.NewHTMLBlock(val.HtmlBlock.GetType().decode())
+ if closure := val.HtmlBlock.GetClosureLine(); closure != nil {
+ htmlBlock.ClosureLine = d.source.Append(val.HtmlBlock.GetClosureLine())
+ } else {
+ htmlBlock.ClosureLine = text.NewSegment(-1, -1)
+ }
+ htmlBlock.SetLines(d.source.AppendMultiple(val.HtmlBlock.GetLines()))
+ res = htmlBlock
+ case *Node_Text:
+ base = val.Text.GetBase()
+ res = d.decodeText(val.Text)
+ case *Node_String_:
+ base = val.String_.GetBase()
+ str := ast.NewString(val.String_.GetValue())
+ str.SetRaw(val.String_.GetRaw())
+ str.SetCode(val.String_.GetCode())
+ res = str
+ case *Node_CodeSpan:
+ base = val.CodeSpan.GetBase()
+ res = ast.NewCodeSpan()
+ case *Node_Emphasis:
+ base = val.Emphasis.GetBase()
+ res = ast.NewEmphasis(int(val.Emphasis.GetLevel()))
+ case *Node_LinkOrImage:
+ base = val.LinkOrImage.GetBase()
+ tRes := ast.NewLink()
+ tRes.Title = val.LinkOrImage.GetTitle()
+ tRes.Destination = val.LinkOrImage.GetDestination()
+ if val.LinkOrImage.GetIsImage() {
+ res = ast.NewImage(tRes)
+ } else {
+ res = tRes
+ }
+ case *Node_AutoLink:
+ base = val.AutoLink.GetBase()
+ tRes := ast.NewAutoLink(val.AutoLink.GetType().decode(), d.decodeText(&Text{
+ Segment: val.AutoLink.GetValue(),
+ }))
+ tRes.Protocol = val.AutoLink.GetProtocol()
+ res = tRes
+
+ case *Node_RawHtml:
+ base = val.RawHtml.GetBase()
+ tRes := ast.NewRawHTML()
+ tRes.Segments = d.source.AppendMultiple(val.RawHtml.GetSegments())
+ res = tRes
+
+ case *Node_Table:
+ base = val.Table.GetBase()
+ tRes := east.NewTable()
+ tRes.Alignments = decodeCellAlignments(val.Table.GetAlignments())
+ res = tRes
+
+ case *Node_TableRow:
+ base = val.TableRow.GetBase()
+ tRes := east.NewTableRow(decodeCellAlignments(val.TableRow.GetAlignments()))
+ if val.TableRow.GetIsHeader() {
+ res = east.NewTableHeader(tRes)
+ } else {
+ res = tRes
+ }
+ case *Node_TableCell:
+ base = val.TableCell.GetBase()
+ tRes := east.NewTableCell()
+ tRes.Alignment = val.TableCell.GetAlignment().decode()
+ res = tRes
+
+ case *Node_Strikethrough:
+ base = val.Strikethrough.GetBase()
+ res = east.NewStrikethrough()
+
+ case *Node_TaskCheckbox:
+ base = val.TaskCheckbox.GetBase()
+ res = east.NewTaskCheckBox(val.TaskCheckbox.GetIsChecked())
+
+ case *Node_ContentNode:
+ res = &nodes.FabricContentNode{
+ Meta: DecodeMetadata(val.ContentNode.GetMetadata()),
+ }
+ base = val.ContentNode.GetRoot()
+ case *Node_Custom:
+ res = nodes.NewCustomNode(val.Custom.GetIsInline(), val.Custom.GetData())
+ base = &BaseNode{
+ BlankPreviousLines: val.Custom.GetBlankPreviousLines(),
+ }
+ default:
+ bubbleUp(fmt.Errorf("Unsupported block kind: %T", node.GetKind()))
+ }
+ switch res.Type() {
+ case ast.TypeBlock:
+ res.SetBlankPreviousLines(base.GetBlankPreviousLines())
+ fallthrough
+ case ast.TypeInline, ast.TypeDocument:
+ d.decodeBaseNode(base, res)
+ default:
+ bubbleUp(fmt.Errorf("Unsupported block type: %+v", res.Type()))
+ }
+ return
+}
+
+func DecodeMetadata(meta *Metadata) *nodes.ContentMeta {
+ if meta == nil {
+ return nil
+ }
+ return &nodes.ContentMeta{
+ Provider: meta.GetProvider(),
+ Plugin: meta.GetPlugin(),
+ Version: meta.GetVersion(),
+ }
+}
diff --git a/plugin/ast/v1/ast_encoder.go b/plugin/ast/v1/ast_encoder.go
new file mode 100644
index 00000000..e14cf02e
--- /dev/null
+++ b/plugin/ast/v1/ast_encoder.go
@@ -0,0 +1,390 @@
+package astv1
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/yuin/goldmark/ast"
+ east "github.com/yuin/goldmark/extension/ast"
+ "github.com/yuin/goldmark/text"
+
+ "github.com/blackstork-io/fabric/plugin/ast/nodes"
+)
+
+// ErrSkipAttribute should be returned from AttributeEncoder if the
+// attribute needs to be be skipped.
+var ErrSkipAttribute = errors.New("skip")
+
+var (
+ ErrUnsupportedNodeType = errors.New("unsupported node type")
+ ErrUnsupportedAttributeType = errors.New("unsupported attribute type")
+ ErrUnsupportedAlignment = errors.New("unsupported alignment")
+)
+
+type (
+ AttributeEncoder func(*ast.Attribute) (*Attribute, error)
+ NodeEncoder func(ast.Node) (isNode_Kind, error)
+)
+
+func (f AttributeEncoder) apply(enc *encoder) {
+ if f != nil {
+ enc.attributeEncoder = f
+ }
+}
+
+func (f NodeEncoder) apply(enc *encoder) {
+ if f != nil {
+ enc.nodeEncoder = f
+ }
+}
+
+type EncoderOption interface {
+ apply(enc *encoder)
+}
+
+type encoder struct {
+ attributeEncoder AttributeEncoder
+ nodeEncoder NodeEncoder
+ source []byte
+}
+
+func defaultNodeEncoder(node ast.Node) (isNode_Kind, error) {
+ return nil, fmt.Errorf("%w: %s", ErrUnsupportedNodeType, node.Kind())
+}
+
+func Encode(root ast.Node, source []byte, opts ...EncoderOption) (node *Node, err error) {
+ defer func() {
+ err = recoverBubbleError(err, recover())
+ }()
+ enc := &encoder{
+ attributeEncoder: DefaultAttributeEncoder,
+ source: source,
+ nodeEncoder: defaultNodeEncoder,
+ }
+ for _, opt := range opts {
+ opt.apply(enc)
+ }
+ node = enc.encodeNode(root)
+ return
+}
+
+func (e *encoder) encodeSegment(seg text.Segment) []byte {
+ if seg.Start > seg.Stop || seg.Start < 0 || seg.Stop > len(e.source) {
+ // Invalid segment.
+ return nil
+ }
+ // Note: zero-length segments (seg.Start == seg.Stop) are valid,
+ // and even could be meaningful if padding is > 0.
+ // Render them using seg.Value.
+ return seg.Value(e.source)
+}
+
+func (e *encoder) encodeBaseNode(node *ast.BaseNode) *BaseNode {
+ var res BaseNode
+ attrs := node.Attributes()
+ res.Attributes = make([]*Attribute, 0, len(attrs))
+ for _, attr := range attrs {
+ encoded, err := e.attributeEncoder(&attr)
+ if err != nil {
+ bubbleUp(err)
+ }
+ res.Attributes = append(res.Attributes, encoded)
+ }
+ res.Children = make([]*Node, 0, node.ChildCount())
+ for child := node.FirstChild(); child != nil; child = child.NextSibling() {
+ res.Children = append(res.Children, e.encodeNode(child))
+ }
+ return &res
+}
+
+func (e *encoder) encodeBaseBlock(node *ast.BaseBlock) (res *BaseNode) {
+ res = e.encodeBaseNode(&node.BaseNode)
+ res.BlankPreviousLines = node.HasBlankPreviousLines()
+ return
+}
+
+func (e *encoder) encodeNode(node ast.Node) *Node {
+ var kind isNode_Kind
+
+ switch n := node.(type) {
+ case *ast.Document:
+ kind = &Node_Document{
+ Document: &Document{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ },
+ }
+ case *ast.TextBlock:
+ kind = &Node_TextBlock{
+ TextBlock: &TextBlock{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ },
+ }
+ case *ast.Paragraph:
+ kind = &Node_Paragraph{
+ Paragraph: &Paragraph{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ },
+ }
+ case *ast.Heading:
+ kind = &Node_Heading{
+ Heading: &Heading{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Level: uint32(n.Level), //nolint:gosec // Level is bounded by 1-6.
+ },
+ }
+ case *ast.ThematicBreak:
+ kind = &Node_ThematicBreak{
+ ThematicBreak: &ThematicBreak{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ },
+ }
+ case *ast.CodeBlock:
+ kind = &Node_CodeBlock{
+ CodeBlock: &CodeBlock{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Lines: e.encodeSegments(n.Lines()),
+ },
+ }
+ case *ast.FencedCodeBlock:
+ var txt *Text
+ if n.Info != nil {
+ txt = &Text{
+ Segment: e.encodeSegment(n.Info.Segment),
+ Raw: n.Info.IsRaw(),
+ }
+ }
+ kind = &Node_FencedCodeBlock{
+ FencedCodeBlock: &FencedCodeBlock{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Info: txt,
+ Lines: e.encodeSegments(n.Lines()),
+ },
+ }
+ case *ast.Blockquote:
+ kind = &Node_Blockquote{
+ Blockquote: &Blockquote{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ },
+ }
+ case *ast.List:
+ kind = &Node_List{
+ List: &List{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Marker: uint32(n.Marker),
+ IsTight: n.IsTight,
+ Start: uint32(n.Start), //nolint:gosec // Start numbers must be nine digits or less (https://spec.commonmark.org/0.31.2/#example-265)
+ },
+ }
+ case *ast.ListItem:
+ kind = &Node_ListItem{
+ ListItem: &ListItem{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Offset: int64(n.Offset),
+ },
+ }
+ case *ast.HTMLBlock:
+ // Doing encoding manually to prevent a change in the internal representation
+ // of HTMLBlockType enum from breaking our encoding.
+ kind = &Node_HtmlBlock{
+ HtmlBlock: &HTMLBlock{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Type: encodeHTMLBlockType(n.HTMLBlockType),
+ Lines: e.encodeSegments(n.Lines()),
+ ClosureLine: e.encodeSegment(n.ClosureLine),
+ },
+ }
+ case *ast.Text:
+ kind = &Node_Text{
+ Text: &Text{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Segment: e.encodeSegment(n.Segment),
+ SoftLineBreak: n.SoftLineBreak(),
+ HardLineBreak: n.HardLineBreak(),
+ Raw: n.IsRaw(),
+ },
+ }
+ case *ast.String:
+ kind = &Node_String_{
+ String_: &String{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Value: n.Value,
+ Raw: n.IsRaw(),
+ Code: n.IsCode(),
+ },
+ }
+ case *ast.CodeSpan:
+ kind = &Node_CodeSpan{
+ CodeSpan: &CodeSpan{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ },
+ }
+ case *ast.Emphasis:
+ kind = &Node_Emphasis{
+ Emphasis: &Emphasis{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Level: int64(n.Level),
+ },
+ }
+ case *ast.Link:
+ kind = &Node_LinkOrImage{
+ LinkOrImage: &LinkOrImage{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Destination: n.Destination,
+ Title: n.Title,
+ IsImage: false,
+ },
+ }
+ case *ast.Image:
+ kind = &Node_LinkOrImage{
+ LinkOrImage: &LinkOrImage{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Destination: n.Destination,
+ Title: n.Title,
+ IsImage: true,
+ },
+ }
+ case *ast.AutoLink:
+ kind = &Node_AutoLink{
+ AutoLink: &AutoLink{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Type: encodeAutoLinkType(n.AutoLinkType),
+ Protocol: n.Protocol,
+ Value: n.Label(e.source),
+ },
+ }
+ case *ast.RawHTML:
+ kind = &Node_RawHtml{
+ RawHtml: &RawHTML{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ Segments: e.encodeSegments(n.Segments),
+ },
+ }
+ // GitHub Flavored Markdown
+ case *east.Table:
+ kind = &Node_Table{
+ Table: &Table{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Alignments: encodeCellAlignments(n.Alignments),
+ },
+ }
+
+ case *east.TableRow:
+ kind = &Node_TableRow{
+ TableRow: &TableRow{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Alignments: encodeCellAlignments(n.Alignments),
+ IsHeader: false,
+ },
+ }
+ case *east.TableHeader:
+ kind = &Node_TableRow{
+ TableRow: &TableRow{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Alignments: encodeCellAlignments(n.Alignments),
+ IsHeader: true,
+ },
+ }
+ case *east.TableCell:
+ kind = &Node_TableCell{
+ TableCell: &TableCell{
+ Base: e.encodeBaseBlock(&n.BaseBlock),
+ Alignment: encodeCellAlignment(n.Alignment),
+ },
+ }
+ case *east.Strikethrough:
+ kind = &Node_Strikethrough{
+ Strikethrough: &Strikethrough{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ },
+ }
+ case *east.TaskCheckBox:
+ kind = &Node_TaskCheckbox{
+ TaskCheckbox: &TaskCheckbox{
+ Base: e.encodeBaseNode(&n.BaseNode),
+ IsChecked: n.IsChecked,
+ },
+ }
+ case *nodes.FabricContentNode:
+ kind = &Node_ContentNode{
+ ContentNode: &FabricContentNode{
+ Metadata: EncodeMetadata(n.Meta),
+ Root: e.encodeBaseBlock(&n.BaseBlock),
+ },
+ }
+ case *nodes.CustomBlock:
+ kind = &Node_Custom{
+ Custom: &CustomNode{
+ IsInline: false,
+ Data: n.Data,
+ },
+ }
+ case *nodes.CustomInline:
+ kind = &Node_Custom{
+ Custom: &CustomNode{
+ IsInline: true,
+ Data: n.Data,
+ },
+ }
+ default:
+ var err error
+ kind, err = e.nodeEncoder(node)
+ if err != nil {
+ bubbleUp(err)
+ }
+ }
+
+ return &Node{
+ Kind: kind,
+ }
+}
+
+func (e *encoder) encodeSegments(seg *text.Segments) [][]byte {
+ res := make([][]byte, seg.Len())
+ for i := range res {
+ res[i] = e.encodeSegment(seg.At(i))
+ }
+ return res
+}
+
+func DefaultAttributeEncoder(attr *ast.Attribute) (*Attribute, error) {
+ var res isAttribute_Value
+ switch val := attr.Value.(type) {
+ case []byte:
+ res = &Attribute_Bytes{
+ Bytes: val,
+ }
+ case string:
+ res = &Attribute_Str{
+ Str: val,
+ }
+ case uint, uint8, uint16, uint32, uintptr, uint64, int, int8, int16, int32, int64:
+ res = &Attribute_Str{
+ Str: fmt.Sprintf("%d", val),
+ }
+ case bool:
+ res = &Attribute_Bytes{
+ Bytes: attr.Name,
+ }
+ case float32, float64:
+ res = &Attribute_Str{
+ Str: fmt.Sprintf("%f", val),
+ }
+ default:
+ return nil, fmt.Errorf("%w: %T", ErrUnsupportedAttributeType, attr.Value)
+ }
+ return &Attribute{
+ Name: attr.Name,
+ Value: res,
+ }, nil
+}
+
+func EncodeMetadata(meta *nodes.ContentMeta) *Metadata {
+ if meta == nil {
+ return nil
+ }
+ return &Metadata{
+ Provider: meta.Provider,
+ Plugin: meta.Plugin,
+ Version: meta.Version,
+ }
+}
diff --git a/plugin/ast/v1/ast_test.go b/plugin/ast/v1/ast_test.go
new file mode 100644
index 00000000..13342940
--- /dev/null
+++ b/plugin/ast/v1/ast_test.go
@@ -0,0 +1,98 @@
+package astv1_test
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/blackstork-io/goldmark-markdown/pkg/mdexamples"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/extension"
+ "github.com/yuin/goldmark/text"
+
+ astv1 "github.com/blackstork-io/fabric/plugin/ast/v1"
+)
+
+func roundtrip(t *testing.T, source []byte) {
+ t.Helper()
+ md := goldmark.New(
+ goldmark.WithExtensions(
+ extension.Table,
+ extension.Strikethrough,
+ extension.TaskList,
+ ),
+ )
+
+ tree := md.Parser().Parse(text.NewReader(source))
+
+ // roundtrip
+ encTree, err := astv1.Encode(tree, source)
+ if err != nil {
+ t.Fatalf("encode: %v", err)
+ }
+ decTree, decSrc, err := astv1.Decode(encTree)
+ if err != nil {
+ t.Fatalf("decode: %v", err)
+ }
+
+ // assume that trees are identical if their html render is identical
+ // can't use equality checks because the segments hold no reference to *which*
+ // source they are referring to
+ var bufOrig, bufRoundtrip bytes.Buffer
+
+ err = md.Renderer().Render(&bufOrig, source, tree)
+ if err != nil {
+ t.Fatalf("render original: %v", err)
+ }
+
+ err = md.Renderer().Render(&bufRoundtrip, decSrc, decTree)
+ if err != nil {
+ t.Fatalf("render decoded: %v", err)
+ }
+
+ if !bytes.Equal(bufOrig.Bytes(), bufRoundtrip.Bytes()) {
+ t.Errorf("rendered html differs")
+ t.Logf("---------- original -----------\n%s\n\n", bufOrig.String())
+ t.Logf("---------- roundtrip ----------\n%s\n\n", bufRoundtrip.String())
+ }
+}
+
+func TestSpecExamplesRoundtrip(t *testing.T) {
+ for _, exFile := range mdexamples.ReadAllSpecExamples() {
+ for _, ex := range exFile.Examples {
+ t.Run(exFile.Name+":"+ex.Link, func(t *testing.T) {
+ t.Parallel()
+ // Skipped tests are ok to use, we're only interested in the roundtrip
+ roundtrip(t, ex.Markdown)
+ })
+ }
+ }
+}
+
+func TestDocumentsRoundtrip(t *testing.T) {
+ for _, ex := range mdexamples.ReadAllDocumentExamples() {
+ t.Run(ex.Name, func(t *testing.T) {
+ t.Parallel()
+ roundtrip(t, ex.Data)
+ })
+ }
+}
+
+func TestFuzzCase(t *testing.T) {
+ t.Skip("Expected to fail: bugs in goldmark")
+ roundtrip(t, []byte("* 0\n-|\n\t0"))
+ roundtrip(t, []byte("> ```\n>\t0"))
+}
+
+func FuzzEncoder(f *testing.F) {
+ // for _, exFile := range test.ReadAllSpecExamples() {
+ // for _, ex := range exFile.Examples {
+ // f.Add(ex.Markdown)
+ // }
+ // }
+ for _, ex := range mdexamples.ReadAllDocumentExamples() {
+ f.Add(ex.Data)
+ }
+ f.Fuzz(func(t *testing.T, data []byte) {
+ roundtrip(t, data)
+ })
+}
diff --git a/plugin/ast/v1/bubble_error.go b/plugin/ast/v1/bubble_error.go
new file mode 100644
index 00000000..4a54b24e
--- /dev/null
+++ b/plugin/ast/v1/bubble_error.go
@@ -0,0 +1,43 @@
+package astv1
+
+import (
+ "errors"
+)
+
+// bubbleError is a wrapper for errors that are meant to be panic'ed
+// and caught by the higher level caller.
+// This is considered to be a bad practice, but return-error propagation
+// is not practical in deeply nested encoding/decoding functions.
+// Standard library uses this pattern in some places, i.e. encoding/json.
+type bubbleError struct {
+ err error
+}
+
+// bubbleWrap wraps an error in a bubbleError and returns it (not panics).
+func bubbleWrap(err error) bubbleError {
+ return bubbleError{err: err}
+}
+
+// bubbleUp wraps an error in a bubbleError and panics it.
+func bubbleUp(err error) {
+ panic(bubbleWrap(err))
+}
+
+// recoverBubbleError expects to be called in a defer statement with
+// the current error and a recover(). If bubbleError is recovered, it
+// would be [errors.Join]'ed with the current error (if non nil) and
+// returned.
+// If there was no panic, the passed in error would be returned.
+// If there is any other panic, it would be re-panicked.
+func recoverBubbleError(err error, recovered any) error {
+ if recovered == nil {
+ return err
+ }
+ if pErr, ok := recovered.(bubbleError); ok {
+ if err != nil {
+ return errors.Join(err, pErr.err)
+ }
+ return pErr.err
+ }
+ panic(recovered)
+}
diff --git a/plugin/ast/v1/enum_codec.go b/plugin/ast/v1/enum_codec.go
new file mode 100644
index 00000000..e8dd3e21
--- /dev/null
+++ b/plugin/ast/v1/enum_codec.go
@@ -0,0 +1,124 @@
+package astv1
+
+import (
+ "fmt"
+
+ "github.com/yuin/goldmark/ast"
+ east "github.com/yuin/goldmark/extension/ast"
+)
+
+func encodeHTMLBlockType(ty ast.HTMLBlockType) HTMLBlockType {
+ switch ty {
+ case ast.HTMLBlockType1:
+ return HTMLBlockType_HTML_BLOCK_TYPE_1
+ case ast.HTMLBlockType2:
+ return HTMLBlockType_HTML_BLOCK_TYPE_2
+ case ast.HTMLBlockType3:
+ return HTMLBlockType_HTML_BLOCK_TYPE_3
+ case ast.HTMLBlockType4:
+ return HTMLBlockType_HTML_BLOCK_TYPE_4
+ case ast.HTMLBlockType5:
+ return HTMLBlockType_HTML_BLOCK_TYPE_5
+ case ast.HTMLBlockType6:
+ return HTMLBlockType_HTML_BLOCK_TYPE_6
+ case ast.HTMLBlockType7:
+ return HTMLBlockType_HTML_BLOCK_TYPE_7
+ default:
+ panic(bubbleWrap(fmt.Errorf("unsupported HTML block type: %v", ty)))
+ }
+}
+
+func (bt HTMLBlockType) decode() ast.HTMLBlockType {
+ switch bt {
+ case HTMLBlockType_HTML_BLOCK_TYPE_1:
+ return ast.HTMLBlockType1
+ case HTMLBlockType_HTML_BLOCK_TYPE_2:
+ return ast.HTMLBlockType2
+ case HTMLBlockType_HTML_BLOCK_TYPE_3:
+ return ast.HTMLBlockType3
+ case HTMLBlockType_HTML_BLOCK_TYPE_4:
+ return ast.HTMLBlockType4
+ case HTMLBlockType_HTML_BLOCK_TYPE_5:
+ return ast.HTMLBlockType5
+ case HTMLBlockType_HTML_BLOCK_TYPE_6:
+ return ast.HTMLBlockType6
+ case HTMLBlockType_HTML_BLOCK_TYPE_7:
+ return ast.HTMLBlockType7
+ case HTMLBlockType_HTML_BLOCK_TYPE_UNSPECIFIED:
+ fallthrough
+ default:
+ panic(bubbleWrap(fmt.Errorf("unsupported HTML block type: %v", bt)))
+ }
+}
+
+func encodeAutoLinkType(ty ast.AutoLinkType) AutoLinkType {
+ switch ty {
+ case ast.AutoLinkURL:
+ return AutoLinkType_AUTO_LINK_TYPE_URL
+ case ast.AutoLinkEmail:
+ return AutoLinkType_AUTO_LINK_TYPE_EMAIL
+ default:
+ panic(bubbleWrap(fmt.Errorf("unsupported auto link type: %v", ty)))
+ }
+}
+
+func (ty AutoLinkType) decode() ast.AutoLinkType {
+ switch ty {
+ case AutoLinkType_AUTO_LINK_TYPE_URL:
+ return ast.AutoLinkURL
+ case AutoLinkType_AUTO_LINK_TYPE_EMAIL:
+ return ast.AutoLinkEmail
+ case AutoLinkType_AUTO_LINK_TYPE_UNSPECIFIED:
+ fallthrough
+ default:
+ panic(bubbleWrap(fmt.Errorf("unsupported auto link type: %v", ty)))
+ }
+}
+
+func encodeCellAlignment(alignment east.Alignment) CellAlignment {
+ switch alignment {
+ case east.AlignLeft:
+ return CellAlignment_CELL_ALIGNMENT_LEFT
+ case east.AlignRight:
+ return CellAlignment_CELL_ALIGNMENT_RIGHT
+ case east.AlignCenter:
+ return CellAlignment_CELL_ALIGNMENT_CENTER
+ case east.AlignNone:
+ return CellAlignment_CELL_ALIGNMENT_NONE
+ default:
+ panic(bubbleWrap(fmt.Errorf("unsupported cell alignment: %v", alignment)))
+ }
+}
+
+func (ca CellAlignment) decode() east.Alignment {
+ switch ca {
+ case CellAlignment_CELL_ALIGNMENT_NONE:
+ return east.AlignNone
+ case CellAlignment_CELL_ALIGNMENT_LEFT:
+ return east.AlignLeft
+ case CellAlignment_CELL_ALIGNMENT_RIGHT:
+ return east.AlignRight
+ case CellAlignment_CELL_ALIGNMENT_CENTER:
+ return east.AlignCenter
+ case CellAlignment_CELL_ALIGNMENT_UNSPECIFIED:
+ fallthrough
+ default:
+ panic(bubbleWrap(fmt.Errorf("unsupported cell alignment: %v", ca)))
+ }
+}
+
+func encodeCellAlignments(align []east.Alignment) []CellAlignment {
+ res := make([]CellAlignment, len(align))
+ for i, a := range align {
+ res[i] = encodeCellAlignment(a)
+ }
+ return res
+}
+
+func decodeCellAlignments(align []CellAlignment) []east.Alignment {
+ res := make([]east.Alignment, len(align))
+ for i, a := range align {
+ res[i] = a.decode()
+ }
+ return res
+}
diff --git a/plugin/ast/v1/interfaces.go b/plugin/ast/v1/interfaces.go
new file mode 100644
index 00000000..c9a27128
--- /dev/null
+++ b/plugin/ast/v1/interfaces.go
@@ -0,0 +1,319 @@
+package astv1
+
+import "slices"
+
+type Content interface {
+ ExtendNodes(nodes []*Node) []*Node
+}
+
+type Contents []Content
+
+func (c Contents) ExtendNodes(nodes []*Node) []*Node {
+ for _, content := range c {
+ nodes = content.ExtendNodes(nodes)
+ }
+ return nodes
+}
+
+// InlineContent represents any inline node or something convertible to inline nodes.
+type InlineContent interface {
+ Content
+ isInline()
+}
+
+// Inlines is a list of InlineContent.
+type Inlines []InlineContent
+
+func (n Inlines) isInline() {}
+func (n Inlines) ExtendNodes(nodes []*Node) []*Node {
+ for _, inlines := range n {
+ nodes = inlines.ExtendNodes(nodes)
+ }
+ return nodes
+}
+
+func (n *Node_CodeSpan) isInline() {}
+func (n *Node_CodeSpan) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Emphasis) isInline() {}
+func (n *Node_Emphasis) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_LinkOrImage) isInline() {}
+func (n *Node_LinkOrImage) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_LinkOrImage) SetDestination(url string) *Node_LinkOrImage {
+ n.LinkOrImage.Destination = []byte(url)
+ return n
+}
+
+func (n *Node_LinkOrImage) SetTitle(title string) *Node_LinkOrImage {
+ n.LinkOrImage.Title = []byte(title)
+ return n
+}
+
+func (n *Node_AutoLink) isInline() {}
+func (n *Node_AutoLink) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_RawHtml) isInline() {}
+func (n *Node_RawHtml) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Text) isInline() {}
+func (n *Node_Text) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_String_) isInline() {}
+func (n *Node_String_) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Strikethrough) isInline() {}
+func (n *Node_Strikethrough) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+// checkbox is inline content, but it can be used only in a list item.
+
+// BlockContent represents any block node or something convertible to block nodes.
+type BlockContent interface {
+ Content
+ isBlock()
+}
+type Blocks []BlockContent
+
+func (b Blocks) isBlock() {}
+func (b Blocks) ExtendNodes(nodes []*Node) []*Node {
+ for _, inlines := range b {
+ nodes = inlines.ExtendNodes(nodes)
+ }
+ return nodes
+}
+
+func (n *Node_Blockquote) isBlock() {}
+func (n *Node_Blockquote) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_List) isBlock() {}
+func (n *Node_List) ExtendNodes(nodes []*Node) []*Node {
+ if n == nil || n.List == nil || len(n.List.GetBase().GetChildren()) == 0 {
+ return nodes
+ }
+ return append(nodes, &Node{
+ Kind: n,
+ })
+}
+
+// SetStart sets the start number of the list (only works on ordered lists, "." and ")" markers).
+func (n *Node_List) SetStart(start uint32) *Node_List {
+ switch n.List.GetMarker() {
+ case '.', ')':
+ n.List.Start = start
+ }
+ return n
+}
+
+func prependCheckbox(checked bool, content []BlockContent) (itemChildren []*Node) {
+ itemChildren = Blocks.ExtendNodes(content, nil)
+
+ checkbox := &Node{
+ Kind: &Node_TaskCheckbox{
+ TaskCheckbox: &TaskCheckbox{
+ IsChecked: checked,
+ },
+ },
+ }
+
+ if len(itemChildren) > 0 {
+ if text, ok := itemChildren[0].GetKind().(*Node_Paragraph); ok {
+ text.Paragraph.Base.Children = slices.Insert(text.Paragraph.GetBase().GetChildren(), 0, checkbox)
+ return
+ }
+ }
+ // Checkbox was not prepended to existing paragraph, create a new paragraph.
+ itemChildren = slices.Insert(itemChildren, 0, &Node{
+ Kind: &Node_Paragraph{
+ Paragraph: &Paragraph{
+ Base: &BaseNode{
+ Children: []*Node{checkbox},
+ },
+ },
+ },
+ })
+ return
+}
+
+func (n *Node_List) appendItem(children []*Node) {
+ n.List.Base.Children = append(n.List.Base.Children, &Node{
+ Kind: &Node_ListItem{
+ ListItem: &ListItem{
+ Base: &BaseNode{
+ Children: children,
+ },
+ },
+ },
+ })
+}
+
+// AppendItem appends a list item to the list.
+func (n *Node_List) AppendItem(content ...BlockContent) *Node_List {
+ n.appendItem(Blocks.ExtendNodes(content, nil))
+ return n
+}
+
+// AppendTaskItem appends a task list item to the list.
+func (n *Node_List) AppendTaskItem(checked bool, content ...BlockContent) *Node_List {
+ n.appendItem(prependCheckbox(checked, content))
+ return n
+}
+
+// Thematic breaks
+func (n *Node_ThematicBreak) isBlock() {}
+
+func (n *Node_ThematicBreak) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Heading) isBlock() {}
+
+func (n *Node_Heading) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_CodeBlock) isBlock() {}
+
+func (n *Node_CodeBlock) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_FencedCodeBlock) isBlock() {}
+
+func (n *Node_FencedCodeBlock) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_FencedCodeBlock) SetLanguage(language string) *Node_FencedCodeBlock {
+ n.FencedCodeBlock.Info = &Text{
+ Segment: []byte(language),
+ }
+ return n
+}
+
+func (n *Node_HtmlBlock) isBlock() {}
+func (n *Node_HtmlBlock) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Paragraph) isBlock() {}
+func (n *Node_Paragraph) ExtendNodes(nodes []*Node) []*Node {
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Table) isBlock() {}
+func (n *Node_Table) ExtendNodes(nodes []*Node) []*Node {
+ if n == nil {
+ return nodes
+ }
+ rows := n.Table.GetBase().GetChildren()
+ if len(rows) == 0 {
+ return nodes
+ }
+ headerCellCount := len(rows[0].GetTableRow().GetBase().GetChildren())
+ if headerCellCount == 0 {
+ // must have at least one table cell
+ return nodes
+ }
+ unspecifiedAlignments := headerCellCount - len(n.Table.Alignments)
+ if unspecifiedAlignments <= 0 {
+ // Trim the alignments to the number of header cells
+ n.Table.Alignments = n.Table.Alignments[:len(n.Table.Alignments)+unspecifiedAlignments]
+ } else {
+ // Extend the alignments to the number of header cells, filling missing alignments with NONE
+ n.Table.Alignments = slices.Grow(n.Table.Alignments, unspecifiedAlignments)
+ for range unspecifiedAlignments {
+ n.Table.Alignments = append(n.Table.Alignments, CellAlignment_CELL_ALIGNMENT_NONE)
+ }
+ }
+ for rowIdx, row := range rows {
+ tr := row.GetTableRow()
+ if tr == nil {
+ return nodes
+ }
+ tr.IsHeader = rowIdx == 0
+ cells := tr.GetBase().GetChildren()
+ tr.Alignments = n.Table.Alignments[:min(len(cells), len(n.Table.Alignments))]
+ for i, cell := range cells {
+ c := cell.GetTableCell()
+ if c == nil {
+ return nodes
+ }
+ if i < len(tr.GetAlignments()) {
+ c.Alignment = tr.GetAlignments()[i]
+ } else {
+ c.Alignment = CellAlignment_CELL_ALIGNMENT_NONE
+ }
+ }
+ }
+ return append(nodes, &Node{Kind: n})
+}
+
+func (n *Node_Table) SetColumnAlignments(alignments ...CellAlignment) *Node_Table {
+ n.Table.Alignments = alignments
+ return n
+}
+
+func (n *Node_Table) AppendRow(cells ...[]InlineContent) *Node_Table {
+ if len(cells) == 0 {
+ return n
+ }
+ cellNodes := make([]*Node, len(cells))
+ for i, cellContent := range cells {
+ if cellContent == nil {
+ continue
+ }
+ cellNodes[i] = &Node{
+ Kind: &Node_TableCell{
+ TableCell: &TableCell{
+ Base: &BaseNode{
+ Children: Inlines.ExtendNodes(cellContent, nil),
+ },
+ },
+ },
+ }
+ }
+
+ n.Table.Base.Children = append(n.Table.Base.Children, &Node{
+ Kind: &Node_TableRow{
+ TableRow: &TableRow{
+ Base: &BaseNode{
+ Children: cellNodes,
+ },
+ IsHeader: false,
+ },
+ },
+ })
+ return n
+}
+
+func NewContent(nodes ...BlockContent) *FabricContentNode {
+ var children []*Node
+ for _, node := range nodes {
+ children = node.ExtendNodes(children)
+ }
+ return &FabricContentNode{
+ Root: &BaseNode{
+ Children: children,
+ BlankPreviousLines: true,
+ },
+ }
+}
diff --git a/plugin/ast/v1/testdata/fuzz/FuzzEncoder/6b5e47038908d6af b/plugin/ast/v1/testdata/fuzz/FuzzEncoder/6b5e47038908d6af
new file mode 100644
index 00000000..918fda37
--- /dev/null
+++ b/plugin/ast/v1/testdata/fuzz/FuzzEncoder/6b5e47038908d6af
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte(" ```\n\t")
diff --git a/plugin/content.go b/plugin/content.go
index 2317db16..2e53e261 100644
--- a/plugin/content.go
+++ b/plugin/content.go
@@ -1,10 +1,21 @@
package plugin
import (
+ "bytes"
"fmt"
+ "log/slog"
"slices"
"sync"
+ markdown "github.com/blackstork-io/goldmark-markdown"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/extension"
+ "github.com/yuin/goldmark/text"
+ "google.golang.org/protobuf/proto"
+
+ "github.com/blackstork-io/fabric/plugin/ast/astsrc"
+ "github.com/blackstork-io/fabric/plugin/ast/nodes"
+ astv1 "github.com/blackstork-io/fabric/plugin/ast/v1"
"github.com/blackstork-io/fabric/plugin/plugindata"
)
@@ -36,27 +47,28 @@ type Location struct {
type ContentResult struct {
Location *Location
Content Content
+ // Content *FabricContentNode // switch to this
}
type Content interface {
setID(id uint32)
- setMeta(meta *ContentMeta)
+ setMeta(meta *nodes.ContentMeta)
AsData() plugindata.Data
ID() uint32
AsPluginData() plugindata.Data
- Meta() *ContentMeta
+ Meta() *nodes.ContentMeta
}
type ContentEmpty struct {
id uint32
- meta *ContentMeta
+ meta *nodes.ContentMeta
}
func (n *ContentEmpty) setID(id uint32) {
n.id = id
}
-func (n *ContentEmpty) setMeta(meta *ContentMeta) {
+func (n *ContentEmpty) setMeta(meta *nodes.ContentMeta) {
n.meta = meta
}
@@ -72,7 +84,7 @@ func (n *ContentEmpty) ID() uint32 {
return n.id
}
-func (n *ContentEmpty) Meta() *ContentMeta {
+func (n *ContentEmpty) Meta() *nodes.ContentMeta {
return n.meta
}
@@ -84,7 +96,7 @@ type ContentSection struct {
*idStore
id uint32
Children []Content
- meta *ContentMeta
+ meta *nodes.ContentMeta
}
func NewSection(contentID uint32) *ContentSection {
@@ -151,7 +163,7 @@ func (c *ContentSection) setID(id uint32) {
c.id = id
}
-func (c *ContentSection) setMeta(meta *ContentMeta) {
+func (c *ContentSection) setMeta(meta *nodes.ContentMeta) {
c.meta = meta
for _, child := range c.Children {
child.setMeta(meta)
@@ -162,7 +174,7 @@ func (c *ContentSection) ID() uint32 {
return c.id
}
-func (c *ContentSection) Meta() *ContentMeta {
+func (c *ContentSection) Meta() *nodes.ContentMeta {
return c.meta
}
@@ -201,9 +213,129 @@ func (c *ContentSection) AsData() plugindata.Data {
}
type ContentElement struct {
- id uint32
- Markdown string
- meta *ContentMeta
+ // Type transitions:
+ // mdString <-> source&node <-> serializedNode
+
+ meta *nodes.ContentMeta
+ id uint32
+
+ // do not access directly
+ // legacy markdown string representation
+ mdString []byte
+ // serialized node representation
+ serializedNode *astv1.FabricContentNode
+ source astsrc.ASTSource
+ node *nodes.FabricContentNode
+}
+
+// NewElement is the preferred way to create a new content element.
+// It accepts a list of AST nodes to build the content element.
+func NewElement(content ...astv1.BlockContent) *ContentElement {
+ var children []*astv1.Node
+ for _, node := range content {
+ children = node.ExtendNodes(children)
+ }
+ return &ContentElement{
+ serializedNode: &astv1.FabricContentNode{
+ Root: &astv1.BaseNode{
+ Children: children,
+ },
+ },
+ }
+}
+
+// NewElementFromMarkdown creates a new content element from a markdown string.
+//
+// Deprecated: opt in to working with the new AST by using [NewElement] instead.
+func NewElementFromMarkdown(source string) *ContentElement {
+ return &ContentElement{
+ mdString: []byte(source),
+ }
+}
+
+// NewElementFromMarkdownAndAST creates a new content element from a markdown string and an AST.
+// This is a temporary method to allow for a smooth transition to the new AST.
+// Should only be used for deserialization purposes during the transition.
+func NewElementFromMarkdownAndAST(source []byte, ast *astv1.FabricContentNode) *ContentElement {
+ return &ContentElement{
+ mdString: source,
+ serializedNode: ast,
+ }
+}
+
+var BaseMarkdownOptions = goldmark.WithExtensions(
+ extension.Table,
+ extension.Strikethrough,
+ extension.TaskList,
+)
+
+// AsMarkdownSrc returns the markdown source of the content element.
+//
+// Deprecated: opt in to working with the new AST by using .AsNode()
+func (c *ContentElement) AsMarkdownSrc() []byte {
+ if c.mdString != nil {
+ return c.mdString
+ }
+
+ source, node := c.AsNode()
+ var buf bytes.Buffer
+ err := goldmark.New(
+ BaseMarkdownOptions,
+ goldmark.WithExtensions(
+ markdown.NewRenderer(
+ markdown.WithIgnoredNodes(
+ nodes.ContentNodeKind,
+ nodes.CustomBlockKind,
+ nodes.CustomInlineKind,
+ ),
+ ),
+ ),
+ ).Renderer().Render(&buf, source.AsBytes(), node)
+ if err != nil {
+ slog.Error("failed to render markdown", "error", err)
+ }
+ c.mdString = buf.Bytes()
+ return c.mdString
+}
+
+func (c *ContentElement) AsSerializedNode() *astv1.FabricContentNode {
+ if c.serializedNode != nil {
+ return c.serializedNode
+ }
+ src, node := c.AsNode()
+ serNode, err := astv1.Encode(node, src.AsBytes())
+ if err != nil {
+ slog.Error("failed to encode AST", "error", err)
+ }
+ c.serializedNode = serNode.GetContentNode()
+ return c.serializedNode
+}
+
+func (c *ContentElement) AsNode() (*astsrc.ASTSource, *nodes.FabricContentNode) {
+ if c.node != nil {
+ return &c.source, c.node
+ }
+ if c.serializedNode != nil {
+ node, source, err := astv1.Decode(&astv1.Node{
+ Kind: &astv1.Node_ContentNode{
+ ContentNode: c.serializedNode,
+ },
+ })
+ if err != nil {
+ slog.Error("failed to decode AST", "error", err)
+ } else {
+ c.node = nodes.ToFabricContentNode(node)
+ c.node.Meta = c.meta
+ c.source = source
+ }
+ } else {
+ node := goldmark.New(BaseMarkdownOptions).
+ Parser().Parse(text.NewReader(c.mdString))
+ c.node = nodes.ToFabricContentNode(node)
+ c.node.Meta = c.meta
+ }
+
+ return &c.source, c.node
}
func (c *ContentElement) ID() uint32 {
@@ -214,11 +346,11 @@ func (c *ContentElement) setID(id uint32) {
c.id = id
}
-func (c *ContentElement) Meta() *ContentMeta {
+func (c *ContentElement) Meta() *nodes.ContentMeta {
return c.meta
}
-func (c *ContentElement) setMeta(meta *ContentMeta) {
+func (c *ContentElement) setMeta(meta *nodes.ContentMeta) {
c.meta = meta
}
@@ -226,33 +358,30 @@ func (c *ContentElement) AsPluginData() plugindata.Data {
return c.AsData()
}
+func (c *ContentElement) IsAst() bool {
+ return c.node != nil || c.serializedNode != nil
+}
+
func (c *ContentElement) AsData() plugindata.Data {
if c == nil {
return nil
}
- return plugindata.Map{
+ data := plugindata.Map{
"type": plugindata.String("element"),
"id": plugindata.Number(c.id),
- "markdown": plugindata.String(c.Markdown),
+ "markdown": plugindata.String(c.AsMarkdownSrc()),
"meta": c.meta.AsData(),
}
-}
-
-type ContentMeta struct {
- Provider string
- Plugin string
- Version string
-}
-
-func (meta *ContentMeta) AsData() plugindata.Data {
- if meta == nil {
- return nil
- }
- return plugindata.Map{
- "provider": plugindata.String(meta.Provider),
- "plugin": plugindata.String(meta.Plugin),
- "version": plugindata.String(meta.Version),
+ // we have some AST data, include it
+ if c.IsAst() {
+ ser, err := proto.Marshal(c.AsSerializedNode())
+ if err != nil {
+ slog.Warn("failed to preserve AST in element", "error", err)
+ } else {
+ data["__ast"] = plugindata.String(ser)
+ }
}
+ return data
}
func ParseContentData(data plugindata.Map) (Content, error) {
@@ -312,7 +441,7 @@ func parseContentElement(data plugindata.Map) (*ContentElement, error) {
if !ok {
return nil, fmt.Errorf("missing markdown")
}
- elem.Markdown = string(markdown)
+ elem.mdString = []byte(markdown)
id, ok := data["id"].(plugindata.Number)
if ok {
elem.id = uint32(id)
@@ -321,6 +450,16 @@ func parseContentElement(data plugindata.Map) (*ContentElement, error) {
if ok {
elem.meta = ParseContentMeta(meta)
}
+ if astData, ok := data["__ast"].(plugindata.String); ok {
+ // we have some AST data, include it
+ serNode := &astv1.FabricContentNode{}
+ err := proto.Unmarshal([]byte(astData), serNode)
+ if err != nil {
+ slog.Warn("failed to decode AST in element", "error", err)
+ } else {
+ elem.serializedNode = serNode
+ }
+ }
return elem, nil
}
@@ -341,7 +480,7 @@ func parseContentEmpty(data plugindata.Map) (*ContentEmpty, error) {
return empty, nil
}
-func ParseContentMeta(data plugindata.Data) *ContentMeta {
+func ParseContentMeta(data plugindata.Data) *nodes.ContentMeta {
if data == nil {
return nil
}
@@ -349,7 +488,7 @@ func ParseContentMeta(data plugindata.Data) *ContentMeta {
provider, _ := meta["provider"].(plugindata.String)
plugin, _ := meta["plugin"].(plugindata.String)
version, _ := meta["version"].(plugindata.String)
- return &ContentMeta{
+ return &nodes.ContentMeta{
Provider: string(provider),
Plugin: string(plugin),
Version: string(version),
diff --git a/plugin/pluginapi/v1/content.pb.go b/plugin/pluginapi/v1/content.pb.go
index d130847b..55a7ddd1 100644
--- a/plugin/pluginapi/v1/content.pb.go
+++ b/plugin/pluginapi/v1/content.pb.go
@@ -7,6 +7,7 @@
package pluginapiv1
import (
+ v1 "github.com/blackstork-io/fabric/plugin/ast/v1"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@@ -326,7 +327,8 @@ type ContentElement struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
- Markdown string `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"`
+ Markdown []byte `protobuf:"bytes,1,opt,name=markdown,proto3" json:"markdown,omitempty"`
+ Ast *v1.FabricContentNode `protobuf:"bytes,2,opt,name=ast,proto3,oneof" json:"ast,omitempty"`
}
func (x *ContentElement) Reset() {
@@ -361,11 +363,18 @@ func (*ContentElement) Descriptor() ([]byte, []int) {
return file_pluginapi_v1_content_proto_rawDescGZIP(), []int{4}
}
-func (x *ContentElement) GetMarkdown() string {
+func (x *ContentElement) GetMarkdown() []byte {
if x != nil {
return x.Markdown
}
- return ""
+ return nil
+}
+
+func (x *ContentElement) GetAst() *v1.FabricContentNode {
+ if x != nil {
+ return x.Ast
+ }
+ return nil
}
type ContentEmpty struct {
@@ -411,58 +420,63 @@ var File_pluginapi_v1_content_proto protoreflect.FileDescriptor
var file_pluginapi_v1_content_proto_rawDesc = []byte{
0x0a, 0x1a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x70, 0x6c,
- 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x22, 0x56, 0x0a, 0x08, 0x4c, 0x6f,
- 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x34, 0x0a, 0x06,
- 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70,
- 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x52, 0x06, 0x65, 0x66, 0x66, 0x65,
- 0x63, 0x74, 0x22, 0x74, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73,
- 0x75, 0x6c, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69,
- 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e,
- 0x74, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61,
- 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08,
- 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xba, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6e,
- 0x74, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70,
- 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d,
- 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x38,
- 0x0a, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43,
- 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52,
- 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x65, 0x6d, 0x70, 0x74,
- 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
- 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6d,
- 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07, 0x0a, 0x05,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
- 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64,
- 0x72, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67,
+ 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x10, 0x61, 0x73, 0x74, 0x2f,
+ 0x76, 0x31, 0x2f, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x08,
+ 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x34,
+ 0x0a, 0x06, 0x65, 0x66, 0x66, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c,
+ 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x52, 0x06, 0x65, 0x66,
+ 0x66, 0x65, 0x63, 0x74, 0x22, 0x74, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52,
+ 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61,
+ 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69,
+ 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xba, 0x01, 0x0a, 0x07, 0x43,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
+ 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6c,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
+ 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
+ 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48,
+ 0x00, 0x52, 0x07, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x65, 0x6d,
+ 0x70, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67,
0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
- 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x2c, 0x0a, 0x0e, 0x43, 0x6f,
- 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08,
- 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
- 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x22, 0x0e, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74,
- 0x65, 0x6e, 0x74, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x2a, 0x68, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4c, 0x4f,
- 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x55, 0x4e,
- 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x4c,
- 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x42,
- 0x45, 0x46, 0x4f, 0x52, 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x4f, 0x43, 0x41, 0x54,
- 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x46, 0x54, 0x45, 0x52,
- 0x10, 0x02, 0x42, 0xb2, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69,
- 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
- 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
- 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69,
- 0x6f, 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f,
- 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75,
- 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02,
- 0x0c, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c,
- 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50,
- 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e,
- 0x61, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x07,
+ 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+ 0x6e, 0x74, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x08, 0x63, 0x68, 0x69,
+ 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6c,
+ 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+ 0x6e, 0x74, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x66, 0x0a, 0x0e,
+ 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1a,
+ 0x0a, 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
+ 0x52, 0x08, 0x6d, 0x61, 0x72, 0x6b, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x30, 0x0a, 0x03, 0x61, 0x73,
+ 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x73, 0x74, 0x2e, 0x76, 0x31,
+ 0x2e, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x4e, 0x6f,
+ 0x64, 0x65, 0x48, 0x00, 0x52, 0x03, 0x61, 0x73, 0x74, 0x88, 0x01, 0x01, 0x42, 0x06, 0x0a, 0x04,
+ 0x5f, 0x61, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x45,
+ 0x6d, 0x70, 0x74, 0x79, 0x2a, 0x68, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x45, 0x66, 0x66, 0x65, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49,
+ 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
+ 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x4c, 0x4f, 0x43, 0x41, 0x54,
+ 0x49, 0x4f, 0x4e, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x42, 0x45, 0x46, 0x4f, 0x52,
+ 0x45, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x4c, 0x4f, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
+ 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x5f, 0x41, 0x46, 0x54, 0x45, 0x52, 0x10, 0x02, 0x42, 0xb2,
+ 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69,
+ 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74,
+ 0x6f, 0x50, 0x01, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x62, 0x6c, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x6f, 0x72, 0x6b, 0x2d, 0x69, 0x6f, 0x2f, 0x66, 0x61,
+ 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x6c, 0x75, 0x67,
+ 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x0c, 0x50, 0x6c, 0x75,
+ 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0c, 0x50, 0x6c, 0x75, 0x67,
+ 0x69, 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x18, 0x50, 0x6c, 0x75, 0x67, 0x69,
+ 0x6e, 0x61, 0x70, 0x69, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0xea, 0x02, 0x0d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x61, 0x70, 0x69, 0x3a,
+ 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -480,13 +494,14 @@ func file_pluginapi_v1_content_proto_rawDescGZIP() []byte {
var file_pluginapi_v1_content_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_pluginapi_v1_content_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
var file_pluginapi_v1_content_proto_goTypes = []any{
- (LocationEffect)(0), // 0: pluginapi.v1.LocationEffect
- (*Location)(nil), // 1: pluginapi.v1.Location
- (*ContentResult)(nil), // 2: pluginapi.v1.ContentResult
- (*Content)(nil), // 3: pluginapi.v1.Content
- (*ContentSection)(nil), // 4: pluginapi.v1.ContentSection
- (*ContentElement)(nil), // 5: pluginapi.v1.ContentElement
- (*ContentEmpty)(nil), // 6: pluginapi.v1.ContentEmpty
+ (LocationEffect)(0), // 0: pluginapi.v1.LocationEffect
+ (*Location)(nil), // 1: pluginapi.v1.Location
+ (*ContentResult)(nil), // 2: pluginapi.v1.ContentResult
+ (*Content)(nil), // 3: pluginapi.v1.Content
+ (*ContentSection)(nil), // 4: pluginapi.v1.ContentSection
+ (*ContentElement)(nil), // 5: pluginapi.v1.ContentElement
+ (*ContentEmpty)(nil), // 6: pluginapi.v1.ContentEmpty
+ (*v1.FabricContentNode)(nil), // 7: ast.v1.FabricContentNode
}
var file_pluginapi_v1_content_proto_depIdxs = []int32{
0, // 0: pluginapi.v1.Location.effect:type_name -> pluginapi.v1.LocationEffect
@@ -496,11 +511,12 @@ var file_pluginapi_v1_content_proto_depIdxs = []int32{
4, // 4: pluginapi.v1.Content.section:type_name -> pluginapi.v1.ContentSection
6, // 5: pluginapi.v1.Content.empty:type_name -> pluginapi.v1.ContentEmpty
3, // 6: pluginapi.v1.ContentSection.children:type_name -> pluginapi.v1.Content
- 7, // [7:7] is the sub-list for method output_type
- 7, // [7:7] is the sub-list for method input_type
- 7, // [7:7] is the sub-list for extension type_name
- 7, // [7:7] is the sub-list for extension extendee
- 0, // [0:7] is the sub-list for field type_name
+ 7, // 7: pluginapi.v1.ContentElement.ast:type_name -> ast.v1.FabricContentNode
+ 8, // [8:8] is the sub-list for method output_type
+ 8, // [8:8] is the sub-list for method input_type
+ 8, // [8:8] is the sub-list for extension type_name
+ 8, // [8:8] is the sub-list for extension extendee
+ 0, // [0:8] is the sub-list for field type_name
}
func init() { file_pluginapi_v1_content_proto_init() }
@@ -587,6 +603,7 @@ func file_pluginapi_v1_content_proto_init() {
(*Content_Section)(nil),
(*Content_Empty)(nil),
}
+ file_pluginapi_v1_content_proto_msgTypes[4].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
diff --git a/plugin/pluginapi/v1/content_decoder.go b/plugin/pluginapi/v1/content_decoder.go
index 627fee96..4b271d8a 100644
--- a/plugin/pluginapi/v1/content_decoder.go
+++ b/plugin/pluginapi/v1/content_decoder.go
@@ -1,6 +1,12 @@
package pluginapiv1
-import "github.com/blackstork-io/fabric/plugin"
+import (
+ "fmt"
+ "log/slog"
+
+ "github.com/blackstork-io/fabric/pkg/utils"
+ "github.com/blackstork-io/fabric/plugin"
+)
func decodeContentResult(src *ContentResult) *plugin.ContentResult {
if src == nil {
@@ -13,48 +19,21 @@ func decodeContentResult(src *ContentResult) *plugin.ContentResult {
}
func decodeContent(src *Content) plugin.Content {
- if src == nil {
- return nil
- }
- switch val := src.Value.(type) {
+ switch val := src.GetValue().(type) {
case *Content_Element:
- return decodeContentElement(val.Element)
+ return plugin.NewElementFromMarkdownAndAST(val.Element.GetMarkdown(), val.Element.GetAst())
case *Content_Section:
- return decodeContentSection(val.Section)
+ return &plugin.ContentSection{
+ Children: utils.FnMap(val.Section.GetChildren(), decodeContent),
+ }
case *Content_Empty:
- return decodeContentEmpty(val.Empty)
- default:
- return nil
- }
-}
-
-func decodeContentElement(src *ContentElement) *plugin.ContentElement {
- if src == nil {
+ return &plugin.ContentEmpty{}
+ case nil:
return nil
- }
- return &plugin.ContentElement{
- Markdown: src.GetMarkdown(),
- }
-}
-
-func decodeContentEmpty(src *ContentEmpty) *plugin.ContentEmpty {
- if src == nil {
- return nil
- }
- return &plugin.ContentEmpty{}
-}
-
-func decodeContentSection(src *ContentSection) *plugin.ContentSection {
- if src == nil {
+ default:
+ slog.Error("unknown content type", "type", fmt.Sprintf("%T", src))
return nil
}
- children := make([]plugin.Content, len(src.GetChildren()))
- for i, child := range src.GetChildren() {
- children[i] = decodeContent(child)
- }
- return &plugin.ContentSection{
- Children: children,
- }
}
func decodeLocation(src *Location) *plugin.Location {
diff --git a/plugin/pluginapi/v1/content_encoder.go b/plugin/pluginapi/v1/content_encoder.go
index 70a67c1e..0d64f175 100644
--- a/plugin/pluginapi/v1/content_encoder.go
+++ b/plugin/pluginapi/v1/content_encoder.go
@@ -1,6 +1,12 @@
package pluginapiv1
-import "github.com/blackstork-io/fabric/plugin"
+import (
+ "fmt"
+ "log/slog"
+
+ "github.com/blackstork-io/fabric/pkg/utils"
+ "github.com/blackstork-io/fabric/plugin"
+)
func encodeContentResult(src *plugin.ContentResult) *ContentResult {
if src == nil {
@@ -13,60 +19,43 @@ func encodeContentResult(src *plugin.ContentResult) *ContentResult {
}
func encodeContent(src plugin.Content) *Content {
- if src == nil {
- return nil
- }
+ var variant isContent_Value
switch val := src.(type) {
+ case nil:
+ return nil
case *plugin.ContentElement:
- return &Content{
- Value: &Content_Element{
- Element: encodeContentElement(val),
- },
+ if val == nil {
+ break
+ }
+ el := &ContentElement{
+ Markdown: val.AsMarkdownSrc(),
+ }
+ if val.IsAst() {
+ el.Ast = val.AsSerializedNode()
+ }
+ variant = &Content_Element{
+ Element: el,
}
case *plugin.ContentSection:
- return &Content{
- Value: &Content_Section{
- Section: encodeContentSection(val),
+ if val == nil {
+ break
+ }
+ variant = &Content_Section{
+ Section: &ContentSection{
+ Children: utils.FnMap(val.Children, encodeContent),
},
}
case *plugin.ContentEmpty:
- return &Content{
- Value: &Content_Empty{
- Empty: encodeContentEmpty(val),
- },
+ variant = &Content_Empty{
+ Empty: &ContentEmpty{},
}
default:
- return nil
- }
-}
-
-func encodeContentSection(src *plugin.ContentSection) *ContentSection {
- if src == nil {
- return nil
- }
- children := make([]*Content, len(src.Children))
- for i, child := range src.Children {
- children[i] = encodeContent(child)
- }
- return &ContentSection{
- Children: children,
- }
-}
-
-func encodeContentElement(src *plugin.ContentElement) *ContentElement {
- if src == nil {
- return nil
- }
- return &ContentElement{
- Markdown: src.Markdown,
+ slog.Error("unknown content type", "type", fmt.Sprintf("%T", src))
}
-}
-func encodeContentEmpty(src *plugin.ContentEmpty) *ContentEmpty {
- if src == nil {
- return nil
+ return &Content{
+ Value: variant,
}
- return &ContentEmpty{}
}
func encodeLocation(src *plugin.Location) *Location {
@@ -74,7 +63,7 @@ func encodeLocation(src *plugin.Location) *Location {
return nil
}
return &Location{
- Index: uint32(src.Index),
+ Index: src.Index,
Effect: encodeLocationEffect(src.Effect),
}
}
diff --git a/plugin/pluginapi/v1/is_plugin.go b/plugin/pluginapi/v1/is_plugin.go
index e52d1212..e745ef65 100644
--- a/plugin/pluginapi/v1/is_plugin.go
+++ b/plugin/pluginapi/v1/is_plugin.go
@@ -10,6 +10,8 @@ import (
"github.com/evanphx/go-hclog-slog/hclogslog"
"github.com/hashicorp/go-hclog"
+
+ "github.com/blackstork-io/fabric/pkg/utils/slogutil"
)
var logMutex sync.Mutex
@@ -27,7 +29,7 @@ func init() {
// Make slog use hclogger
// NOTE: slog.SetDefault also calls log.SetOutput, the order of operations is important
- slog.SetDefault(slog.New(hclogslog.Adapt(hclog.New(&hclog.LoggerOptions{
+ slog.SetDefault(slog.New(slogutil.NewSourceRewriter(hclogslog.Adapt(hclog.New(&hclog.LoggerOptions{
Level: hclog.Trace,
Output: os.Stderr,
JSONFormat: true,
@@ -35,7 +37,7 @@ func init() {
IncludeLocation: true,
AdditionalLocationOffset: 3, // for slog
Mutex: &logMutex,
- }))))
+ })))))
// Make standard logger use hclogger
log.SetOutput(hclog.Default().StandardWriter(&hclog.StandardLoggerOptions{
diff --git a/plugin/schema.go b/plugin/schema.go
index 4ce815a0..61f2fcca 100644
--- a/plugin/schema.go
+++ b/plugin/schema.go
@@ -8,6 +8,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/blackstork-io/fabric/pkg/diagnostics"
+ "github.com/blackstork-io/fabric/plugin/ast/nodes"
"github.com/blackstork-io/fabric/plugin/plugindata"
)
@@ -102,7 +103,8 @@ func (p *Schema) ProvideContent(ctx context.Context, name string, params *Provid
if diags.HasErrors() {
return nil, diags
}
- result.Content.setMeta(&ContentMeta{
+ // TODO: set metadata in content provider
+ result.Content.setMeta(&nodes.ContentMeta{
Provider: name,
Plugin: p.Name,
Version: p.Version,
diff --git a/print/htmlprint/printer.go b/print/htmlprint/printer.go
index e9562dc6..8d1a7c43 100644
--- a/print/htmlprint/printer.go
+++ b/print/htmlprint/printer.go
@@ -8,11 +8,9 @@ import (
"fmt"
"html/template"
"io"
- "strings"
"github.com/pelletier/go-toml/v2"
"github.com/yuin/goldmark"
- "github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
"gopkg.in/yaml.v3"
@@ -86,7 +84,7 @@ func (p Printer) Print(ctx context.Context, w io.Writer, el plugin.Content) (err
return err
}
md := goldmark.New(
- goldmark.WithExtensions(extension.GFM),
+ plugin.BaseMarkdownOptions,
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
@@ -151,9 +149,9 @@ func (p Printer) firstTitle(el plugin.Content) (string, bool) {
case *plugin.ContentElement:
meta := el.Meta()
if meta.Plugin == "blackstork/builtin" && meta.Provider == "title" {
- return strings.TrimSpace(
- strings.TrimPrefix(el.Markdown, "#"),
- ), true
+ return string(bytes.TrimSpace(
+ bytes.TrimPrefix(el.AsMarkdownSrc(), []byte("#")),
+ )), true
}
}
return "", false
@@ -179,16 +177,16 @@ func (p Printer) extractFrontmatter(el plugin.Content) (*plugin.ContentElement,
}
func (p Printer) parseFrontmatter(fm *plugin.ContentElement) (result map[string]any, err error) {
- str := fm.Markdown
+ str := fm.AsMarkdownSrc()
switch {
- case strings.HasPrefix(str, "{"):
- err = json.Unmarshal([]byte(str), &result)
- case strings.HasPrefix(str, "---"):
- str = strings.Trim(str, "-")
- err = yaml.Unmarshal([]byte(str), &result)
- case strings.HasPrefix(str, "+++"):
- str = strings.Trim(str, "+")
- err = toml.Unmarshal([]byte(str), &result)
+ case bytes.HasPrefix(str, []byte("{")):
+ err = json.Unmarshal(str, &result)
+ case bytes.HasPrefix(str, []byte("---")):
+ str = bytes.Trim(str, "-")
+ err = yaml.Unmarshal(str, &result)
+ case bytes.HasPrefix(str, []byte("+++")):
+ str = bytes.Trim(str, "+")
+ err = toml.Unmarshal(str, &result)
default:
err = fmt.Errorf("invalid frontmatter format")
}
diff --git a/print/mdprint/printer.go b/print/mdprint/printer.go
index 0888eaca..ea019e12 100644
--- a/print/mdprint/printer.go
+++ b/print/mdprint/printer.go
@@ -3,9 +3,18 @@ package mdprint
import (
"bytes"
"context"
+ "fmt"
"io"
+ "log/slog"
+
+ markdown "github.com/blackstork-io/goldmark-markdown"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/ast"
"github.com/blackstork-io/fabric/plugin"
+ "github.com/blackstork-io/fabric/plugin/ast/astsrc"
+ "github.com/blackstork-io/fabric/plugin/ast/nodes"
+ "github.com/blackstork-io/fabric/print"
)
// Printer is the interface for printing markdown content.
@@ -38,19 +47,54 @@ func (p Printer) Print(ctx context.Context, w io.Writer, el plugin.Content) (err
func (p Printer) printContent(w io.Writer, content plugin.Content) (err error) {
switch content := content.(type) {
case *plugin.ContentElement:
- if err = p.printContentElement(w, content); err != nil {
- return
+ if content.IsAst() {
+ src, node := content.AsNode()
+ err = p.printContentElement(w, src, node)
+ } else {
+ _, err = w.Write(content.AsMarkdownSrc())
}
case *plugin.ContentSection:
- if err = p.printContentSection(w, content); err != nil {
- return
- }
+ err = p.printContentSection(w, content)
}
- return nil
+ return
}
-func (p Printer) printContentElement(w io.Writer, elem *plugin.ContentElement) error {
- _, err := w.Write([]byte(elem.Markdown))
+func (p Printer) printContentElement(w io.Writer, source *astsrc.ASTSource, node *nodes.FabricContentNode) error {
+ n, err := print.ReplaceNodes(node, func(n ast.Node) (repl ast.Node, err error) {
+ switch nT := n.(type) {
+ case *nodes.CustomBlock:
+ slog.Info("OtherBlock found in AST, replacing with message segment")
+ p := ast.NewCodeBlock()
+ p.AppendChild(p, ast.NewRawTextSegment(
+ source.Appendf("", nT.Data.GetTypeUrl()),
+ ))
+ return p, nil
+ case *nodes.CustomInline:
+ slog.Info("OtherInline found in AST, replacing with message segment")
+ p := ast.NewCodeSpan()
+ p.AppendChild(p, ast.NewRawTextSegment(
+ source.Appendf("", nT.Data.GetTypeUrl()),
+ ))
+ return p, nil
+ }
+ return n, nil
+ })
+ if err != nil {
+ return fmt.Errorf("replacement failed: %w", err)
+ }
+ err = goldmark.New(
+ plugin.BaseMarkdownOptions,
+ goldmark.WithExtensions(
+ markdown.NewRenderer(
+ markdown.WithIgnoredNodes(
+ nodes.ContentNodeKind,
+ ),
+ ),
+ ),
+ ).Renderer().Render(w, source.AsBytes(), n)
+ if err != nil {
+ return fmt.Errorf("failed to render markdown from AST: %w", err)
+ }
return err
}
diff --git a/print/pdfprint/printer.go b/print/pdfprint/printer.go
index 6a49c586..98ac6989 100644
--- a/print/pdfprint/printer.go
+++ b/print/pdfprint/printer.go
@@ -3,15 +3,19 @@ package pdfprint
import (
"bytes"
"context"
+ "fmt"
"image/color"
"io"
+ "log/slog"
pdf "github.com/stephenafamo/goldmark-pdf"
"github.com/yuin/goldmark"
- "github.com/yuin/goldmark/extension"
+ "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/blackstork-io/fabric/plugin"
+ "github.com/blackstork-io/fabric/plugin/ast/astsrc"
+ "github.com/blackstork-io/fabric/print"
"github.com/blackstork-io/fabric/print/mdprint"
)
@@ -32,13 +36,37 @@ func Print(w io.Writer, el plugin.Content) error {
}
func (p Printer) Print(ctx context.Context, w io.Writer, el plugin.Content) (err error) {
- p.removeFrontatter(el)
- buf := bytes.NewBuffer(nil)
+ p.removeFrontmatter(el)
+ err = print.ReplaceNodesInContent(el, func(src *astsrc.ASTSource, n ast.Node) (repl ast.Node, err error) {
+ switch n := n.(type) {
+ case *ast.HTMLBlock:
+ slog.Info("HTML block found in AST, replacing with message segment")
+ p := ast.NewCodeBlock()
+ p.AppendChild(p, ast.NewRawTextSegment(
+ src.Appendf("", n.Kind()),
+ ))
+ return p, nil
+ case *ast.RawHTML:
+ slog.Info("Raw HTML found in AST, replacing with message segment")
+ p := ast.NewCodeSpan()
+ p.AppendChild(p, ast.NewRawTextSegment(
+ src.Appendf("", n.Kind()),
+ ))
+ return p, nil
+ }
+ return n, nil
+ })
+ if err != nil {
+ return fmt.Errorf("replacement failed: %w", err)
+ }
+
+ buf := &bytes.Buffer{}
if err := p.md.Print(ctx, buf, el); err != nil {
return err
}
+
md := goldmark.New(
- goldmark.WithExtensions(extension.GFM),
+ plugin.BaseMarkdownOptions,
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
@@ -59,7 +87,7 @@ func (p Printer) Print(ctx context.Context, w io.Writer, el plugin.Content) (err
return md.Convert(buf.Bytes(), w)
}
-func (p Printer) removeFrontatter(el plugin.Content) bool {
+func (p Printer) removeFrontmatter(el plugin.Content) bool {
section, ok := el.(*plugin.ContentSection)
if !ok {
return false
diff --git a/print/printer.go b/print/printer.go
index 3488f1e6..aace1513 100644
--- a/print/printer.go
+++ b/print/printer.go
@@ -2,12 +2,94 @@ package print
import (
"context"
+ "errors"
+ "fmt"
"io"
+ "github.com/yuin/goldmark/ast"
+
"github.com/blackstork-io/fabric/plugin"
+ "github.com/blackstork-io/fabric/plugin/ast/astsrc"
)
// Printer is the interface for printing content.
type Printer interface {
Print(ctx context.Context, w io.Writer, el plugin.Content) error
}
+
+var (
+ // ErrReplacerSkipChildren could be returned from the ReplaceNodes replacer func
+ // to skip children. Will never be returned by ReplaceNodes itself.
+ ErrReplacerSkipChildren = errors.New("skip children")
+ // ErrReplacerStuck means that replacer failed to make progress
+ ErrReplacerStuck = errors.New("replacer stuck")
+)
+
+// ReplaceNodes walks the AST starting from the given node and replaces nodes in it.
+// If replacer returns nil - the node is deleted
+func ReplaceNodes(n ast.Node, replacer func(n ast.Node) (repl ast.Node, err error)) (ast.Node, error) {
+ const maxReplacementsWithoutAdvance = 100
+ if n == nil {
+ return nil, nil
+ }
+ n, err := replacer(n)
+ if n == nil || err != nil {
+ if err == ErrReplacerSkipChildren {
+ err = nil
+ }
+ return n, err
+ }
+ c := n.FirstChild()
+ replacementsWithoutAdvance := 0
+ for c != nil {
+ repl, err := ReplaceNodes(c, replacer)
+ if err != nil {
+ return n, err
+ }
+ switch repl {
+ case nil:
+ next := c.NextSibling()
+ n.RemoveChild(n, c)
+ c = next
+ replacementsWithoutAdvance = 0
+ case c:
+ c = c.NextSibling()
+ replacementsWithoutAdvance = 0
+ default:
+ if replacementsWithoutAdvance >= maxReplacementsWithoutAdvance {
+ return n, fmt.Errorf("%w: node %q", ErrReplacerStuck, repl.Kind())
+ }
+ replacementsWithoutAdvance++
+ n.ReplaceChild(n, c, repl)
+ // Intentionally trying to replace the replacement result
+ // This allows replacer to not care whether the replacement node
+ // or its children need to be further replaced
+ c = repl
+ }
+ }
+ return n, nil
+}
+
+// ReplaceNodesInContent runs ReplaceNodes on plugin.ContentAST nodes
+// Replacer is not expected to replace the top-level plugin node (ContentNode)
+func ReplaceNodesInContent(el plugin.Content, replacer func(src *astsrc.ASTSource, n ast.Node) (repl ast.Node, err error)) error {
+ switch el := el.(type) {
+ case *plugin.ContentSection:
+ for _, child := range el.Children {
+ err := ReplaceNodesInContent(child, replacer)
+ if err != nil {
+ return err
+ }
+ }
+ case *plugin.ContentElement:
+ if !el.IsAst() {
+ return nil
+ }
+ src, node := el.AsNode()
+ _, err := ReplaceNodes(node, func(n ast.Node) (repl ast.Node, err error) {
+ return replacer(src, n)
+ })
+ return err
+ }
+ return nil
+}
diff --git a/proto/ast/v1/ast.proto b/proto/ast/v1/ast.proto
new file mode 100644
index 00000000..5d89481a
--- /dev/null
+++ b/proto/ast/v1/ast.proto
@@ -0,0 +1,232 @@
+syntax = "proto3";
+
+package ast.v1;
+
+import "google/protobuf/any.proto";
+
+message Attribute {
+ bytes name = 1;
+ oneof value {
+ bytes bytes = 2;
+ string str = 3;
+ }
+}
+
+message BaseNode {
+ repeated Node children = 1;
+ repeated Attribute attributes = 2;
+ // value meaningful only for blocks
+ bool blank_previous_lines = 3;
+}
+
+message Node {
+ oneof kind {
+ // Blocks
+ Document document = 1;
+ TextBlock text_block = 5;
+ Paragraph paragraph = 6;
+ Heading heading = 7;
+ ThematicBreak thematic_break = 8;
+ CodeBlock code_block = 9;
+ FencedCodeBlock fenced_code_block = 10;
+ Blockquote blockquote = 11;
+ List list = 12;
+ ListItem list_item = 13;
+ HTMLBlock html_block = 14;
+
+ // inlines
+ Text text = 15;
+ String string = 16;
+ CodeSpan code_span = 17;
+ Emphasis emphasis = 18;
+ LinkOrImage link_or_image = 19;
+ AutoLink auto_link = 20;
+ RawHTML raw_html = 21;
+
+ // Github Flavored Markdown
+ // blocks
+ Table table = 22;
+ TableRow table_row = 23;
+ TableCell table_cell = 24;
+ // inline
+ TaskCheckbox task_checkbox = 25;
+ Strikethrough strikethrough = 26;
+
+
+ // Root of the plugin-rendered data
+ FabricContentNode content_node = 254;
+ // Custom node types can be serialized using this
+ CustomNode custom = 255;
+ }
+}
+
+// Node kinds
+
+message Document {
+ BaseNode base = 1;
+}
+
+message TextBlock {
+ BaseNode base = 1;
+}
+
+message Paragraph {
+ BaseNode base = 1;
+}
+
+message Heading {
+ BaseNode base = 1;
+ uint32 level = 2;
+}
+
+message ThematicBreak {
+ BaseNode base = 1;
+}
+
+message CodeBlock {
+ BaseNode base = 1;
+ repeated bytes lines = 2;
+}
+
+message FencedCodeBlock {
+ BaseNode base = 1;
+ Text info = 2;
+ repeated bytes lines = 3;
+}
+
+message Blockquote {
+ BaseNode base = 1;
+}
+
+message List {
+ BaseNode base = 1;
+ uint32 marker = 2;
+ bool is_tight = 3;
+ uint32 start = 4;
+}
+
+message ListItem {
+ BaseNode base = 1;
+ int64 offset = 2;
+}
+
+enum HTMLBlockType {
+ HTML_BLOCK_TYPE_UNSPECIFIED = 0;
+ HTML_BLOCK_TYPE_1 = 1;
+ HTML_BLOCK_TYPE_2 = 2;
+ HTML_BLOCK_TYPE_3 = 3;
+ HTML_BLOCK_TYPE_4 = 4;
+ HTML_BLOCK_TYPE_5 = 5;
+ HTML_BLOCK_TYPE_6 = 6;
+ HTML_BLOCK_TYPE_7 = 7;
+}
+
+message HTMLBlock {
+ BaseNode base = 1;
+ HTMLBlockType type = 2;
+ repeated bytes lines = 3;
+ bytes closure_line = 4;
+}
+
+message Text {
+ BaseNode base = 1;
+ bytes segment = 2;
+ bool soft_line_break = 3;
+ bool hard_line_break = 4;
+ bool raw = 5;
+}
+
+message String {
+ BaseNode base = 1;
+ bytes value = 2;
+ bool raw = 3;
+ bool code = 4;
+}
+
+message CodeSpan {
+ BaseNode base = 1;
+}
+
+message Emphasis {
+ BaseNode base = 1;
+ int64 level = 2;
+}
+
+message LinkOrImage {
+ BaseNode base = 1;
+ bytes destination = 2;
+ bytes title = 3;
+ bool is_image = 4;
+}
+
+enum AutoLinkType {
+ AUTO_LINK_TYPE_UNSPECIFIED = 0;
+ AUTO_LINK_TYPE_EMAIL = 1;
+ AUTO_LINK_TYPE_URL = 2;
+}
+
+message AutoLink {
+ BaseNode base = 1;
+ AutoLinkType type = 2;
+ bytes protocol = 3;
+ bytes value = 4;
+}
+
+message RawHTML {
+ BaseNode base = 1;
+ repeated bytes segments = 2;
+}
+
+enum CellAlignment {
+ CELL_ALIGNMENT_UNSPECIFIED = 0;
+ CELL_ALIGNMENT_LEFT = 1;
+ CELL_ALIGNMENT_RIGHT = 2;
+ CELL_ALIGNMENT_CENTER = 3;
+ CELL_ALIGNMENT_NONE = 4;
+}
+
+message Table {
+ BaseNode base = 1;
+ repeated CellAlignment alignments = 2;
+}
+
+message TableRow {
+ BaseNode base = 1;
+ repeated CellAlignment alignments = 2;
+ bool is_header = 4;
+}
+
+message TableCell {
+ BaseNode base = 1;
+ CellAlignment alignment = 2;
+}
+
+message TaskCheckbox {
+ BaseNode base = 1;
+ bool is_checked = 2;
+}
+
+message Strikethrough {
+ BaseNode base = 1;
+}
+
+message CustomNode {
+ // Indicates that this block is an inline element
+ bool is_inline = 1;
+ google.protobuf.Any data = 2;
+ bool blank_previous_lines = 3;
+}
+
+message Metadata {
+ // ie "blackstork/builtin"
+ string provider = 1;
+ // ie "title"
+ string plugin = 2;
+ string version = 3;
+}
+
+// Root of the plugin-rendered data
+message FabricContentNode {
+ Metadata metadata = 1;
+ BaseNode root = 2; // direct content, no document node
+}
diff --git a/proto/pluginapi/v1/content.proto b/proto/pluginapi/v1/content.proto
index 0038bfe0..a3f84592 100644
--- a/proto/pluginapi/v1/content.proto
+++ b/proto/pluginapi/v1/content.proto
@@ -1,6 +1,7 @@
syntax = "proto3";
package pluginapi.v1;
+import "ast/v1/ast.proto";
enum LocationEffect {
@@ -21,9 +22,9 @@ message ContentResult {
message Content {
oneof value {
- ContentElement element = 1;
- ContentSection section = 2;
- ContentEmpty empty = 3;
+ ContentElement element = 1;
+ ContentSection section = 2;
+ ContentEmpty empty = 3;
};
}
@@ -32,7 +33,8 @@ message ContentSection {
}
message ContentElement {
- string markdown = 1;
+ bytes markdown = 1;
+ optional ast.v1.FabricContentNode ast = 2;
}
message ContentEmpty {}
\ No newline at end of file