Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

clikeyrs

A terminal typing trainer for programmers. Point it at a source file and it extracts real, semantically complete code blocks — functions, structs, match expressions, loops — formats them properly, and serves them one at a time as typing practice in a TUI.

Typing prose doesn't train the muscle memory programming needs: brackets, colons, generics, indentation. clikeyrs practices you on actual code, in the formatting style you'd see in a real codebase.

$ clikeyrs src/main.rs

Each practice unit is a complete, valid construct pulled from your file:

fn practice_match_expression() -> i32 {
    match result {
        Ok(value) => value,
        Err(_) => 0,
    }
}

How it works

For Rust files, clikeyrs parses the source into an AST with syn, walks it to extract practice blocks, and formats each block with prettyplease — so you always type idiomatically formatted code, regardless of how the input file looks:

File ──► AST parser (syn) ──► Formatter (prettyplease) ──► Dictionary ──► TUI (ratatui)

The AST pass extracts:

  • complete functions, and their signatures as separate focused units
  • methods from impl blocks, individually
  • structs, enums, traits, type aliases, constants, statics, modules
  • control flow — for/while/loop, if/if let, match — lifted out of function bodies and wrapped in a synthetic fn practice_*() so each block is valid, formattable Rust on its own

Python, JavaScript, and Go fall back to a simpler token-based parser without AST-level extraction or reformatting.

Installation

Build from source (requires a Rust toolchain):

git clone <this repository>
cd clikeyrs
cargo install --path .

Usage

clikeyrs <file> [OPTIONS]

clikeyrs src/main.rs                 # practice on a Rust file (full AST pipeline)
clikeyrs script.py --time 15         # 15-minute session, fallback parser
clikeyrs lib.rs --seed 12345         # reproducible block order
Option Description
-t, --time <minutes> Limit session duration (unlimited by default)
-s, --seed <number> Fix the shuffle seed for a reproducible session
--no-comments Exclude comment blocks
--include-imports Include use/import statements
-d, --debug Print the first 5 parsed and formatted blocks, then exit
-dv, --debug-verbose Print all blocks with formatting details, then exit
-h, --help Show help

Blocks are shuffled each session (seeded, via fastrand); pass --seed to replay the same order. Session targets default to 95% accuracy and 30 WPM.

During a session

  • Type the displayed block; the timer starts on your first keystroke.
  • Backspace steps back and clears the error at that position.
  • Tab counts as the matching run of spaces (up to 4), so indentation can be typed either way.
  • Enter advances to the next line.
  • Esc or Ctrl+C ends the block / exits.

Debug mode

--debug / --debug-verbose run the parse-and-format pipeline without starting the TUI and print every extracted block alongside its formatted output. This is the primary way to inspect what a given file will turn into:

 1. Function Signature | Lines 5-5 | 40 chars
    ┌─ Original Source:
    │ fn print_usage (program_name : & str) {}
    ├─ Formatted Output:
    │ fn print_usage(program_name: &str) {}
    └─ Formatting applied

Language support

Language Pipeline Blocks
Rust (.rs) AST (syn) + prettyplease functions, signatures, methods, types, control flow
Python (.py), JavaScript (.js), Go (.go) token-based fallback coarser blocks, no reformatting

Adding a language to the AST path means implementing two traits — see Architecture.

Development

The Makefile is the single dev entrypoint:

make check          # full CI gate: fmt-check + clippy -D warnings + tests + architecture tests
make dev            # fast inner loop: format + lint + test
make test           # test suite (deterministic, <1s)
make arch           # architecture tests only
make coverage       # line-coverage gate (fails under 80%)
make audit          # cargo-audit + cargo-deny
make run FILE=src/main.rs ARGS="--debug"

Finish every change with make check — clippy warnings are errors and the codebase is rustfmt-formatted.

Tests live in inline #[cfg(test)] modules next to the code and are fully deterministic: shuffles take fixed seeds, widgets render into an in-memory ratatui buffer, and there are no timing or network assertions.

Architecture

The crate is layered, and the layering is enforced by tests/architecture.rs (make arch):

core  ◄──  parsers, formatters  ◄──  dictionary  ◄──  ui
Module Responsibility
core/ shared types: Language, AstBlock, SemanticBlock, statistics
parsers/ extraction only, no formatting — AstParser (modern) and LanguageParser (fallback)
formatters/ CodeFormatter trait; RustFormatter wraps prettyplease
dictionary/ holds blocks, filters by size, creates TrainingSessions
ui/ ratatui/crossterm TUI, themes, and widgets

External crates are confined to their layer (ratatui/crossterm in ui, syn/prettyplease in parsers/formatters), also test-enforced. Each module directory has a README.md with its implementation patterns and constraints.

To add a language on the AST path:

  1. Add a variant to Language in src/core/mod.rs (extension mapping + name).
  2. Implement AstParser for it in src/parsers/.
  3. Implement CodeFormatter in src/formatters/ using the language's standard formatter.
  4. Register both in ParserFactory::create_ast_parser and formatters::create_formatter.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages