>_EXECUTIVE SUMMARY
zsh-learn is a MySQL/MariaDB-backed flashcard plugin for zsh. Every entry is a row in a $ZPWR_SCHEMA_NAME.$ZPWR_TABLE_NAME table (default root.LearningCollection); the plugin ships save / search / random-recall / quiz / SQL-passthrough / in-place-edit / delete operations as autoloaded zsh functions. le "git rebase -i HEAD~3" writes the row, se rebase searches it, qu 50 pipes 50 random entries into fzf for spaced-recall drilling. The plugin's database surface is the same one consumed by the LearningCollectionAPI Spring Boot service — the zsh CLI and the web/REST API operate on the same authoritative table. Source: 833 zsh lines across 1 plugin entry + 26 autoload functions + 1 completion. Pinned by 453 @test blocks across 6 zunit files.
~ARCHITECTURE
Single entry point that conditionally registers ZLE + zstyle + bindkeys, then autoloads every function under autoload/ via autoload -Uz "${0:h}/autoload/"*(.:t). Each verb is its own one-file function — lazy-loaded on first use.
| File / Group | Lines | Role |
|---|---|---|
zsh-learn.plugin.zsh |
137 | Plugin entry. Declares ZPWR_VARS association; sets defaults for ZPWR_SCHEMA_NAME / ZPWR_TABLE_NAME / ZPWR_LEARN_COMMAND / temp paths; declares 16 alias-as-verb pinned shortcuts (le, se, see, seee, re, rsql, editl, delid, plus typo variants es/ees/ses/ese/sse/ssee/rer/er); populates ZPWR_VERBS association when zpwr is loaded; registers per-completion zstyle (group-order, list-colors, sort-disabled for numeric IDs); binds Ctrl-K across viins / vicmd / emacs keymaps; prepends ${0:h}/src + ${0:h}/autoload to fpath; autoloads all 26 functions under autoload/. |
autoload/zsh-learn-Learn |
40 | Core ZLE widget. Prompts via the current line buffer, formats the entry, dispatches to zsh-learn-Savel. Bound to Ctrl-K. |
autoload/zsh-learn-Savel |
57 | The insert path. Escapes apostrophes, length-checks against ZPWR_LEARN_MAX_SIZE (default 3000), composes the SQL INSERT, dispatches via $ZPWR_LEARN_COMMAND $ZPWR_SCHEMA_NAME (default mysql root). |
autoload/zsh-learn-Searchl · Searchle · Searchlee |
71 / 72 / 21 | Three-tier search functions, fronted by se / see / seee respectively. Each tier widens the projected column set (learning → learning+category → learning+category+date). All three accept filter arguments and apply them via SQL WHERE ... LIKE .... |
autoload/zsh-learn-Redo · Redosql |
21 / 29 | SQL passthrough. re ID prints the matched row plus a ready-to-edit UPDATE statement; rsql ID opens the same in $EDITOR. |
autoload/zsh-learn-Editl · DeleteId · del |
35 / 11 / 9 | In-place edit via $EDITOR; targeted delete by ID; bulk delete of last N rows. |
autoload/qu · qua · ser · sera · sef |
14 / 8 / 13 / 8 / 12 | Random-recall family. ser N = N random rows; sera = all randomized; qu N = same as ser but piped to fzf for interactive quiz; qua = all randomized into fzf; sef = recency-ordered fzf browse. |
autoload/zsh-learn-CreateLearningCollection · DropLearningCollection |
65 / 61 | DDL admin. Create emits the schema (id PK + auto-increment, learning TEXT, category VARCHAR, timestamp), Drop tears it down. Largest single autoload files because they carry the literal DDL strings. |
autoload/zsh-learn-Get · GetItems · GetLastItem |
20 / 42 / 12 | Read primitives used by the search/redo verbs internally. |
autoload/_fzf_complete_{se,redo,rsql} + _post |
11 + 8 (× 3) | fzf-tab integration hooks. When fzf-tab or fzf's shell-completer is loaded, these widgets feed candidate IDs / learning rows directly into fzf. |
src/_se |
18 | compdef completion shared by se / see / seee / re / rsql / zsh-learn-Redo / zsh-learn-Redosql / zsh-learn-Searchl / zsh-learn-Searchle / zsh-learn-Searchlee. |
| Total source | 833 | 1 entry + 26 autoloads + 1 completion · pure zsh |
#TEST COVERAGE
453 @test blocks across 6 zunit files. t-unit.zsh carries the bulk (432) — behavioral pins for every verb against a fixture $ZPWR_LEARN_COMMAND stub. The 4 contract files pin install-path / fpath / autoload shape.
| File | Tests | Pins |
|---|---|---|
t-unit.zsh | 432 | Every verb (le, se, see, seee, ser, sera, qu, qua, sef, re, rsql, editl, del, delid, Create/Drop) emits the documented SQL shape against a mocked $ZPWR_LEARN_COMMAND; apostrophe-escape behavior; length-cap behavior; verb-typo aliases resolve to the canonical function. |
t-syntax.zsh | 2 | zsh -n over plugin entry + autoload tree. |
t-contract.zsh | 3 | Entrypoint stem matches dir; entrypoint parses; #compdef on every _* file. |
t-contract2.zsh | 6 | fpath augmentation includes both src/ AND autoload/; autoload glob picks up every file under autoload/ with (.:t). |
t-contract3.zsh | 5 | Zinit-style install path stability; ZPWR_LEARN=false opt-out skips bindkey/zstyle registration. |
t-contract4.zsh | 5 | OMZ-style install path; ZPWR_VARS initialized as association on first source; ZLE widget registered exactly once. |
| Total | 453 | 6 zunit files · verb-surface + install-contract |
/INTEGRATION
Database backend
Default: mysql command, schema root, table LearningCollection. Override via ZPWR_LEARN_COMMAND (e.g. 'sudo mysql' for MariaDB unix-auth) and ZPWR_SCHEMA_NAME / ZPWR_TABLE_NAME. zsh-learn-CreateLearningCollection emits the DDL the first time.
LearningCollectionAPI cross-stack
The same $ZPWR_SCHEMA_NAME.$ZPWR_TABLE_NAME rows are consumed by the LearningCollectionAPI Spring Boot REST service — entries saved from zsh are immediately readable from the web app, and vice versa. One source of truth, two front-ends.
ZLE widget binding
zle -N zsh-learn-Learn registers the widget; the file then binds Ctrl-K in three keymaps (viins, vicmd, emacs) so the binding survives any $KEYMAP setting in the user's .zshrc. Vim-emulation users press Ctrl-K in insert OR command mode; emacs-mode users press Ctrl-K directly.
Completion: numeric IDs, ordered, color-coded
The plugin sets three zstyle contracts on the _se completion: sort false (preserve numeric-DESC server order rather than lexicographic), group-order to put IDs before learning text, and per-group list-colors for ID vs text contrast.
fzf integration
If fzf's shell-completer is loaded, the six _fzf_complete_se/redo/rsql hooks intercept the **TAB trigger and feed live database rows into fzf — same backend as the inline search, different presentation.
Lazy autoload + Zsh Plugin Standard
Each verb file under autoload/ is one function. The plugin entry registers them via autoload -Uz — nothing is parsed until first call. Honors the Zsh Plugin Standard 0= header so ${0:h} resolves correctly under zinit / oh-my-zsh / antibody / antigen / bare source.
!DESIGN DECISIONS
SQL backend, not an embedded SQLite
Embedded SQLite would isolate the data to one shell host. MySQL/MariaDB lets the same table be served by the LearningCollectionAPI Spring Boot service — the user's notes are accessible from any browser, any cron job, any sibling process. Cost: a running mysqld. Gain: a personal knowledge graph that isn't bound to one terminal.
Typo-proof verb aliases
16 aliases mapped to 7 canonical functions. es / ees / ses / ese / sse / ssee all dispatch to zsh-learn-Searchl; re / rer / er all dispatch to zsh-learn-Redo. Muscle-memory typos still work — the operator never has to remember the exact spelling.
3-tier search by projected columns
Naive plugins would have one search with a --verbose flag. zsh-learn instead ships three named verbs (se < see < seee) corresponding to projected-column count. Saves a flag, makes the cardinality explicit in the verb itself, and pre-renders the zstyle group-order per verb.
Three keymaps for one widget
bindkey applies to one keymap at a time. The plugin doesn't try to detect the user's mode — it just registers Ctrl-K against all three of viins, vicmd, emacs. Total cost: three bindkey calls. Total gain: never lose access to Learn after a mode switch.
noglob on every alias
Every alias is noglob zsh-learn-*. Search filters contain *, ?, [] — without noglob the shell would glob-expand them against the local filesystem before the function ever saw them. noglob at alias time is one keystroke for the operator and zero behavioral surprises.
Verb-as-file, not verb-as-block
26 separate files under autoload/ instead of one mega-file. Each function lazy-loads on first call, so cold-startup cost is just the entry-point parse (~137 lines). Editing one verb does not invalidate any compiled .zwc for the others.
@FOOTPRINT
- Disk: ~28 files under
autoload/+src/_se+ plugin entry. No compiled artifacts. - Memory:
ZPWR_VARSassociation + theZPWR_VERBSentries (when zpwr is loaded) + the autoloaded function table. Zero data structures — everything is in MySQL. - Startup cost: 137-line entry parse +
autoload -Uzregistration (no compile, no body parse). - Runtime cost: one
$ZPWR_LEARN_COMMANDsubprocess per verb invocation. Search latency is bounded by the configuredmaxRecords(default 2000) row count. - External tools:
mysql(required),fzf(optional, forqu/qua/sef),$EDITOR(foreditl/rsql).