]> git.r.bdr.sh - rbdr/nota.nvim/commitdiff
Add minimum workable functionality
authorRuben Beltran del Rio <redacted>
Sat, 2 Mar 2024 21:49:47 +0000 (22:49 +0100)
committerRuben Beltran del Rio <redacted>
Sat, 2 Mar 2024 21:49:47 +0000 (22:49 +0100)
README.md
lua/configuration.lua
lua/keybinds.lua
lua/learning.lua
lua/nota.lua [new file with mode: 0644]
lua/notes.lua
lua/plan.lua
lua/task_views.lua
lua/tasks.lua
lua/util.lua [new file with mode: 0644]
plugin/nota.lua

index 520345ca130c59495da9f5021c72f4e1fd951ffb..8106dfee6b59da15f675e8bf361e23a93827bfd9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ require('lazy').setup({
 })
 ```
 
+This plugin has an optional dependency: [fzf-lua][fzf-lua].
+
 By default, nota adds keybinds that might not suit your style or conflict with other plugins. You can disable them with the `default_keybinds` option.
 
 ```lua
@@ -77,15 +79,17 @@ If the default directories don't work for you, you can override them as well:
 - `<leader>om`, `:NotaOpenMonthlyNote`, Opens this month's monthly note.
 - `<leader>os`, `:NotaOpenSeasonalNote`, Opens this season's seasonal note.
 - `<leader>oy`, `:NotaOpenYearlyNote`, Opens this year's yearly note.
-- `<leader>on`, `:NotaOpenNote`, Opens an arbitrary note.
+- `<leader>on`, `:NotaOpenNote`, Opens an arbitrary note. (Requires [fzf-lua][fzf-lua])
 
 ### Task Views
 - `<leader>oa`, `:NotaOpenAgenda`, Opens the agenda window with this week's tasks.
 - `<leader>oo`, `:NotaOpenOpen`, Opens a window that lets you navigate through all open tasks.
-- `<leader>oj`, `:NotaOpenJournal`, Opens a window that lets you search completed tasks to find journal entries.
+- `<leader>oO`, `:NotaOpenOpenImportant`, Opens a window that lets you navigate through all open important tasks.
+- `<leader>oj`, `:NotaOpenJournal`, Opens a window that lets you search completed tasks to find journal entries. (Requires [fzf-lua][fzf-lua] and ripgrep)
 
 ### Task Handling Commands
 - `<leader>t`, `:NotaToggleTask`, Toggles completion state of the task under the cursor.
+- `<leader>st`, `:NotaToggleTaskImportance`, Toggles importance state of the task under the cursor. (- [ ] is a regular task, * [ ] is an important task)
 - `<leader>it`, `:NotaInsertTask`, Inserts a task at cursor location.
 - `<leader>ct`, `:NotaCaptureTask`, Captures a new task into the inbox.
 - `<leader>Tt`, `:NotaTagTask`, Adds a tag to the current task.
@@ -101,3 +105,5 @@ If the default directories don't work for you, you can override them as well:
 ### Plan Handling Commands
 - `<leader>op`, `:NotaOpenPlan`, Opens the current plan file.
 - `<leader>cp`, `:NotaCapturePlan`, Captures a new plan and archives the current one.
