docs: update vimdoc for sync refactor, remove demo scripts
Problem: Docs still referenced openssl dependency, defaulting to `sync` action, and the `calendar` config field. Demo scripts used the old singleton `store` API. Solution: Update vimdoc and README to reflect explicit actions, per- category calendars, and pure-Lua PKCE. Remove stale demo scripts and update sync specs to match new behavior.
This commit is contained in:
parent
9a762a5320
commit
bc2a106617
6 changed files with 42 additions and 110 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
doc/tags
|
doc/tags
|
||||||
*.log
|
*.log
|
||||||
|
minimal_init.lua
|
||||||
|
|
||||||
.*cache*
|
.*cache*
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ Edit tasks like text. `:w` saves them.
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Neovim 0.10+
|
- Neovim 0.10+
|
||||||
- (Optionally) `curl` and `openssl` for Google Calendar and Google Task sync
|
- (Optionally) `curl` for Google Calendar and Google Task sync
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ REQUIREMENTS *pending-requirements*
|
||||||
|
|
||||||
- Neovim 0.10+
|
- Neovim 0.10+
|
||||||
- No external dependencies for local use
|
- No external dependencies for local use
|
||||||
- `curl` and `openssl` are required for the `gcal` and `gtasks` sync backends
|
- `curl` is required for the `gcal` and `gtasks` sync backends
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
INSTALL *pending-install*
|
INSTALL *pending-install*
|
||||||
|
|
@ -149,17 +149,17 @@ COMMANDS *pending-commands*
|
||||||
Open the list with |:copen| to navigate to each task's category.
|
Open the list with |:copen| to navigate to each task's category.
|
||||||
|
|
||||||
*:Pending-gtasks*
|
*:Pending-gtasks*
|
||||||
:Pending gtasks [{action}]
|
:Pending gtasks {action}
|
||||||
Run a Google Tasks sync action. {action} defaults to `sync` when omitted.
|
Run a Google Tasks action. An explicit action is required.
|
||||||
|
|
||||||
Actions: ~
|
Actions: ~
|
||||||
`sync` Push local changes then pull remote changes (default).
|
`sync` Push local changes then pull remote changes.
|
||||||
`push` Push local changes to Google Tasks only.
|
`push` Push local changes to Google Tasks only.
|
||||||
`pull` Pull remote changes from Google Tasks only.
|
`pull` Pull remote changes from Google Tasks only.
|
||||||
`auth` Run the OAuth authorization flow.
|
`auth` Run the OAuth authorization flow.
|
||||||
|
|
||||||
Examples: >vim
|
Examples: >vim
|
||||||
:Pending gtasks " push then pull (default)
|
:Pending gtasks sync " push then pull
|
||||||
:Pending gtasks push " push local → Google Tasks
|
:Pending gtasks push " push local → Google Tasks
|
||||||
:Pending gtasks pull " pull Google Tasks → local
|
:Pending gtasks pull " pull Google Tasks → local
|
||||||
:Pending gtasks auth " authorize
|
:Pending gtasks auth " authorize
|
||||||
|
|
@ -169,16 +169,15 @@ COMMANDS *pending-commands*
|
||||||
See |pending-gtasks| for full details.
|
See |pending-gtasks| for full details.
|
||||||
|
|
||||||
*:Pending-gcal*
|
*:Pending-gcal*
|
||||||
:Pending gcal [{action}]
|
:Pending gcal {action}
|
||||||
Run a Google Calendar sync action. {action} defaults to `sync` when
|
Run a Google Calendar action. An explicit action is required.
|
||||||
omitted.
|
|
||||||
|
|
||||||
Actions: ~
|
Actions: ~
|
||||||
`sync` Push tasks with due dates to Google Calendar (default).
|
`push` Push tasks with due dates to Google Calendar.
|
||||||
`auth` Run the OAuth authorization flow.
|
`auth` Run the OAuth authorization flow.
|
||||||
|
|
||||||
Examples: >vim
|
Examples: >vim
|
||||||
:Pending gcal " push to Google Calendar (default)
|
:Pending gcal push " push to Google Calendar
|
||||||
:Pending gcal auth " authorize
|
:Pending gcal auth " authorize
|
||||||
<
|
<
|
||||||
|
|
||||||
|
|
@ -606,9 +605,7 @@ loads: >lua
|
||||||
prev_task = '[t',
|
prev_task = '[t',
|
||||||
},
|
},
|
||||||
sync = {
|
sync = {
|
||||||
gcal = {
|
gcal = {},
|
||||||
calendar = 'Pendings',
|
|
||||||
},
|
|
||||||
gtasks = {},
|
gtasks = {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -893,8 +890,8 @@ SYNC BACKENDS *pending-sync-backend*
|
||||||
|
|
||||||
Sync backends are Lua modules under `lua/pending/sync/<name>.lua`. Each
|
Sync backends are Lua modules under `lua/pending/sync/<name>.lua`. Each
|
||||||
backend is exposed as a top-level `:Pending` subcommand: >vim
|
backend is exposed as a top-level `:Pending` subcommand: >vim
|
||||||
:Pending gtasks [action]
|
:Pending gtasks {action}
|
||||||
:Pending gcal [action]
|
:Pending gcal {action}
|
||||||
<
|
<
|
||||||
|
|
||||||
Each module returns a table conforming to the backend interface: >lua
|
Each module returns a table conforming to the backend interface: >lua
|
||||||
|
|
@ -902,9 +899,9 @@ Each module returns a table conforming to the backend interface: >lua
|
||||||
---@class pending.SyncBackend
|
---@class pending.SyncBackend
|
||||||
---@field name string
|
---@field name string
|
||||||
---@field auth fun(): nil
|
---@field auth fun(): nil
|
||||||
---@field sync fun(): nil
|
|
||||||
---@field push? fun(): nil
|
---@field push? fun(): nil
|
||||||
---@field pull? fun(): nil
|
---@field pull? fun(): nil
|
||||||
|
---@field sync? fun(): nil
|
||||||
---@field health? fun(): nil
|
---@field health? fun(): nil
|
||||||
<
|
<
|
||||||
|
|
||||||
|
|
@ -924,16 +921,15 @@ Backend-specific configuration goes under `sync.<name>` in |pending-config|.
|
||||||
==============================================================================
|
==============================================================================
|
||||||
GOOGLE CALENDAR *pending-gcal*
|
GOOGLE CALENDAR *pending-gcal*
|
||||||
|
|
||||||
pending.nvim can push tasks with due dates to a dedicated Google Calendar as
|
pending.nvim can push tasks with due dates to Google Calendar as all-day
|
||||||
all-day events. This is a one-way push; changes made in Google Calendar are
|
events. Each pending.nvim category maps to a Google Calendar of the same
|
||||||
not pulled back into pending.nvim.
|
name. Calendars are created automatically on first push. This is a one-way
|
||||||
|
push; changes made in Google Calendar are not pulled back.
|
||||||
|
|
||||||
Configuration: >lua
|
Configuration: >lua
|
||||||
vim.g.pending = {
|
vim.g.pending = {
|
||||||
sync = {
|
sync = {
|
||||||
gcal = {
|
gcal = {},
|
||||||
calendar = 'Pendings',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
<
|
<
|
||||||
|
|
@ -943,11 +939,6 @@ used by default. Run `:Pending gcal auth` and the browser opens immediately.
|
||||||
|
|
||||||
*pending.GcalConfig*
|
*pending.GcalConfig*
|
||||||
Fields: ~
|
Fields: ~
|
||||||
{calendar} (string, default: 'Pendings')
|
|
||||||
Name of the Google Calendar to sync to. If a calendar
|
|
||||||
with this name does not exist it is created
|
|
||||||
automatically on the first sync.
|
|
||||||
|
|
||||||
{client_id} (string, optional)
|
{client_id} (string, optional)
|
||||||
OAuth client ID. When set together with
|
OAuth client ID. When set together with
|
||||||
{client_secret}, these take priority over the
|
{client_secret}, these take priority over the
|
||||||
|
|
@ -973,26 +964,24 @@ OAuth flow: ~
|
||||||
On the first `:Pending gcal` call the plugin detects that no refresh token
|
On the first `:Pending gcal` call the plugin detects that no refresh token
|
||||||
exists and opens the Google authorization URL in the browser using
|
exists and opens the Google authorization URL in the browser using
|
||||||
|vim.ui.open()|. A temporary local HTTP server listens on port 18392 for the
|
|vim.ui.open()|. A temporary local HTTP server listens on port 18392 for the
|
||||||
OAuth redirect. The PKCE (Proof Key for Code Exchange) flow is used —
|
OAuth redirect. The PKCE (Proof Key for Code Exchange) flow is used. After
|
||||||
`openssl` generates the code challenge. After the user grants consent, the
|
the user grants consent, the
|
||||||
authorization code is exchanged for tokens and the refresh token is stored at
|
authorization code is exchanged for tokens and the refresh token is stored at
|
||||||
`stdpath('data')/pending/gcal_tokens.json` with mode `600`. Subsequent syncs
|
`stdpath('data')/pending/gcal_tokens.json` with mode `600`. Subsequent syncs
|
||||||
use the stored refresh token and refresh the access token automatically when
|
use the stored refresh token and refresh the access token automatically when
|
||||||
it is about to expire.
|
it is about to expire.
|
||||||
|
|
||||||
`:Pending gcal` behavior: ~
|
`:Pending gcal push` behavior: ~
|
||||||
For each task in the store:
|
For each task in the store:
|
||||||
- A pending task with a due date and no existing event: a new all-day event is
|
- A pending task with a due date and no existing event: a new all-day event is
|
||||||
created and the event ID is stored in the task's `_extra` table.
|
created in the calendar matching the task's category. The event ID and
|
||||||
|
calendar ID are stored in the task's `_extra` table.
|
||||||
- A pending task with a due date and an existing event: the event summary and
|
- A pending task with a due date and an existing event: the event summary and
|
||||||
date are updated in place.
|
date are updated in place.
|
||||||
- A done or deleted task with an existing event: the event is deleted.
|
- A done or deleted task with an existing event: the event is deleted.
|
||||||
- A pending task with no due date that had an existing event: the event is
|
- A pending task with no due date that had an existing event: the event is
|
||||||
deleted.
|
deleted.
|
||||||
|
|
||||||
A summary notification is shown after sync: `created: N, updated: N,
|
|
||||||
deleted: N`.
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
GOOGLE TASKS *pending-gtasks*
|
GOOGLE TASKS *pending-gtasks*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
vim.opt.runtimepath:prepend(vim.fn.getcwd())
|
|
||||||
local tmpdir = vim.fn.tempname()
|
|
||||||
vim.fn.mkdir(tmpdir, 'p')
|
|
||||||
|
|
||||||
vim.g.pending = {
|
|
||||||
data_path = tmpdir .. '/tasks.json',
|
|
||||||
}
|
|
||||||
|
|
||||||
local store = require('pending.store')
|
|
||||||
store.load()
|
|
||||||
|
|
||||||
local today = os.date('%Y-%m-%d')
|
|
||||||
local yesterday = os.date('%Y-%m-%d', os.time() - 86400)
|
|
||||||
local tomorrow = os.date('%Y-%m-%d', os.time() + 86400)
|
|
||||||
|
|
||||||
store.add({
|
|
||||||
description = 'Finish quarterly report',
|
|
||||||
category = 'Work',
|
|
||||||
due = tomorrow,
|
|
||||||
recur = 'monthly',
|
|
||||||
priority = 1,
|
|
||||||
})
|
|
||||||
store.add({ description = 'Review pull requests', category = 'Work' })
|
|
||||||
store.add({ description = 'Update deployment docs', category = 'Work', status = 'done' })
|
|
||||||
store.add({ description = 'Buy groceries', category = 'Personal', due = today })
|
|
||||||
store.add({ description = 'Call dentist', category = 'Personal', due = yesterday, priority = 1 })
|
|
||||||
store.add({ description = 'Read chapter 5', category = 'Personal' })
|
|
||||||
store.add({ description = 'Learn a new language', category = 'Someday' })
|
|
||||||
store.add({ description = 'Plan hiking trip', category = 'Someday' })
|
|
||||||
store.save()
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
Output assets/demo.gif
|
|
||||||
|
|
||||||
Require nvim
|
|
||||||
|
|
||||||
Set Shell "bash"
|
|
||||||
Set FontSize 14
|
|
||||||
Set Width 900
|
|
||||||
Set Height 450
|
|
||||||
|
|
||||||
Type "nvim -u scripts/demo-init.lua -c 'autocmd VimEnter * Pending'"
|
|
||||||
Enter
|
|
||||||
|
|
||||||
Sleep 2s
|
|
||||||
|
|
||||||
Down
|
|
||||||
Down
|
|
||||||
Sleep 300ms
|
|
||||||
Down
|
|
||||||
Sleep 300ms
|
|
||||||
|
|
||||||
Enter
|
|
||||||
Sleep 500ms
|
|
||||||
|
|
||||||
Tab
|
|
||||||
Sleep 1s
|
|
||||||
|
|
||||||
Type "q"
|
|
||||||
Sleep 200ms
|
|
||||||
|
|
@ -49,27 +49,27 @@ describe('sync', function()
|
||||||
assert.are.equal("gcal backend has no 'notreal' action", msg)
|
assert.are.equal("gcal backend has no 'notreal' action", msg)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('defaults to sync action when action is omitted', function()
|
it('lists actions when action is omitted', function()
|
||||||
local called = false
|
local msg = nil
|
||||||
local gcal = require('pending.sync.gcal')
|
local orig = vim.notify
|
||||||
local orig_sync = gcal.sync
|
vim.notify = function(m)
|
||||||
gcal.sync = function()
|
msg = m
|
||||||
called = true
|
|
||||||
end
|
end
|
||||||
pending.command('gcal')
|
pending.command('gcal')
|
||||||
gcal.sync = orig_sync
|
vim.notify = orig
|
||||||
assert.is_true(called)
|
assert.is_not_nil(msg)
|
||||||
|
assert.is_truthy(msg:find('push'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('routes explicit sync action', function()
|
it('routes explicit push action', function()
|
||||||
local called = false
|
local called = false
|
||||||
local gcal = require('pending.sync.gcal')
|
local gcal = require('pending.sync.gcal')
|
||||||
local orig_sync = gcal.sync
|
local orig_push = gcal.push
|
||||||
gcal.sync = function()
|
gcal.push = function()
|
||||||
called = true
|
called = true
|
||||||
end
|
end
|
||||||
pending.command('gcal sync')
|
pending.command('gcal push')
|
||||||
gcal.sync = orig_sync
|
gcal.push = orig_push
|
||||||
assert.is_true(called)
|
assert.is_true(called)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -90,10 +90,10 @@ describe('sync', function()
|
||||||
config.reset()
|
config.reset()
|
||||||
vim.g.pending = {
|
vim.g.pending = {
|
||||||
data_path = tmpdir .. '/tasks.json',
|
data_path = tmpdir .. '/tasks.json',
|
||||||
sync = { gcal = { calendar = 'NewStyle' } },
|
sync = { gcal = { client_id = 'test-id' } },
|
||||||
}
|
}
|
||||||
local cfg = config.get()
|
local cfg = config.get()
|
||||||
assert.are.equal('NewStyle', cfg.sync.gcal.calendar)
|
assert.are.equal('test-id', cfg.sync.gcal.client_id)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe('gcal module', function()
|
describe('gcal module', function()
|
||||||
|
|
@ -107,9 +107,9 @@ describe('sync', function()
|
||||||
assert.are.equal('function', type(gcal.auth))
|
assert.are.equal('function', type(gcal.auth))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('has sync function', function()
|
it('has push function', function()
|
||||||
local gcal = require('pending.sync.gcal')
|
local gcal = require('pending.sync.gcal')
|
||||||
assert.are.equal('function', type(gcal.sync))
|
assert.are.equal('function', type(gcal.push))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('has health function', function()
|
it('has health function', function()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue