diff --git a/modules/markup/markdown/ast.go b/modules/markup/markdown/ast.go
index e844f801c4..3e6e291ab2 100644
--- a/modules/markup/markdown/ast.go
+++ b/modules/markup/markdown/ast.go
@@ -76,7 +76,8 @@ func IsSummary(node ast.Node) bool {
// TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox
type TaskCheckBoxListItem struct {
*ast.ListItem
- IsChecked bool
+ IsChecked bool
+ SourcePosition int
}
// KindTaskCheckBoxListItem is the NodeKind for TaskCheckBoxListItem
@@ -86,6 +87,7 @@ var KindTaskCheckBoxListItem = ast.NewNodeKind("TaskCheckBoxListItem")
func (n *TaskCheckBoxListItem) Dump(source []byte, level int) {
m := map[string]string{}
m["IsChecked"] = strconv.FormatBool(n.IsChecked)
+ m["SourcePosition"] = strconv.FormatInt(int64(n.SourcePosition), 10)
ast.DumpHelper(n, source, level, m, nil)
}
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go
index f03a780900..ff4e6b1bd0 100644
--- a/modules/markup/markdown/goldmark.go
+++ b/modules/markup/markdown/goldmark.go
@@ -177,6 +177,11 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
newChild := NewTaskCheckBoxListItem(listItem)
newChild.IsChecked = taskCheckBox.IsChecked
newChild.SetAttributeString("class", []byte("task-list-item"))
+ segments := newChild.FirstChild().Lines()
+ if segments.Len() > 0 {
+ segment := segments.At(0)
+ newChild.SourcePosition = rc.metaLength + segment.Start
+ }
v.AppendChild(v, newChild)
}
}
@@ -457,12 +462,7 @@ func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byt
} else {
_, _ = w.WriteString("
")
}
- _, _ = w.WriteString(` 0 {
- segment := segments.At(0)
- _, _ = w.WriteString(fmt.Sprintf(` data-source-position="%d"`, segment.Start))
- }
+ fmt.Fprintf(w, `
+
+
+foo |
+
+
+
+
+bar |
+
+
+
+
+`,
+ },
+ }
+
+ for _, test := range testcases {
+ res, err := RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
+ assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
+ assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
+ }
+}
diff --git a/modules/markup/markdown/renderconfig.go b/modules/markup/markdown/renderconfig.go
index 691df74312..f4c48d1b3d 100644
--- a/modules/markup/markdown/renderconfig.go
+++ b/modules/markup/markdown/renderconfig.go
@@ -20,6 +20,9 @@ type RenderConfig struct {
TOC string // "false": hide, "side"/empty: in sidebar, "main"/"true": in main view
Lang string
yamlNode *yaml.Node
+
+ // Used internally. Cannot be controlled by frontmatter.
+ metaLength int
}
func renderMetaModeFromString(s string) markup.RenderMetaMode {
diff --git a/web_src/js/markup/tasklist.js b/web_src/js/markup/tasklist.js
index 0f03837baa..ad1c6964a7 100644
--- a/web_src/js/markup/tasklist.js
+++ b/web_src/js/markup/tasklist.js
@@ -29,6 +29,14 @@ export function initMarkupTasklist() {
const encoder = new TextEncoder();
const buffer = encoder.encode(oldContent);
+ // Indexes may fall off the ends and return undefined.
+ if (buffer[position - 1] !== '['.codePointAt(0) ||
+ buffer[position] !== ' '.codePointAt(0) && buffer[position] !== 'x'.codePointAt(0) ||
+ buffer[position + 1] !== ']'.codePointAt(0)) {
+ // Position is probably wrong. Revert and don't allow change.
+ checkbox.checked = !checkbox.checked;
+ throw new Error(`Expected position to be space or x and surrounded by brackets, but it's not: position=${position}`);
+ }
buffer.set(encoder.encode(checkboxCharacter), position);
const newContent = new TextDecoder().decode(buffer);