Implemented Markdown escaping for render output
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
This commit is contained in:
@@ -17,13 +17,13 @@ func (MarkdownRenderer) Render(transcript Transcript, opts Options) (string, err
|
||||
if title == "" {
|
||||
title = "Transcript"
|
||||
}
|
||||
lines = append(lines, "# "+title, "")
|
||||
lines = append(lines, "# "+escapeMarkdownInline(title), "")
|
||||
|
||||
if opts.IncludeMetadata {
|
||||
lines = append(lines,
|
||||
fmt.Sprintf("- Application: %s", transcript.Metadata.Application),
|
||||
fmt.Sprintf("- Version: %s", transcript.Metadata.Version),
|
||||
fmt.Sprintf("- Output schema: %s", transcript.Schema),
|
||||
fmt.Sprintf("- Application: %s", escapeMarkdownInline(transcript.Metadata.Application)),
|
||||
fmt.Sprintf("- Version: %s", escapeMarkdownInline(transcript.Metadata.Version)),
|
||||
fmt.Sprintf("- Output schema: %s", escapeMarkdownInline(transcript.Schema)),
|
||||
"",
|
||||
)
|
||||
}
|
||||
@@ -37,11 +37,11 @@ func (MarkdownRenderer) Render(transcript Transcript, opts Options) (string, err
|
||||
parts = append(parts, fmt.Sprintf("[#%d]", segment.ID))
|
||||
}
|
||||
|
||||
text := segment.Text
|
||||
text := escapeMarkdownInline(segment.Text)
|
||||
if shouldItalicize(segment.Categories) {
|
||||
text = "*" + text + "*"
|
||||
}
|
||||
parts = append(parts, fmt.Sprintf("**%s:** %s", segment.Speaker, text))
|
||||
parts = append(parts, fmt.Sprintf("**%s:** %s", escapeMarkdownInline(segment.Speaker), text))
|
||||
lines = append(lines, strings.Join(parts, " "))
|
||||
lines = append(lines, "")
|
||||
}
|
||||
@@ -53,6 +53,28 @@ func (MarkdownRenderer) Render(transcript Transcript, opts Options) (string, err
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func escapeMarkdownInline(value string) string {
|
||||
replacer := strings.NewReplacer(
|
||||
`\`, `\\`,
|
||||
"`", "\\`",
|
||||
"*", "\\*",
|
||||
"_", "\\_",
|
||||
"{", "\\{",
|
||||
"}", "\\}",
|
||||
"[", "\\[",
|
||||
"]", "\\]",
|
||||
"(", "\\(",
|
||||
")", "\\)",
|
||||
"#", "\\#",
|
||||
"+", "\\+",
|
||||
"!", "\\!",
|
||||
"|", "\\|",
|
||||
"<", "\\<",
|
||||
">", "\\>",
|
||||
)
|
||||
return replacer.Replace(value)
|
||||
}
|
||||
|
||||
func shouldItalicize(categories []string) bool {
|
||||
for _, category := range categories {
|
||||
switch category {
|
||||
|
||||
@@ -108,6 +108,48 @@ func TestMarkdownRendererMetadataOnlyWhenRequested(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkdownRendererEscapesUserProvidedMarkdown(t *testing.T) {
|
||||
transcript := Transcript{
|
||||
Schema: "seriatim-intermediate",
|
||||
Metadata: Metadata{
|
||||
Application: "seriatim *cli*",
|
||||
Version: "v[test]",
|
||||
},
|
||||
Segments: []Segment{
|
||||
{
|
||||
ID: 1,
|
||||
Start: 1,
|
||||
End: 2,
|
||||
Speaker: "Dr. *A_[1]",
|
||||
Text: "Use *literal* [link](target) and `code` \\ slash!",
|
||||
},
|
||||
{
|
||||
ID: 2,
|
||||
Start: 2,
|
||||
End: 3,
|
||||
Speaker: "Narrator",
|
||||
Text: "_aside_ with | pipe",
|
||||
Categories: []string{"background"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
output, err := MarkdownRenderer{}.Render(transcript, Options{
|
||||
Title: "# Planning [notes]",
|
||||
IncludeTimestamps: false,
|
||||
IncludeMetadata: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("render markdown: %v", err)
|
||||
}
|
||||
|
||||
assertContains(t, output, "# \\# Planning \\[notes\\]")
|
||||
assertContains(t, output, "- Application: seriatim \\*cli\\*")
|
||||
assertContains(t, output, "- Version: v\\[test\\]")
|
||||
assertContains(t, output, "**Dr. \\*A\\_\\[1\\]:** Use \\*literal\\* \\[link\\]\\(target\\) and \\`code\\` \\\\ slash\\!")
|
||||
assertContains(t, output, "**Narrator:** *\\_aside\\_ with \\| pipe*")
|
||||
}
|
||||
|
||||
func TestMarkdownRendererCategoryHintItalicsAndUnknownCategories(t *testing.T) {
|
||||
transcript := Transcript{
|
||||
Segments: []Segment{
|
||||
@@ -176,3 +218,10 @@ func TestMarkdownRendererIsDeterministic(t *testing.T) {
|
||||
t.Fatalf("expected HH:MM:SS timestamp formatting:\n%s", first)
|
||||
}
|
||||
}
|
||||
|
||||
func assertContains(t *testing.T, value string, want string) {
|
||||
t.Helper()
|
||||
if !strings.Contains(value, want) {
|
||||
t.Fatalf("expected output to contain %q:\n%s", want, value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user