Skip to content

GFM Task Lists Example

This example demonstrates how to create GitHub Flavored Markdown task lists, which are essentially checkboxes that can be either checked or unchecked.

Basic Task List Example

rust
#[cfg(feature = "gfm")]
use cmark_writer::ast::{Node, ListItem, TaskListStatus};
use cmark_writer::options::WriterOptionsBuilder;
use cmark_writer::writer::CommonMarkWriter;

#[cfg(feature = "gfm")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a document with task lists
    let document = Node::Document(vec![
        Node::heading(1, vec![Node::Text("Project Tasks".to_string())]),
        
        Node::Paragraph(vec![
            Node::Text("The following tasks need to be completed:".to_string())
        ]),
        
        // Unordered list with task items
        Node::UnorderedList(vec![
            // Unchecked task
            ListItem::Task {
                status: TaskListStatus::Unchecked,
                content: vec![Node::Paragraph(vec![
                    Node::Text("Implement feature X".to_string())
                ])],
            },
            
            // Checked task
            ListItem::Task {
                status: TaskListStatus::Checked,
                content: vec![Node::Paragraph(vec![
                    Node::Text("Write documentation".to_string())
                ])],
            },
            
            // Another unchecked task
            ListItem::Task {
                status: TaskListStatus::Unchecked,
                content: vec![Node::Paragraph(vec![
                    Node::Text("Test on various platforms".to_string())
                ])],
            },
            
            // Nested tasks
            ListItem::Task {
                status: TaskListStatus::Unchecked,
                content: vec![
                    Node::Paragraph(vec![Node::Text("Deploy to production".to_string())]),
                    // Nested task list
                    Node::UnorderedList(vec![
                        ListItem::Task {
                            status: TaskListStatus::Checked,
                            content: vec![Node::Paragraph(vec![
                                Node::Text("Prepare staging environment".to_string())
                            ])],
                        },
                        ListItem::Task {
                            status: TaskListStatus::Unchecked,
                            content: vec![Node::Paragraph(vec![
                                Node::Text("Configure CI/CD pipeline".to_string())
                            ])],
                        },
                    ]),
                ],
            },
        ]),
    ]);
    
    // Configure writer with GFM task lists enabled
    let options = WriterOptionsBuilder::new()
        .gfm_tasklists(true)
        .build();
    
    let mut writer = CommonMarkWriter::with_options(options);
    writer.write(&document)?;
    let markdown = writer.into_string();
    
    // Print the generated markdown
    println!("{}", markdown);
    
    Ok(())
}

#[cfg(not(feature = "gfm"))]
fn main() {
    println!("This example requires the 'gfm' feature to be enabled");
}

With the gfm feature enabled, this produces:

markdown
# Project Tasks

The following tasks need to be completed:

- [ ] Implement feature X
- [x] Write documentation
- [ ] Test on various platforms
- [ ] Deploy to production
  - [x] Prepare staging environment
  - [ ] Configure CI/CD pipeline

Mixed List Types Example

You can mix task list items with regular list items:

rust
#[cfg(feature = "gfm")]
use cmark_writer::ast::{Node, ListItem, TaskListStatus};
use cmark_writer::options::WriterOptionsBuilder;
use cmark_writer::writer::CommonMarkWriter;

#[cfg(feature = "gfm")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a document with mixed list types
    let document = Node::Document(vec![
        Node::heading(1, vec![Node::Text("Mixed List Example".to_string())]),
        
        // Unordered list with both task and regular items
        Node::UnorderedList(vec![
            // Regular list item
            ListItem::Unordered {
                content: vec![Node::Paragraph(vec![
                    Node::Text("This is a regular list item".to_string())
                ])],
            },
            
            // Task list item
            ListItem::Task {
                status: TaskListStatus::Unchecked,
                content: vec![Node::Paragraph(vec![
                    Node::Text("This is a task list item".to_string())
                ])],
            },
            
            // Another regular list item
            ListItem::Unordered {
                content: vec![Node::Paragraph(vec![
                    Node::Text("Another regular item".to_string())
                ])],
            },
            
            // Completed task
            ListItem::Task {
                status: TaskListStatus::Checked,
                content: vec![Node::Paragraph(vec![
                    Node::Text("A completed task".to_string())
                ])],
            },
        ]),
        
        // Also works with ordered lists
        Node::heading(2, vec![Node::Text("With Ordered Lists".to_string())]),
        
        Node::OrderedList {
            start: 1,
            items: vec![
                // Regular ordered item
                ListItem::Ordered {
                    number: None,
                    content: vec![Node::Paragraph(vec![
                        Node::Text("First ordered item".to_string())
                    ])],
                },
                
                // Task item in ordered list
                ListItem::Task {
                    status: TaskListStatus::Unchecked,
                    content: vec![Node::Paragraph(vec![
                        Node::Text("Task in ordered list".to_string())
                    ])],
                },
                
                // Another regular ordered item
                ListItem::Ordered {
                    number: None,
                    content: vec![Node::Paragraph(vec![
                        Node::Text("Another ordered item".to_string())
                    ])],
                },
            ],
        },
    ]);
    
    // Configure writer with GFM task lists enabled
    let options = WriterOptionsBuilder::new()
        .gfm_tasklists(true)
        .build();
    
    let mut writer = CommonMarkWriter::with_options(options);
    writer.write(&document)?;
    let markdown = writer.into_string();
    
    // Print the generated markdown
    println!("{}", markdown);
    
    Ok(())
}

With GFM enabled, this produces:

markdown
# Mixed List Example

- This is a regular list item
- [ ] This is a task list item
- Another regular item
- [x] A completed task

## With Ordered Lists

1. First ordered item
2. [ ] Task in ordered list
3. Another ordered item

Task List Best Practices

  1. Use task lists for actionable items: Task lists are best for tracking to-do items, rather than general information
  2. Keep task descriptions concise: Brief, clear descriptions work best in task lists
  3. Use nesting for hierarchical tasks: Group related sub-tasks under parent tasks
  4. Combine with other Markdown elements: Task list descriptions can include other formatting like emphasis or links
  5. Consider state representation: Checked items typically represent completed tasks, use them consistently

Implementation Notes

When implementing task lists:

  • Remember to enable the gfm feature in your Cargo.toml
  • Use the WriterOptionsBuilder to enable GFM task lists
  • Task lists can be nested within other lists
  • Task items work in both ordered and unordered lists