Post

Manage Markdown tasks in Neovim similar to Obsidian | Telescope to List Completed and Pending Tasks

Are you an Obsidian user and miss the way to manage tasks in Obsidian and would like to have something similar in Neovim? You don't need a dedicated plugin, I created a few keymaps for this and I'll share them below

Manage Markdown tasks in Neovim similar to Obsidian | Telescope to List Completed and Pending Tasks

Table of contents

YouTube video

Pre-requisites


  • If you don’t want to watch the video above, and you already have your own neovim config, and want to quickly get started and follow along in this tutorial
  • Run the git clone commands below to clone my dotfiles in your .config directory and we will run my config below
1
2
mkdir -p ~/.config
git clone git@github.com:linkarzu/dotfiles-latest ~/.config/linkarzu/dotfiles-latest
  • Open the newly downloaded neobean config with:
1
NVIM_APPNAME=linkarzu/dotfiles-latest/neovim/neobean nvim
  • You can create an alias in your .bashrc or .zshrc file to run my config
1
alias neobean='NVIM_APPNAME=linkarzu/dotfiles-latest/neovim/neobean nvim'
  • Then to run this config, just run neobean


  • If you don’t even have neovim yet, of course you will need to install it first, so if you’re just getting started, I have a video for you:

If you like my content, and want to support me

  • I create and edit my videos in an M1 mac mini, and it’s starting to stay behind in the editing side of things, tends to slow me down a bit, I’d like to upgrade the machine I use for all my videos to a mac mini with these specs:
    • Apple M4 Pro chip with 14‑core CPU, 20‑core GPU, 16-core Neural Engine
    • 24GB unified memory
    • 1TB SSD storage
    • 10 Gigabit Ethernet
  • If you want to help me reach my goal, you can donate here

Image

Discord server

Image

Follow me on social media

  • The following links will be in the YouTube video description:
    • Each one of the videos shown
    • A link to this blogpost

How do you manage your passwords?

Image

How I managed tasks in obsidian?

  • In obsidian I used to manage tasks as you see on the image below, notice that when I mark a task as done it shows me the date and time in which it was completed
  • I wanted the same in Neovim, so I created a few keymaps that allow me to mange tasks

Image How I used to manage tasks in Obsidian

How I manage tasks in Neovim

  • This is the end result in Neovim, notice that when I mark a task as done, it moves it to the Completed Tasks section
  • Also notice that the date and time are added to the completed tasks, that’s configurable and you can modify it to your liking
  • Also notice that I add the done: tag at the beginning of a completed task, I use this tag when searching for completed or incomplete tasks

Image How I manage tasks in Neovim

How I mark tasks as done in Neovim

  • In the video I demo how I complete tasks by pressing alt+x and how they are automatically moved to the Completed Tasks section
  • In the video I also demonstrate how I use the app you see on the right hand side, where I keep my daily tasks, it’s called skitty-notes
  • It’s basically a sticky notes app, but the main reason I came up with it, is that it allows me to use vim motions to edit text
  • It’s basically my neovim configuration (modified a bit) running inside kitty
  • I have a full tutorial on how to set it up, you can find it here:

How I untoggle tasks

  • Sometimes you need to untoggle a finished task, I mean, mark it again as not done, so if you go to the Completed Tasks section, and you press alt+x in one of the tasks, it’s going to replace the label
  • Notice in the image below that the first 2 tasks in that section show as untoggled, notice the date was automatically removed
  • You can toggle them as completed again, and the new date and time will be added

Image Untoggling a task with Alt+x

List pending tasks

  • A lot of the times, you want to see all the pending tasks, not only in a single file, but in the current working directory in which you opened Neovim
  • So I created the keymap leader+tt
  • Notice in the image below, I have some tasks that are not completed in my blogpost

Image List of uncompleted tasks in the current working directory

  • I’ll show you how I configured this keymap below

List completed tasks

  • For this I created another keymap, leader+tc as seen on the image below

Image List of completed tasks in the current working directory

  • I’ll show you how I configured this keymap below

Why did I switch from Obsidian to Neovim?

Keymap to create a task

  • I create tasks with Alt+l
  • So if I’m on a blank line and press that, it will add - [ ] at the beginning of the line, which is basically a task
  • I also use the plugin: bullets-vim/bullets.vim
    • This plugin is used to create a task on the line below if I hit “enter” at the end of a task
    • I also use this plugin to create bulletpoints across all my different files
  • If you would like to learn about my markdown setup in detail, I would highly recommend you to check my markdown workflow video, the 2025 version:

Configure keymaps

Alt+x to toggle tasks

  • This can be found in my keymaps.lua file which is in my dotfiles, link here: config/keymaps.lua
  • There you’ll be able to find the most up to date version of this keymap, as of Feb 11th 2025 the keymaps tarts here (If you don’t understand where the keymap starts and ends, watch the video):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- If there is no `untoggled` or `done` label on an item, mark it as done
-- and move it to the "## completed tasks" markdown heading in the same file, if
-- the heading does not exist, it will be created, if it exists, items will be
-- appended to it at the top lamw25wmal
--
-- If an item is moved to that heading, it will be added the `done` label
vim.keymap.set("n", "<M-x>", function()
  -- Customizable variables
  -- NOTE: Customize the completion label
  local label_done = "done:"
  -- NOTE: Customize the timestamp format
  local timestamp = os.date("%y%m%d-%H%M")
  -- local timestamp = os.date("%y%m%d")
  -- NOTE: Customize the heading and its level
  local tasks_heading = "## Completed tasks"
  -- Save the view to preserve folds
  vim.cmd("mkview")
  .
  .
  .
  .
  • Notice above I left some notes on the things that you can customize for the keymap
  • The configuration for this keymap is quite long, so go and get it directly from my dotfiles also, while you’re there:
    • ⭐⭐⭐⭐ Remember to star my dotfiles ⭐⭐⭐⭐

Alt+l to create task

  • This is the keymap I’m used when I’m on a blank line and I need to create a new tasks, so that I don’t have to manually type - [ ]
  • This one is shorter (that’s what she said), so you can see it below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- Crate task or checkbox lamw25wmal
-- These are marked with <leader>x using bullets.vim
-- I used <C-l> before, but that is used for pane navigation
vim.keymap.set({ "n", "i" }, "<M-l>", function()
  -- Get the current line and cursor position
  local line = vim.api.nvim_get_current_line()
  local cursor = vim.api.nvim_win_get_cursor(0)
  -- Check if the line starts with a bullet or "- ", and remove it
  local updated_line = line:gsub("^%s*[-*]%s*", "", 1)
  -- Update the line
  vim.api.nvim_set_current_line(updated_line)
  -- Move the cursor back to its original position
  vim.api.nvim_win_set_cursor(0, { cursor[1], #updated_line })
  -- Insert the checkbox
  vim.api.nvim_put({ "- [ ] " }, "c", true, true)
end, { desc = "[P]Toggle checkbox" })

Leader+tt and leader+tc to list incomplete and completed tasks

  • I used telescope when I recorded this video, but I recently switched over to the Snacks Picker by Folke, in the video I demo the telescope keymaps, here’s the code for Leader+tt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Iterate through incomplete tasks in telescope
-- You can confirm in your terminal lamw25wmal with:
-- rg "^\s*-\s\[ \]" test-markdown.md
vim.keymap.set("n", "<leader>tt", function()
  require("telescope.builtin").grep_string(require("telescope.themes").get_ivy({
    prompt_title = "Incomplete Tasks",
    -- search = "- \\[ \\]", -- Fixed search term for tasks
    -- search = "^- \\[ \\]", -- Ensure "- [ ]" is at the beginning of the line
    search = "^\\s*- \\[ \\]", -- also match blank spaces at the beginning
    search_dirs = { vim.fn.getcwd() }, -- Restrict search to the current working directory
    use_regex = true, -- Enable regex for the search term
    initial_mode = "normal", -- Start in normal mode
    layout_config = {
      preview_width = 0.5, -- Adjust preview width
    },
    additional_args = function()
      return { "--no-ignore" } -- Include files ignored by .gitignore
    end,
  }))
end, { desc = "[P]Search for incomplete tasks" })
  • The same thing for the leader+tc keymaps, here’s the code below, but keep in mind that I don’t use this anymore, as I migrated from telescope
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- Iterate through completed tasks in telescope lamw25wmal
vim.keymap.set("n", "<leader>tc", function()
  require("telescope.builtin").grep_string(require("telescope.themes").get_ivy({
    prompt_title = "Completed Tasks",
    -- search = [[- \[x\] `done:]], -- Regex to match the text "`- [x] `done:"
    -- search = "^- \\[x\\] `done:", -- Matches lines starting with "- [x] `done:"
    search = "^\\s*- \\[x\\] `done:", -- also match blank spaces at the beginning
    search_dirs = { vim.fn.getcwd() }, -- Restrict search to the current working directory
    use_regex = true, -- Enable regex for the search term
    initial_mode = "normal", -- Start in normal mode
    layout_config = {
      preview_width = 0.5, -- Adjust preview width
    },
    additional_args = function()
      return { "--no-ignore" } -- Include files ignored by .gitignore
    end,
  }))
end, { desc = "[P]Search for completed tasks" })

Start your 14 day FREE trial

Start your 14 day FREE trial

Image

This post is licensed under CC BY 4.0 by the author.