+
+[fzf-lua]: https://github.com/ibhagwan/fzf-lua
index 14e8591a87c88b1ca41d8a5cea53aad722210c12..f251eb9344b036e405b744cfb688e6e5dcf2182c 100644 (file)
@@ -1,3 +1,14 @@
+-------------------------------------------------------------------------------
+-- Configuration for nota
+-------------------------------------------------------------------------------
+local Configuration = {}
+
+local Util = require('util')
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
+local fs = vim.loop
+
 local defaults = {
   nota_home = '~/.local/share/nota',          -- Root location in which to store all notes
   default_keybinds = true,                    -- Whether or not to set the default keybinds
@@ -22,7 +33,7 @@ local defaults = {
   },
   learning  = {
       learning_file = 'learning.md',            -- Location of the file in which to store learning entries, relative to nota_home
-      prefix = '%Y-%x-%d: '                     -- Prefix to add when capturing learning entries
+      prefix = '%Y-%m-%d: '                     -- Prefix to add when capturing learning entries
   },
   plan = {
       archive = 'plans',                        -- Location of the plan archives.
@@ -33,7 +44,7 @@ local defaults = {
 -- Recursively extends a table with another
 local function extend(target, extender)
   for key, value in pairs(extender) do
-    if type(target[key]) == "table" and type(value) == "table" then
+    if type(target[key]) == 'table' and type(value) == 'table' then
       extend(target[key], value)
     else
       target[key] = value
@@ -41,6 +52,39 @@ local function extend(target, extender)
   end
 end
 
-function configure(user_configuration)
-  return extend(defaults, extender)
+-------------------------------------------------------------------------------
+-- Public Interface
+-------------------------------------------------------------------------------
+
+Configuration.configuration = {}
+extend(Configuration.configuration, defaults)
+
+--- Extends configuration with another configuration
+function Configuration.configure(configuration)
+  configuration = configuration or {}
+  extend(Configuration.configuration, configuration)
+end
+
+--- Gets expanded paths relative to nota_home
+-- @param path string the relative path to expand
+function Configuration.path_for(path)
+  if not path then
+    return vim.fn.expand(Configuration.configuration.nota_home)
+  end
+  return Util.join(vim.fn.expand(Configuration.configuration.nota_home), path)
+end
+
+--- Loads a template by template name
+function Configuration.load_template(type)
+  local template_path = Configuration.path_for(Configuration.configuration.templates[type])
+
+  local template_file = io.open(template_path, 'r')
+  if not template_file then
+      return ''
+  end
+  local content = template_file:read('*a')
+  template_file:close()
+  return content
 end
+
+return Configuration
index de3d9ecabc5828ae8da7765ab25a3ab4fd990199..bbf160b34f84f3a7249ebff06ceab381ed5194a6 100644 (file)
@@ -1,13 +1,14 @@
 -------------------------------------------------------------------------------
 -- Sets the default keybinds
 -------------------------------------------------------------------------------
+local Keybinds = {}
 -------------------------------------------------------------------------------
 -- Public Interface
 -------------------------------------------------------------------------------
 
 --- Sets the default keybinds
 -- @param configuration tNotaConfiguration the plugin configuration
-function bind()
+function Keybinds.bind()
   local api = vim.api
 
   api.nvim_set_keymap('n', '<leader>od', '<cmd>NotaOpenDailyNote<CR>', { noremap = true, silent = true })
@@ -19,9 +20,11 @@ function bind()
 
   api.nvim_set_keymap('n', '<leader>oa', '<cmd>NotaOpenAgenda<CR>', { noremap = true, silent = true })
   api.nvim_set_keymap('n', '<leader>oo', '<cmd>NotaOpenOpen<CR>', { noremap = true, silent = true })
+  api.nvim_set_keymap('n', '<leader>oO', '<cmd>NotaOpenOpenImportant<CR>', { noremap = true, silent = true })
   api.nvim_set_keymap('n', '<leader>oj', '<cmd>NotaOpenJournal<CR>', { noremap = true, silent = true })
 
   api.nvim_set_keymap('n', '<leader>t', '<cmd>NotaToggleTask<CR>', { noremap = true, silent = true })
+  api.nvim_set_keymap('n', '<leader>st', '<cmd>NotaToggleTaskImportance<CR>', { noremap = true, silent = true })
   api.nvim_set_keymap('n', '<leader>it', '<cmd>NotaInsertTask<CR>', { noremap = true, silent = true })
   api.nvim_set_keymap('n', '<leader>ct', '<cmd>NotaCaptureTask<CR>', { noremap = true, silent = true })
   api.nvim_set_keymap('n', '<leader>Tt', '<cmd>NotaTagTask<CR>', { noremap = true, silent = true })
@@ -36,3 +39,5 @@ function bind()
   api.nvim_set_keymap('n', '<leader>op', '<cmd>NotaOpenPlan<CR>', { noremap = true, silent = true })
   api.nvim_set_keymap('n', '<leader>cp', '<cmd>NotaCapturePlan<CR>', { noremap = true, silent = true })
 end
+
+return Keybinds
index 0994b31dbd4ab4ce863c7acf71791ef12144a964..b730c6a082d05c27dc2bffcdb3dbeb83a67a9956 100644 (file)
@@ -1,22 +1,31 @@
 -------------------------------------------------------------------------------
 -- Tools to deal with the learning file
 -------------------------------------------------------------------------------
+local Learning = {}
+
+local Configuration = require('configuration')
+local Util = require('util')
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
 -------------------------------------------------------------------------------
 -- Public Interface
 -------------------------------------------------------------------------------
 
 --- Opens the learning file
--- @param configuration tNotaConfiguration the plugin configuration
-function open_learning(configuration)
-  error("Not yet implemented")
+function Learning.open()
+  local learning_path = Configuration.path_for(Configuration.configuration.learning.learning_file)
+  local learning_parent = Util.directory_name(learning_path)
+  Util.ensure_directory_exists(learning_parent)
+  vim.cmd('edit ' .. learning_path)
 end
 
 --- Capture a learning entry
--- @param configuration tNotaConfiguration the plugin configuration
-function capture_learning(configuration)
-  error("Not yet implemented")
+function Learning.capture()
+  local prefix = os.date(Configuration.configuration.learning.prefix)
+  Learning.open()
+  vim.cmd('normal! ggO'..prefix)
+  vim.cmd('startinsert!')
 end
 
--------------------------------------------------------------------------------
--- Internal Functions
--------------------------------------------------------------------------------
+return Learning
diff --git a/lua/nota.lua b/lua/nota.lua
new file mode 100644 (file)
index 0000000..3043d77
--- /dev/null
@@ -0,0 +1,7 @@
+local Nota = {}
+
+function Nota.setup(user_configuration)
+  configuration = require('configuration').configure(user_configuration)
+end
+
+return Nota
index acffc2585fb16aada100026f1f03ad78026ee71b..008baac1c148fb5d1df182a3974061a42a53ba32 100644 (file)
@@ -1,46 +1,84 @@
 -------------------------------------------------------------------------------
 -- Tools to deal with notes
 -------------------------------------------------------------------------------
+local Notes = {}
+
+local Util = require('util')
+local Configuration = require('configuration')
+local api = vim.api
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
+local function open_or_create_from_template(type, file_path)
+
+  local journal_file = io.open(file_path, 'r')
+  if not journal_file then
+    local template_contents = Configuration.load_template(type)
+
+    journal_file = io.open(file_path, 'w')
+    journal_file:write(template_contents)
+    journal_file:close()
+  end
+  vim.cmd('edit ' .. file_path)
+end
+
+local function open_periodic_note(type, filename)
+  local file_directory_path = Configuration.path_for(Configuration.configuration.periodic_locations[type])
+
+  Util.ensure_directory_exists(file_directory_path)
+  local file_path = Util.join(file_directory_path, filename)
+
+  open_or_create_from_template(type, file_path)
+end
+
 -------------------------------------------------------------------------------
 -- Public Interface
 -------------------------------------------------------------------------------
 
 --- Opens the daily note
--- @param configuration tNotaConfiguration the plugin configuration
-function open_daily(configuration)
-  error("Not yet implemented")
+function Notes.open_daily()
+  local filename = os.date('%Y-%m-%d') .. '.md'
+  open_periodic_note('daily', filename)
 end
 
 --- Opens the weekly note
--- @param configuration tNotaConfiguration the plugin configuration
-function open_weekly(configuration)
-  error("Not yet implemented")
+function Notes.open_weekly()
+  local filename = os.date('%Y-w%V')
+  open_periodic_note('weekly', filename)
 end
 
 --- Opens the monthly note
--- @param configuration tNotaConfiguration the plugin configuration
-function open_monthly(configuration)
-  error("Not yet implemented")
+function Notes.open_monthly()
+  local filename = os.date('%Y-%m') .. '.md'
+  open_periodic_note('monthly', filename)
 end
 
 --- Opens the seasonal note
--- @param configuration tNotaConfiguration the plugin configuration
-function open_seasonal(configuration)
-  error("Not yet implemented")
+function Notes.open_seasonal()
+  local year = os.date('%Y')
+  local month = tonumber(os.date('%m'))
+  local season = math.ceil(month / 3)
+
+  local filename = year .. '-s' .. season .. '.md'
+  open_periodic_note('seasonal', filename)
 end
 
 --- Opens the yearly note
--- @param configuration tNotaConfiguration the plugin configuration
-function open_yearly(configuration)
-  error("Not yet implemented")
+function Notes.open_yearly()
+  local filename = os.date('%Y') .. '.md'
+  open_periodic_note('yearly', filename)
 end
 
 --- Opens an arbitrary note
--- @param configuration tNotaConfiguration the plugin configuration
-function open_note(configuration)
-  error("Not yet implemented")
+function Notes.open()
+  local success, module = pcall(require, 'fzf-lua')
+  if success then
+    local notes_path = Configuration.path_for()
+    Util.ensure_directory_exists(notes_path)
+    module.files({ cwd = notes_path })
+  else
+    api.nvim_err_writeln('This feature requires optional dependency fzf-lua')
+  end
 end
 
--------------------------------------------------------------------------------
--- Internal Functions
--------------------------------------------------------------------------------
+return Notes
index e78ee00f9534c53e1eb9bca9f3c172a3fcf7c636..477ff9a0a35677bf16de33048ae17e0661aa7b37 100644 (file)
@@ -1,23 +1,67 @@
 -------------------------------------------------------------------------------
 -- Tools to deal with the plan file
 -------------------------------------------------------------------------------
+local Plan = {}
+
+local Configuration = require('configuration')
+local Util = require('util')
 -------------------------------------------------------------------------------
--- Public Interface
+-- Internal Functions
 -------------------------------------------------------------------------------
+local function open_or_create_from_template(file_path, force_template)
 
---- Opens the active plan file
--- @param configuration tNotaConfiguration the plugin configuration
-function open_plan(configuration)
-  error("Not yet implemented")
+  local plan_file = io.open(file_path, 'r')
+  if force_template or not plan_file then
+    local template_contents = Configuration.load_template('plan')
+    local date = os.date('%Y-%m-%d')
+
+    template_contents = template_contents .. '[' .. date .. ']\n'
+    plan_file = io.open(file_path, 'w')
+    plan_file:write(template_contents)
+    plan_file:close()
+  end
+  vim.cmd('edit ' .. file_path)
 end
 
---- Capture a new plan file and archive the current one
--- @param configuration tNotaConfiguration the plugin configuration
-function capture_plan(configuration)
-  error("Not yet implemented")
+local function copy(plan_path, archive_path)
+  local plan_file = io.open(plan_path, 'r')
+  local archive_file = io.open(archive_path, 'wb')
+  local plan_content = plan_file:read('*a')
+  archive_file:write(plan_content) -- Write the content to the target file
+
+  plan_file:close()
+  archive_file:close()
 end
 
 -------------------------------------------------------------------------------
--- Internal Functions
+-- Public Interface
 -------------------------------------------------------------------------------
 
+--- Opens the active plan file
+function Plan.open(force_template)
+  local plan_path = vim.fn.expand(Configuration.configuration.plan.plan_file)
+  local plan_parent = Util.directory_name(plan_path)
+  Util.ensure_directory_exists(plan_parent)
+  open_or_create_from_template(plan_path, force_template)
+end
+
+--- Capture a new plan file and archive the current one
+function Plan.capture()
+  local fs = vim.loop
+
+  local plan_path = vim.fn.expand(Configuration.configuration.plan.plan_file)
+  local plan_parent = Util.directory_name(plan_path)
+  Util.ensure_directory_exists(plan_parent)
+
+  local archive_path = Configuration.path_for(Configuration.configuration.plan.archive)
+  Util.ensure_directory_exists(archive_path)
+
+  if fs.fs_stat(plan_path) then
+    local archive_filename = os.date('%Y-%m-%d') .. '.md'
+    copy(plan_path, Util.join(archive_path, archive_filename))
+  end
+
+  Plan.open(true)
+end
+
+return Plan
index f763996e1055fcdba314ec1e75080146c9c58db7..ee76ad3218dea9c8c74f1d2548fdc57b6a6f9fb0 100644 (file)
 -------------------------------------------------------------------------------
 -- Tools to deal with task views
 -------------------------------------------------------------------------------
+local TaskViews = {}
+local Util = require('util')
+local Configuration = require('configuration')
+local api = vim.api
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
+local function get_this_weeks_files()
+  local today = os.time()
+  local day_of_week = os.date('*t', today).wday
+  local week_start = today - (day_of_week - 2) * 86400
+  local filenames = {}
+
+  for i = 0, 6 do
+    local date = os.date('*t', week_start + i * 86400)
+    table.insert(filenames, string.format('%04d-%02d-%02d.md', date.year, date.month, date.day))
+  end
+
+  return filenames
+end
+
+local function find_tasks(completed, important)
+  local file_directory_path = Configuration.path_for(Configuration.configuration.periodic_locations.daily)
+
+  local completed_fragment = '(\\s|x)'
+  if completed == 1 then
+    completed_fragment = 'x'
+  elseif completed == 0 then
+    completed_fragment = '\\s'
+  end
+
+  local important_fragment = '(\\-|\\*)'
+  if important == 1 then
+    important_fragment = '\\*'
+  elseif important == 0 then
+    important_fragment = '\\-'
+  end
+
+
+  local pattern = '^\\s*' .. important_fragment .. '\\s\\[' .. completed_fragment .. ']'
+
+  local command = string.format('rg --vimgrep \'%s\' \'%s\'', pattern, file_directory_path)
+  local results = vim.fn.systemlist(command)
+
+  if vim.v.shell_error == 0 then
+    local items = {}
+    for _, line in ipairs(results) do
+      local filename, lnum, col, text = line:match('([^:]+):(%d+):(%d+):(.*)')
+            table.insert(items, {
+                filename = filename,
+                lnum = tonumber(lnum),
+                col = tonumber(col),
+                text = text,
+            })
+    end
+
+    -- Set location list for the current window and open it
+    vim.fn.setloclist(0, items)
+    vim.cmd('lopen')
+  end
+end
+
+local function populate_quicklist_with_files(filenames)
+  local uv = vim.loop
+  local items = {}
+  local task_pattern = '^%s*%- %[[ ]?x?%]'
+  local important_task_pattern = '^%s*%* %[[ ]?x?%]'
+
+  local file_directory_path = Configuration.path_for(Configuration.configuration.periodic_locations.daily)
+  Util.ensure_directory_exists(file_directory_path)
+
+  for _, filename in ipairs(filenames) do
+    local daily_note = Util.join(file_directory_path, filename)
+    local stat = uv.fs_stat(daily_note)
+
+    if stat then -- File exists
+      local file, err = io.open(daily_note, 'r')
+      if file then
+        local set_header = 0
+        local line_number = 0
+        for line in file:lines() do
+          line_number = line_number + 1
+          if line:match(task_pattern) or line:match(important_task_pattern) then
+            if set_header == 0 then
+              local header = string.sub(filename:match('([^/\\]+)$'), 1, -4)
+              table.insert(items, {filename = '', lnum = 0, text = header})
+              set_header = 1
+            end
+            table.insert(items, {filename = daily_note, text = line, lnum = line_number})
+          end
+        end
+        file:close()
+      end
+    end
+  end
+  vim.fn.setloclist(0, {}, ' ', {title = 'Weekly Tasks', items = items})
+  vim.cmd('lopen')
+end
+
 -------------------------------------------------------------------------------
 -- Public Interface
 -------------------------------------------------------------------------------
 
 --- Opens the agenda view to show tasks
--- @param configuration tNotaConfiguration the plugin configuration
-function open_agenda(configuration)
-  error("Not yet implemented")
+function TaskViews.open_agenda()
+  local week_filenames = get_this_weeks_files()
+  populate_quicklist_with_files(week_filenames)
 end
 
 --- Opens the view to show open tasks
--- @param configuration tNotaConfiguration the plugin configuration
-function open_open(configuration)
-  error("Not yet implemented")
+function TaskViews.open_open()
+  find_tasks(0)
+end
+
+--- Opens the view to show open important tasks
+function TaskViews.open_open_important()
+  find_tasks(0, 1)
 end
 
 --- Opens the view to search the journal
--- @param configuration tNotaConfiguration the plugin configuration
-function open_journal(configuration)
-  error("Not yet implemented")
+function TaskViews.open_journal()
+  local pattern = '^\\s*(\\*|\\-)\\s\\[x]'
+  -- local pattern = 'hell'
+  local success, module = pcall(require, 'fzf-lua')
+  if success then
+    local notes_path = Configuration.path_for()
+    Util.ensure_directory_exists(notes_path)
+    module.files({ cwd = notes_path, cmd = 'rg --line-number --no-heading -- \'' .. pattern ..'\''})
+  else
+    api.nvim_err_writeln('This feature requires optional dependency fzf-lua')
+  end
 end
 
--------------------------------------------------------------------------------
--- Internal Functions
--------------------------------------------------------------------------------
+return TaskViews
index 04e70df0e0d9f6eb8f795255e7724c2daf5248ba..618a688dfa60a2e99c5880152577bc08589ee3c9 100644 (file)
@@ -1,59 +1,96 @@
 -------------------------------------------------------------------------------
 -- Tools to deal with tasks
 -------------------------------------------------------------------------------
+local Tasks = {}
+local Configuration = require('configuration')
+local api = vim.api
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
 -------------------------------------------------------------------------------
 -- 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()
+
+    local unchecked_pattern = '^(%s*)%- %[ %]'
+    local unchecked_important_pattern = '^(%s*)%* %[ %]'
+    local checked_pattern = '^(%s*)%- %[x%]'
+    local checked_important_pattern = '^(%s*)%* %[x%]'
+
+    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)
+    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()
+
+    local unchecked_pattern = '^(%s*)%- %[ %]'
+    local unchecked_important_pattern = '^(%s*)%* %[ %]'
+    local checked_pattern = '^(%s*)%- %[x%]'
+    local checked_important_pattern = '^(%s*)%* %[x%]'
+
+    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)
+    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()
+  error('Not yet implemented')
 end
 
 --- Tag a task
--- @param configuration tNotaConfiguration the plugin configuration
-function tag(configuration)
-  error("Not yet implemented")
+function Tasks.tag()
+  error('Not yet implemented')
 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()
+  error('Not yet implemented')
 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()
+  error('Not yet implemented')
 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()
+  error('Not yet implemented')
 end
 
 --- Reschedule a task for an arbitrary date
--- @param configuration tNotaConfiguration the plugin someday
-function reschedule(configuration, new_date)
-  error("Not yet implemented")
+function Tasks.reschedule(new_date)
+  error('Not yet implemented')
 end
 
--------------------------------------------------------------------------------
--- Internal Functions
--------------------------------------------------------------------------------
-
+return Tasks
diff --git a/lua/util.lua b/lua/util.lua
new file mode 100644 (file)
index 0000000..2636e7c
--- /dev/null
@@ -0,0 +1,48 @@
+-------------------------------------------------------------------------------
+-- Utilities shared by all modules. Categorize better if > 5 public functions
+-------------------------------------------------------------------------------
+local Util = {}
+-------------------------------------------------------------------------------
+-- Internal Functions
+-------------------------------------------------------------------------------
+local fs = vim.loop
+
+local function create_directory(directory)
+  local stat = fs.fs_stat(directory)
+  if stat then
+    if stat.type == 'directory' then
+      return
+    else
+      error('Expected directory but found file at: ' .. directory)
+    end
+  else
+    local parent = directory:match('(.+)/[^/]*$')
+    if parent then
+      create_directory(parent)
+    end
+    local success, error = fs.fs_mkdir(directory, 493)
+    if not success then
+      error('Could not directory at: ' .. directory .. '. ' .. error)
+    end
+  end
+end
+-------------------------------------------------------------------------------
+-- Public Interface
+-------------------------------------------------------------------------------
+function Util.ensure_directory_exists(path)
+  local full_path = vim.fn.expand(path)
+  create_directory(path)
+end
+
+function Util.join(...)
+  local separator = '/'
+  local paths = {...}
+  return table.concat(paths, separator):gsub(separator..'+', separator)
+end
+
+function Util.directory_name(file_path)
+  local pattern = '(.+)/[^/]+$'
+  return file_path:match(pattern)
+end
+
+return Util
index 4e621706e1b8243a748f848456a18d26769f0cf1..9c6e419bb8c3a71f8577a026667841e61e41278e 100644 (file)
 
 --- Sets up the Nota commands and keybins
 -- @param user_configuration tNotaConfiguration the user provided configuration for the plugin
-function setup(user_configuration)
-  local api = vim.api
-  local fs = vim.loop
-  local fn = vim.fs
+local api = vim.api
+local fs = vim.loop
+local fn = vim.fs
 
-  if not api.nvim_create_user_command then
-    return
-  end
+if not api.nvim_create_user_command then
+  return
+end
 
-  local command = api.nvim_create_user_command
+local command = api.nvim_create_user_command
 
-  local configuration = require('configuration').configure(user_configuration)
+local configuration = require('configuration').configuration
 
-  if configuration.default_keybinds then
-    local configuration = require('keybinds').bind()
-  end
+if configuration.default_keybinds then
+  require('keybinds').bind()
+end
 
-  -- Note Handling Commands
-  command('NotaOpenDailyNote', function() require('notes').open_daily(configuration) end, { nargs = 0 })
-  command('NotaOpenWeeklyNote', function() require('notes').open_weekly(configuration) end, { nargs = 0 })
-  command('NotaOpenMonthlyNote', function() require('notes').open_monthly(configuration) end, { nargs = 0 })
-  command('NotaOpenSeasonalNote', function() require('notes').open_seasonal(configuration) end, { nargs = 0 })
-  command('NotaOpenYearlyNote', function() require('notes').open_yearly(configuration) end, { nargs = 0 })
-  command('NotaOpenNote', function() require('notes').open(configuration) end, { nargs = 0 })
+-- Note Handling Commands
+command('NotaOpenDailyNote', function() require('notes').open_daily() end, { nargs = 0 })
+command('NotaOpenWeeklyNote', function() require('notes').open_weekly() end, { nargs = 0 })
+command('NotaOpenMonthlyNote', function() require('notes').open_monthly() end, { nargs = 0 })
+command('NotaOpenSeasonalNote', function() require('notes').open_seasonal() end, { nargs = 0 })
+command('NotaOpenYearlyNote', function() require('notes').open_yearly() end, { nargs = 0 })
+command('NotaOpenNote', function() require('notes').open() end, { nargs = 0 })
 
-  -- Task View Handling Commands
-  command('NotaOpenAgenda', function() require('task_views').open_agenda(configuration) end, { nargs = 0 })
-  command('NotaOpenOpen', function() require('task_views').open_open(configuration) end, { nargs = 0 })
-  command('NotaOpenJournal', function() require('task_views').open_journal(configuration) end, { nargs = 0 })
+-- Task View Handling Commands
+command('NotaOpenAgenda', function() require('task_views').open_agenda() end, { nargs = 0 })
+command('NotaOpenOpen', function() require('task_views').open_open() end, { nargs = 0 })
+command('NotaOpenOpenImportant', function() require('task_views').open_open_important() end, { nargs = 0 })
+command('NotaOpenJournal', function() require('task_views').open_journal() end, { nargs = 0 })
 
-  -- Task Handling Commands
-  command('NotaToggleTask', function() require('tasks').toggle(configuration) end, { nargs = 0 })
-  command('NotaInsertTask', function() require('tasks').insert(configuration) end, { nargs = 0 })
-  command('NotaCaptureTask', function() require('tasks').capture(configuration) end, { nargs = 0 })
-  command('NotaTagTask', function() require('tasks').tag(configuration) end, { nargs = 0 })
-  command('NotaRescheduleTaskToday', function() require('tasks').reschedule_for_today(configuration) end, { nargs = 0 })
-  command('NotaRescheduleTaskTomorrow', function() require('tasks').reschedule_for_tomorrow(configuration) end, { nargs = 0 })
-  command('NotaRescheduleTaskSomeday', function() require('tasks').reschedule_for_someday(configuration) end, { nargs = 0 })
-  command('NotaRescheduleTask', function(options) require('tasks').reschedule(configuration, options.args) end, { nargs = 1 })
+-- Task Handling Commands
+command('NotaToggleTask', function() require('tasks').toggle() end, { nargs = 0 })
+command('NotaToggleTaskImportance', function() require('tasks').toggle_importance() end, { nargs = 0 })
+command('NotaInsertTask', function() require('tasks').insert() end, { nargs = 0 })
+command('NotaCaptureTask', function() require('tasks').capture() end, { nargs = 0 })
+command('NotaTagTask', function() require('tasks').tag() end, { nargs = 0 })
+command('NotaRescheduleTaskToday', function() require('tasks').reschedule_for_today() end, { nargs = 0 })
+command('NotaRescheduleTaskTomorrow', function() require('tasks').reschedule_for_tomorrow() end, { nargs = 0 })
+command('NotaRescheduleTaskSomeday', function() require('tasks').reschedule_for_someday() end, { nargs = 0 })
+command('NotaRescheduleTask', function(options) require('tasks').reschedule(options.args) end, { nargs = 1 })
 
-  -- .plan Handling Commands
-  command('NotaOpenPlan', function() require('plan').open(configuration) end, { nargs = 0 })
-  command('NotaCapturePlan', function() require('plan').capture(configuration) end, { nargs = 0 })
+-- .plan Handling Commands
+command('NotaOpenPlan', function() require('plan').open() end, { nargs = 0 })
+command('NotaCapturePlan', function() require('plan').capture() end, { nargs = 0 })
 
-  -- Learning File Handling Commands
-  command('NotaOpenLearning', function() require('learning').open(configuration) end, { nargs = 0 })
-  command('NotaCaptureLearning', function() require('learning').capture(configuration) end, { nargs = 0 })
-end
+-- Learning File Handling Commands
+command('NotaOpenLearning', function() require('learning').open() end, { nargs = 0 })
+command('NotaCaptureLearning', function() require('learning').capture() end, { nargs = 0 })