Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(extension/tasklist): Preserve TaskList item source positions to … #452

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

movsb
Copy link
Contributor

@movsb movsb commented May 28, 2024

…allow Tasks to be Accomplished later (by updating markdown source and re-rendering).

This is for someone who may want to use Goldmark as a better TODO list implementation.

Reference AST Transformer

// ... implements parser.ASTTransformer.
type PreserveTaskListSourcePosition struct{}

func (a *PreserveTaskListSourcePosition) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
	ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
		if entering && n.Kind() == extast.KindTaskCheckBox {
			c := n.(*extast.TaskCheckBox)
			c.SetAttributeString(`data-source-position`, fmt.Sprintf(`[%d,%d]`, c.Segment.Start, c.Segment.Stop))
		}
		return ast.WalkContinue, nil
	})
}

Reference Renderer (patched)

goldmark (preserve-tasklist-source-position) → git diff
diff --git a/extension/tasklist.go b/extension/tasklist.go
index 6a5e98e..ac12f9e 100644
--- a/extension/tasklist.go
+++ b/extension/tasklist.go
@@ -85,6 +85,13 @@ func (r *TaskCheckBoxHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRe
        reg.Register(ast.KindTaskCheckBox, r.renderTaskCheckBox)
 }
 
+// TaskCheckBoxAttributeFilter defines attribute names which check elements can have.
+var TaskCheckBoxAttributeFilter = html.GlobalAttributeFilter.Extend(
+       []byte(`checked`),
+       []byte(`disabled`),
+       []byte(`data-source-position`),
+)
+
 func (r *TaskCheckBoxHTMLRenderer) renderTaskCheckBox(
        w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
        if !entering {
@@ -97,6 +104,9 @@ func (r *TaskCheckBoxHTMLRenderer) renderTaskCheckBox(
        } else {
                _, _ = w.WriteString(`<input disabled="" type="checkbox"`)
        }
+       if n.Attributes() != nil {
+               html.RenderAttributes(w, n, TaskCheckBoxAttributeFilter)
+       }
        if r.XHTML {
                _, _ = w.WriteString(" /> ")
        } else {

Finally, the Use case

var source = []byte(`
- [ ] Task1
- [ ] Task2
`)

func main() {
	md := goldmark.New(
		goldmark.WithExtensions(extension.TaskList),
		goldmark.WithParserOptions(
			parser.WithASTTransformers(util.Prioritized(&PreserveTaskListSourcePosition{}, 1000)),
		),
	)

	doc := md.Parser().Parse(text.NewReader(source))
	md.Renderer().Render(os.Stdout, source, doc)
	// <ul>
	// <li><input disabled="" type="checkbox" data-source-position="[4,5]"> Task1</li>
	// <li><input disabled="" type="checkbox" data-source-position="[16,17]"> Task2</li>
	// </ul>

	// This piece of code should be implemented by the Server
	// to respond to a Click event on the Web page.
	// We assume that we got a request to finish
	// the task at `data-source-position="[16,17]"`.
	{
		var taskPos = `[16,17]`
		var start, stop int
		_, _ = fmt.Sscanf(taskPos, `[%d,%d]`, &start, &stop)

		_ = stop

		// mark it accomplished
		source[start] = 'x'

		doc = md.Parser().Parse(text.NewReader(source))
		md.Renderer().Render(os.Stdout, source, doc)
		// <ul>
		// <li><input disabled="" type="checkbox" data-source-position="[4,5]"> Task1</li>
		// <li><input checked="" disabled="" type="checkbox" data-source-position="[16,17]"> Task2</li>
		// </ul>

		// TODO: Save the new source and rendered content.
	}
}

Result in diff:

tests (main) → git diff 1.html
diff --git a/1.html b/1.html
index e54db85..7bc4284 100644
--- a/1.html
+++ b/1.html
@@ -1,4 +1,4 @@
 <ul>
 <li><input disabled="" type="checkbox" data-source-position="[4,5]"> Task1</li>
-<li><input disabled="" type="checkbox" data-source-position="[16,17]"> Task2</li>
+<li><input checked="" disabled="" type="checkbox" data-source-position="[16,17]"> Task2</li>
 </ul>

@yuin yuin force-pushed the master branch 5 times, most recently from e7e4266 to a590622 Compare June 14, 2024 13:05
Copy link

This PR is stale because it has been open for 180 days with no activity.

@github-actions github-actions bot added the stale label Dec 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant