]> git.r.bdr.sh - rbdr/nota.nvim/blobdiff - lua/tasks.lua
Allow next and previous
[rbdr/nota.nvim] / lua / tasks.lua
index 04e70df0e0d9f6eb8f795255e7724c2daf5248ba..33fce8f23717eaab87d9b20f83622f0e5dd338a3 100644 (file)
 -------------------------------------------------------------------------------
 -- Tools to deal with tasks
 -------------------------------------------------------------------------------
+local Tasks = {}
+local Configuration = require('configuration')
+local Notes = require('notes')
+local Util = require('util')
+local api = vim.api
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
+local unchecked_pattern = '^(%s*)%- %[ %]'
+local unchecked_important_pattern = '^(%s*)%* %[ %]'
+local checked_pattern = '^(%s*)%- %[x%]'
+local checked_important_pattern = '^(%s*)%* %[x%]'
+
+local function is_open_task(line)
+  return line:match(unchecked_pattern) or line:match(unchecked_important_pattern)
+end
+
+local function is_completed_task(line)
+  return line:match(checked_pattern) or line:match(checked_important_pattern)
+end
+
+local function is_task(line)
+    return is_open_task(line) or is_completed_task(line)
+end
+
+local function open(path)
+  local parent = Util.directory_name(path)
+  Util.ensure_directory_exists(parent)
+  vim.cmd('edit ' .. path)
+end
+
+local function open_inbox()
+  local path = Configuration.path_for(Configuration.configuration.tasks.inbox)
+  open(path)
+end
+
+local function open_someday()
+  local path = Configuration.path_for(Configuration.configuration.tasks.someday)
+  open(path)
+end
+
+local function parse_tags(tag_string)
+  local tags = {}
+  for tag in tag_string:gmatch('([^,]+)') do
+    table.insert(tags, tag)
+  end
+  return tags
+end
+
+local function table_contains_value(target_table, value)
+  for _, table_value in ipairs(target_table) do
+    if table_value == value then
+        return true
+    end
+  end
+  return false
+end
+
+local function remove_value(target_table, value)
+  for i, table_value in ipairs(target_table) do
+    if table_value == value then
+      table.remove(target_table, i)
+    end
+  end
+end
+
 -------------------------------------------------------------------------------
 -- Public Interface
 -------------------------------------------------------------------------------
 
 --- Toggles a task completion status
--- @param configuration tNotaConfiguration the plugin configuration
-function toggle(configuration)
-  error("Not yet implemented")
+function Tasks.toggle()
+    local line_number = api.nvim_win_get_cursor(0)[1]
+    local line = api.nvim_get_current_line()
+
+    if line:match(unchecked_pattern) then
+        line = line:gsub(unchecked_pattern, '%1- [x]', 1)
+    elseif line:match(unchecked_important_pattern) then
+        line = line:gsub(unchecked_important_pattern, '%1* [x]', 1)
+    elseif line:match(checked_pattern) then
+        line = line:gsub(checked_pattern, '%1- [ ]', 1)
+    elseif line:match(checked_important_pattern) then
+        line = line:gsub(checked_important_pattern, '%1* [ ]', 1)
+    else
+      api.nvim_err_writeln('Toggle completion only works on tasks')
+    end
+
+    api.nvim_buf_set_lines(0, line_number - 1, line_number, false, {line})
+end
+
+--- Toggles a task completion status
+function Tasks.toggle_importance()
+    local line_number = api.nvim_win_get_cursor(0)[1]
+    local line = api.nvim_get_current_line()
+
+    if line:match(unchecked_pattern) then
+        line = line:gsub(unchecked_pattern, '%1* [ ]', 1)
+    elseif line:match(unchecked_important_pattern) then
+        line = line:gsub(unchecked_important_pattern, '%1- [ ]', 1)
+    elseif line:match(checked_pattern) then
+        line = line:gsub(checked_pattern, '%1* [x]', 1)
+    elseif line:match(checked_important_pattern) then
+        line = line:gsub(checked_important_pattern, '%1- [x]', 1)
+    else
+      api.nvim_err_writeln('Toggle importance only works on tasks')
+    end
+
+    api.nvim_buf_set_lines(0, line_number - 1, line_number, false, {line})
 end
 
 --- Inserts a new task at the cursor location
--- @param configuration tNotaConfiguration the plugin configuration
-function insert(configuration)
-  error("Not yet implemented")
+function Tasks.insert()
+  vim.cmd('normal! o- [ ] ')
+  vim.cmd('startinsert!')
 end
 
 --- Captures a new task into the inbox
--- @param configuration tNotaConfiguration the plugin configuration
-function capture(configuration)
-  error("Not yet implemented")
+function Tasks.capture()
+  local prefix = '- [ ] '
+  open_inbox()
+  vim.cmd('normal! Go'..prefix)
+  vim.cmd('startinsert!')
 end
 
 --- Tag a task
