Skip to content

fix: robust task-CLI I/O (#5), prefix-preserving completion (#4), wrap option + focusable confirm picker (#3)#6

Open
MattHandzel wants to merge 1 commit into
mainfrom
fix/issues-3-4-5-robust-task-io
Open

fix: robust task-CLI I/O (#5), prefix-preserving completion (#4), wrap option + focusable confirm picker (#3)#6
MattHandzel wants to merge 1 commit into
mainfrom
fix/issues-3-4-5-robust-task-io

Conversation

@MattHandzel

Copy link
Copy Markdown
Owner

What this fixes

#5:Tw opens a blank buffer, :TwRefresh has no effect

Root cause. vim.fn.system merges stderr into stdout. Any Taskwarrior chatter around the export JSON — the TW 3.x task news nag, Configuration override notices (triggered by our own rc.* args for users with verbose=override), hook output, forced-color ANSI escapes — made the JSON parse fail, and every parse site silently degraded to "zero tasks". A zero-task render is only the header comment, which conceallevel=3 hides entirely: the buffer looks blank, and :TwRefresh faithfully re-renders the same empty result. (Telling detail from the report itself: the feedback template's task_count_bucket worked — 11-25 — because that one call uses 2>/dev/null.)

Fix, defense in depth:

  1. READ_RC adds rc.verbose=nothing rc.color=off to every read-only task invocation — chatter suppressed at the source. Mutation paths keep verbose output (tw_add parses Created task <uuid>).
  2. New decode_json_array: strips ANSI, tries prioritized bracket candidates, and falls back to line-filtering for chatter interleaved between TW3's line-per-task output.
  3. The ten hand-rolled find("%[") slices (buffer render/virt-text, graph, inbox, views, delegate ×2, modify, statusline, telescope picker, api.export) are replaced by one shared taskmd.shell_export that additionally redirects stderr away from the parse stream.
  4. tw_export now raises a diagnosable error (with the actual task output) instead of silently returning {} — a failure can no longer render as silence.
  5. A genuinely-zero-task render shows a virt-lines empty-state hint (No tasks match filter: … · :TwFilter to change it) instead of a blank buffer.
  6. _udas / _projects / _tags line parsing rejects lines containing whitespace (valid names never do; chatter always does) so nag lines can't become phantom UDAs or completion candidates.

#4 — Tab in the :TwFilter box deleted the already-typed filter

vim.fn.input() completion replaces the entire typed text with the chosen candidate; _complete_filter returned bare last-token candidates, so completing +ACTIVE after project:foo wiped project:foo. Candidates now carry the untouched leading tokens. Bonus find: the gm modify prompt referenced _complete_modify, which was never defined — Tab there raised E117. It's now implemented, and a lint spec asserts every v:lua completion reference in the codebase resolves to a real function.

#3 — wrap option + unusable Apply/Cancel popup

  • New setup{ wrap = false } option (validated, documented in README + :help) for one-line-per-task rendering.
  • The Apply/Cancel confirmation was a vim.ui.select called synchronously inside the BufWriteCmd handler; float-based select backends (dressing, snacks, telescope) rendered the picker but couldn't take focus — cursor stayed in the task buffer. The picker is now vim.schedule()d out of the autocmd context.

Verification

  • 19 new unit specs (tests/lua/spec/issue_fixes_spec.lua): decode robustness (leading/trailing/bracketed/ANSI/interleaved noise), tw_export raising instead of silently returning {}, empty-state extmarks, completion prefix preservation, _complete_modify, the v:lua lint, wrap validation.
  • 6 new e2e specs against a real Taskwarrior DB: render survival under a real noisy on-launch hook, wrap window state, empty-state extmark on a real render + absence when tasks match, and the confirm-picker Apply/Cancel drain.
  • Full Lua unit suite and e2e suite green (the one pre-existing icons=true → NF glyph e2e failure exists on main and is unrelated — worth a separate issue).

Closes #3. Closes #4. Addresses #5 — leaving that one to be closed once @oliviertripet confirms on this branch (repro depends on his environment; see the ask in the issue thread).

🤖 Generated with Claude Code

…focusable confirm picker

Fixes three classes of user-reported bugs:

Issue #5 — :Tw opened a blank buffer with no error. vim.fn.system merges
stderr into stdout, so any Taskwarrior chatter around the export JSON
(the `task news` nag, "Configuration override" notices, hook output,
forced-color ANSI escapes) made the parse fail — and every parse site
silently degraded to "zero tasks". A zero-task render is only the
concealed header comment, so the buffer looked blank and :TwRefresh
looked like a no-op. Now:
  - READ_RC adds rc.verbose=nothing + rc.color=off to every read-only
    invocation, suppressing chatter at the source (mutations keep
    verbose output — tw_add parses "Created task <uuid>" from it);
  - decode_json_array strips ANSI, scans prioritized bracket
    candidates, and falls back to line-filtering for chatter
    interleaved between TW3's line-per-task output;
  - the ten hand-rolled `find("%[")` slices (buffer, graph, inbox,
    views, delegate, modify, statusline, telescope, api.export) are
    replaced by one shared taskmd.shell_export that also redirects
    stderr away from the parse stream;
  - tw_export raises a diagnosable error instead of silently returning
    {} on unparseable output;
  - a zero-task render shows a virt-lines empty-state hint instead of
    a blank buffer;
  - _udas/_projects/_tags line parsing rejects lines containing
    whitespace (valid names never do; chatter always does).

Issue #4 — <Tab> in the filter prompt wiped the already-typed filter.
input() completion replaces the ENTIRE line, so candidates now carry
the untouched leading tokens. Also defines _complete_modify, which the
gm prompt referenced but which never existed (E117 on <Tab>), and adds
a lint spec asserting every v:lua completion reference resolves.

Issue #3 — adds setup{ wrap = false } to disable line wrap in task
buffers, and vim.schedule()s the Apply/Cancel confirm picker out of
the BufWriteCmd context so float-based vim.ui.select backends
(dressing, snacks, telescope) can take focus.

Verified: 19 new unit specs (decode robustness, completion prefixing,
empty state, wrap validation, v:lua lint) and 6 new e2e specs (wrap
window state, empty-state extmark on a real render, render survival
under a real noisy on-launch hook, confirm-picker drain) — all green
against a real Taskwarrior DB.

Co-Authored-By: Claude Fable 5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feedback Add option for disabling wrap

1 participant