--- @param configuration tNotaConfiguration the plugin configuration
-function tag(configuration)
-  error("Not yet implemented")
+function Tasks.tag(tag)
+  local line_number = api.nvim_win_get_cursor(0)[1]
+  local line = api.nvim_get_current_line()
+  if is_task(line) then
+    local pattern = ':(.*):$'
+    local tag_string = line:match(pattern) or ''
+    if not tag or tag == '' then
+      tag = vim.fn.input('Add new tag for task (' .. tag_string .. '): ')
+      if not tag or tag == '' then
+        return
+      end
+    end
+    local tags = parse_tags(tag_string)
+    if not table_contains_value(tags, tag) then
+      table.insert(tags, tag)
+      local new_tags = table.concat(tags, ',')
+      if line:match(pattern) then
+        line = line:gsub(pattern, ':' .. new_tags .. ':', 1)
+      else
+        line = line .. '    :' .. new_tags .. ':'
+      end
+      api.nvim_buf_set_lines(0, line_number - 1, line_number, false, {line})
+    end
+  else
+    api.nvim_err_writeln('Tagging only works on tasks')
+  end
+end
+
+--- Remove tag on a task
+function Tasks.remove_tag(tag)
+  local line_number = api.nvim_win_get_cursor(0)[1]
+  local line = api.nvim_get_current_line()
+  if is_task(line) then
+    local pattern = ':(.*):$'
+    local tag_string = line:match(pattern)
+    if not tag_string then
+      api.nvim_err_writeln('No tags to remove')
+      return
+    end
+
+    if not tag or tag == '' then
+      tag = vim.fn.input('Pick tag to remove (' .. tag_string .. '): ')
+    end
+
+    local tags = parse_tags(tag_string)
+    remove_value(tags, tag)
+    local new_tags = ''
+
+    if #tags > 0 then
+      new_tags = ':' .. table.concat(tags, ',') .. ':'
+    end
+
+    line = line:gsub(pattern, new_tags, 1):gsub('%s+$', '')
+    api.nvim_buf_set_lines(0, line_number - 1, line_number, false, {line})
+  else
+    api.nvim_err_writeln('Tagging only works on tasks')
+  end
 end
 
 --- Reschedule a task for today
--- @param configuration tNotaConfiguration the plugin configuration
-function reschedule_for_today(configuration)
-  error("Not yet implemented")
+function Tasks.reschedule_for_today()
+  local today = os.date('%Y-%m-%d')
+  Tasks.reschedule(today)
 end
 
 --- Reschedule a task for tomorrow
--- @param configuration tNotaConfiguration the plugin configuration
-function reschedule_for_tomorrow(configuration)
-  error("Not yet implemented")
+function Tasks.reschedule_for_tomorrow()
+  local tomorrow = os.date('%Y-%m-%d', os.time() + 24*60*60)
+  Tasks.reschedule(tomorrow)
 end
 
 --- Reschedule a task for someday
--- @param configuration tNotaConfiguration the plugin someday
-function reschedule_for_someday(configuration)
-  error("Not yet implemented")
+function Tasks.reschedule_for_someday()
+  Tasks.reschedule('someday')
 end
 
 --- Reschedule a task for an arbitrary date
--- @param configuration tNotaConfiguration the plugin someday
-function reschedule(configuration, new_date)
-  error("Not yet implemented")
-end
+function Tasks.reschedule(new_date)
 
--------------------------------------------------------------------------------
--- Internal Functions
--------------------------------------------------------------------------------
+  if not new_date or new_date == '' then
+    new_date = vim.fn.input('Reschedule for which date (YYYY-mm-dd): ')
+  end
+
+  if not Util.is_valid_date(new_date) and new_date ~= 'someday' then
+    api.nvim_err_writeln(new_date .. ' is not a valid date in the format YYYY-mm-dd')
+    return
+  end
+
+  local line_number = api.nvim_win_get_cursor(0)[1]
+  local buffer = api.nvim_get_current_buf()
+  local line = api.nvim_get_current_line()
+  local filename = vim.fn.expand('%:t:r')
+
+  if is_open_task(line) then
+    if Util.is_before_today(filename) and line:match(unchecked_pattern) then
+      local rescheduled_line = line:gsub(unchecked_pattern, '%1- [>' .. new_date .. ']', 1)
+      api.nvim_buf_set_lines(0, line_number - 1, line_number, false, {rescheduled_line})
+    elseif Util.is_before_today(filename) and line:match(unchecked_important_pattern) then
+      local rescheduled_line = line:gsub(unchecked_important_pattern, '%1* [>' .. new_date .. ']', 1)
+      api.nvim_buf_set_lines(0, line_number - 1, line_number, false, {rescheduled_line})
+    else
+      api.nvim_buf_set_lines(buffer, line_number - 1, line_number, false, {})
+    end
+
+    vim.cmd('write')
+
+    if new_date == 'someday' then
+      open_someday()
+    else
+      Notes.open_daily(new_date)
+    end
+
+    local line_count = api.nvim_buf_line_count(0)
+    api.nvim_buf_set_lines(0, line_count, line_count, false, {line})
+    api.nvim_win_set_cursor(0, {line_count, 0})
+  else
+    api.nvim_err_writeln('Reschedule only works on open tasks')
+  end
+end
 
+return Tasks