Compare commits
10 commits
fb7eaecb42
...
61a37706db
Author | SHA1 | Date | |
---|---|---|---|
61a37706db | |||
|
1b92268df9 | ||
|
93cb5f5854 | ||
935fc28ba3 | |||
370331638c | |||
345f494c0f | |||
129a5c6ce3 | |||
2deb0488f0 | |||
|
772c66e132 | ||
|
4d0c4129d8 |
8 changed files with 228 additions and 26 deletions
4
Makefile
4
Makefile
|
@ -1,5 +1,5 @@
|
|||
build: fasttyper
|
||||
python setup.py sdist bdist_wheel
|
||||
build: fasttyper dist
|
||||
python3 setup.py sdist bdist_wheel
|
||||
|
||||
push:
|
||||
twine upload dist/*
|
||||
|
|
77
README.md
77
README.md
|
@ -3,11 +3,24 @@
|
|||
|
||||
# About
|
||||
|
||||
_Fasttyper_ is minimalistic typing test based on user provided exercising text. It supports both reading from text files and stdin supporting wide range of usecases. The goal was to create it as simple as it can be, without any additional bloatware functionalities. That means that _Fasttyper_ doesn't come with build in test generator and you have to provide your own scripts generating tests. Some examples of such scrips are providen in [Usage section](#usage).
|
||||
_Fasttyper_ is minimalistic typing test based on user provided exercising text. It supports both reading from text files and stdin supporting wide range of usecases. The goal was to create it as simple as it can be, without any additional bloatware functionalities. That means that _Fasttyper_ doesn't come with build in test generator and you have to provide your own scripts generating tests. Some examples of such scripts are provided in [Usage section](#usage).
|
||||
|
||||
# Table of contents
|
||||
|
||||
- [fasttyper](#fasttyper)
|
||||
- [About](#about)
|
||||
- [Installation](#installation)
|
||||
- [Usage](#usage)
|
||||
- [Running next test and stopping application](#running-next-test-and-stopping-application)
|
||||
- [Configuring](#configuring)
|
||||
- [Usage as module, piping stuff, custom scripts etc](#usage-as-module,-piping-stuff,-custom-scripts-etc)
|
||||
- [When backspace does wierd shiet](#when-backspace-does-wierd-shiet)
|
||||
- [Hiding cursor](#hiding-cursor)
|
||||
- [Example scripts](#example-scripts)
|
||||
|
||||
# Installation
|
||||
|
||||
_Fasttyper_ is currently maintained on [PyPi](https://pypi.org/) Python Package Index. To install package simpply use:
|
||||
_Fasttyper_ is currently maintained on [PyPi](https://pypi.org/) Python Package Index. To install package simply use:
|
||||
|
||||
`python3 -m pip install fasttyper`
|
||||
|
||||
|
@ -19,7 +32,7 @@ With installation of fasttyper you should have new executable - `fasttyper`. It
|
|||
fasttyper
|
||||
```
|
||||
|
||||
from command line. There are some avalibe options:
|
||||
from command line. There are some available options:
|
||||
|
||||
```
|
||||
usage: fasttyper [-h] [--config FILE] [--unclutter-backspace] [--no-cursor] [amount] [language]
|
||||
|
@ -34,7 +47,7 @@ options:
|
|||
configuration file
|
||||
--unclutter-backspace, -b
|
||||
unclutter backspace, when it raises ctrl+backspace instead
|
||||
--no-cursor, -n disable cursos
|
||||
--no-cursor, -n disable cursors
|
||||
```
|
||||
|
||||
I personally use alias:
|
||||
|
@ -53,7 +66,49 @@ To kill application (exit) press CTRL+C.
|
|||
|
||||
# Configuring
|
||||
|
||||
Fasttyper by default looks for config file in: `$HOME/.config/fasttyper/config.json`. You can provide different location for config file with `--config` argument, for example: `--config=~/.fasttyper.json`. Config has to be a json dict. Avalibe keys:
|
||||
Default configuration:
|
||||
|
||||
```python
|
||||
{
|
||||
"user_input_valid_color": 3,
|
||||
"user_input_invalid_color": 2,
|
||||
"reference_text_color": 9,
|
||||
"stats_template": "wpm: {stats.wpm:0.2f}, time: {stats.total_seconds:0.2f}s",
|
||||
"stats_color": 5,
|
||||
"stats_position": "top",
|
||||
"summary_datafile": "~/.cache/fasttyper/datafile.csv",
|
||||
"top_margin_percentage": 40,
|
||||
"left_margin_percentage": 35,
|
||||
"min_width": 80,
|
||||
"lines_on_screen": 3,
|
||||
"border": 1,
|
||||
"logo": " ~ FastTyper ~ ",
|
||||
"logo_color": 8,
|
||||
"resume_text": " press <Tab> to continue, <Ctrl>C to exit ",
|
||||
"resume_text_color": 9,
|
||||
"summary_template": (
|
||||
"wpm: {wpm:5.1f} | peak: {peak_wpm:5.1f}",
|
||||
"raw: {raw_wpm:5.1f} | peak: {peak_raw_wpm:5.1f}",
|
||||
"acc: {accuracy:5.1f}% | words: {correct_words}/{total_words}",
|
||||
"time: {total_seconds:5.1f}s ",
|
||||
),
|
||||
"summary_centered": True,
|
||||
"summary_color": 9,
|
||||
"summary_lines": 4,
|
||||
"summary_border": 0,
|
||||
"random_capitalization": 0,
|
||||
"random_punctuation": 0,
|
||||
"sentence_mode": False,
|
||||
"punctuation": ",.?!;:",
|
||||
"sentence_ending": ".?!",
|
||||
"amount": 25,
|
||||
"language": "english",
|
||||
"no_cursor": False,
|
||||
"unclutter_backspace": False,
|
||||
}
|
||||
```
|
||||
|
||||
Fasttyper by default looks for config file in: `$HOME/.config/fasttyper/config.json`. You can provide different location for config file with `--config` argument, for example: `--config=~/.fasttyper.json`. Config has to be a json dict. Available keys:
|
||||
|
||||
- **user_input_valid_color** - integer, terminal color for valid text, by default it is 3
|
||||
- **user_input_invalid_color** - integer, terminal color for invalid text, by default it is 2
|
||||
|
@ -64,7 +119,9 @@ Fasttyper by default looks for config file in: `$HOME/.config/fasttyper/config.j
|
|||
- **left_margin_percentage** - integer, percentage of screen used for left (and right) margin, by default 10
|
||||
- **lines_on_screen** - integer, number of lines to display on screen, by default 3
|
||||
|
||||
Example config file with all default values in avalibe [here](https://github.com/ickyicky/fasttyper/blob/main/doc/example_config.json).
|
||||
Also there are keys that override fault parameters for CLI args, like: amount and language will override Your default settings for runner, same goes for punctuation, sentence_mode etc.
|
||||
|
||||
Example config file with all default values in available [here](https://github.com/ickyicky/fasttyper/blob/main/doc/example_config.json).
|
||||
|
||||
Other example config files:
|
||||
|
||||
|
@ -92,13 +149,13 @@ Program also allows user to pipe text into it. Keep in mind, it only supports sp
|
|||
|
||||
`furtune | python3 -m fasttyper`
|
||||
|
||||
or if you want to randomize words from given file with shuf on for example all disctionaries in system:
|
||||
or if you want to randomize words from given file with shuf on for example all dictionaries in system:
|
||||
|
||||
`shuf -n5 /usr/share/dict/* | python -m fasttyper`
|
||||
|
||||
You can use another similar projects set of words as well, for example to create test with 20 random words from [Monkeytype's](https://github.com/Miodec/monkeytype) english 100 dictionary use:
|
||||
|
||||
`curl -s https://raw.githubusercontent.com/Miodec/monkeytype/master/static/languages/english.json | python3 -c "import sys, json; print('\n'.join(json.load(sys.stdin)['words']))" | shuf -n20 | python3 -m fasttyper`
|
||||
`curl -s https://raw.githubusercontent.com/monkeytypegame/monkeytype/master/frontend/static/languages/english.json | python3 -c "import sys, json; print('\n'.join(json.load(sys.stdin)['words']))" | shuf -n20 | python3 -m fasttyper`
|
||||
|
||||
To exit you can either finish test, use `KeyboardInterrupt` (CTRL+C) or tap **tab**. After you finish test, there will be summary printed, use enter to exit from it.
|
||||
|
||||
|
@ -128,6 +185,6 @@ function ff() {
|
|||
```
|
||||
`ff 50 english_1k`
|
||||
|
||||
This shell function shuffles N words from cached word list, and if given word list doesnt exist it download's it. It runs in loop, but does exit from it if you exit fasttyper with CTRL+C.
|
||||
This shell function shuffles N words from cached word list, and if given word list doesn't exist it download's it. It runs in loop, but does exit from it if you exit fasttyper with CTRL+C.
|
||||
|
||||
The above script is avalible for download from doc folder.
|
||||
The above script is available for download from doc folder.
|
||||
|
|
|
@ -53,8 +53,13 @@ class Buffer:
|
|||
|
||||
def _next_word(self):
|
||||
self.current_word += 1
|
||||
|
||||
if self.current_char > 0:
|
||||
self.stats.signal_valid() # space is a char after all
|
||||
else:
|
||||
self.stats.signal_invalid() # space after empty word
|
||||
|
||||
self.current_char = 0
|
||||
self.stats.signal_valid() # space is a char after all
|
||||
|
||||
if self.current_word >= self.total_words:
|
||||
self.stats.signal_stop(True)
|
||||
|
@ -143,4 +148,4 @@ class Buffer:
|
|||
if w != self.reference_words[i]:
|
||||
count += 1
|
||||
|
||||
return count
|
||||
return count + len(self.reference_words) - len(self.user_words)
|
||||
|
|
|
@ -24,7 +24,7 @@ class RuntimeConfig:
|
|||
self.mode = mode
|
||||
|
||||
|
||||
def initialize(config_path, rbuffer, backspace_debug, no_cursor, runtime_config):
|
||||
def get_config(config_path):
|
||||
try:
|
||||
with open(os.path.expanduser(config_path)) as f:
|
||||
configmap = json.load(f)
|
||||
|
@ -32,6 +32,17 @@ def initialize(config_path, rbuffer, backspace_debug, no_cursor, runtime_config)
|
|||
configmap = {}
|
||||
|
||||
config = Config(configmap)
|
||||
return config
|
||||
|
||||
|
||||
def initialize(config, rbuffer, backspace_debug, no_cursor, runtime_config):
|
||||
if isinstance(config, str):
|
||||
config = get_config(config)
|
||||
|
||||
backspace_debug = (
|
||||
backspace_debug(config) if callable(backspace_debug) else backspace_debug
|
||||
)
|
||||
no_cursor = no_cursor(config) if callable(no_cursor) else no_cursor
|
||||
|
||||
text_box = TextBox(config)
|
||||
summary = Summary(config)
|
||||
|
@ -71,13 +82,21 @@ def get_parser():
|
|||
"-b",
|
||||
action="store_true",
|
||||
help="unclutter backspace, when it raises ctrl+backspace instead",
|
||||
default=False,
|
||||
default=ValueFromConfig("unclutter_backspace"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-cursor",
|
||||
"-n",
|
||||
action="store_true",
|
||||
help="disable cursos",
|
||||
default=False,
|
||||
default=ValueFromConfig("no_cursor"),
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
class ValueFromConfig:
|
||||
def __init__(self, key):
|
||||
self.key = key
|
||||
|
||||
def __call__(self, config):
|
||||
return config.get(self.key)
|
||||
|
|
|
@ -26,6 +26,15 @@ class Config:
|
|||
"summary_color": 9,
|
||||
"summary_lines": 4,
|
||||
"summary_border": 0,
|
||||
"random_capitalization": 0,
|
||||
"random_punctuation": 0,
|
||||
"sentence_mode": False,
|
||||
"punctuation": ",.?!;:",
|
||||
"sentence_ending": ".?!",
|
||||
"amount": 25,
|
||||
"language": "english",
|
||||
"no_cursor": False,
|
||||
"unclutter_backspace": False,
|
||||
}
|
||||
|
||||
def __init__(self, configmap):
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from .cli import initialize, get_parser, RuntimeConfig
|
||||
from random import choice
|
||||
from .cli import initialize, get_parser, RuntimeConfig, get_config, ValueFromConfig
|
||||
from random import choice, random
|
||||
import os
|
||||
import requests
|
||||
import pathlib
|
||||
|
@ -33,23 +33,135 @@ def get_words(language):
|
|||
return words
|
||||
|
||||
|
||||
def generate_text(
|
||||
words,
|
||||
amount,
|
||||
capitalization_factor,
|
||||
punctuation_factor,
|
||||
sentence_mode,
|
||||
punctuation,
|
||||
sentence_ending,
|
||||
):
|
||||
chosen_words = []
|
||||
|
||||
new_sentence = True
|
||||
for _ in range(amount):
|
||||
word = choice(words)
|
||||
|
||||
if sentence_mode and new_sentence:
|
||||
word = word.capitalize()
|
||||
new_sentence = False
|
||||
elif capitalization_factor and capitalization_factor / 100 > random():
|
||||
word = word.capitalize()
|
||||
|
||||
if punctuation_factor and punctuation_factor / 100 > random():
|
||||
chosen_punctuation = choice(punctuation)
|
||||
word = "".join((word, chosen_punctuation))
|
||||
|
||||
new_sentence = word[-1] in sentence_ending
|
||||
chosen_words.append(word)
|
||||
|
||||
return " ".join(chosen_words)
|
||||
|
||||
|
||||
def runner():
|
||||
parser = get_parser()
|
||||
|
||||
parser.add_argument(
|
||||
"amount", type=int, default=25, help="Amount of words", nargs="?"
|
||||
"amount",
|
||||
type=int,
|
||||
default=ValueFromConfig("amount"),
|
||||
help="Amount of words",
|
||||
nargs="?",
|
||||
)
|
||||
parser.add_argument(
|
||||
"language", type=str, default="english", help="Language", nargs="?"
|
||||
"language",
|
||||
type=str,
|
||||
default=ValueFromConfig("language"),
|
||||
help="Language",
|
||||
nargs="?",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-rc",
|
||||
"--random-capitalization",
|
||||
type=int,
|
||||
default=ValueFromConfig("random_capitalization"),
|
||||
help="Percent of randomally capitalized words",
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-rp",
|
||||
"--random-punctuation",
|
||||
type=int,
|
||||
default=ValueFromConfig("random_capitalization"),
|
||||
help="Percent of words that will have randomally appender punctuation at the end",
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-sm",
|
||||
"--sentence-mode",
|
||||
default=ValueFromConfig("sentence_mode"),
|
||||
help="Forces uppercase after sentence ending characters like dot or question mark and at the beggining of text",
|
||||
action="store_true",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--punctuation",
|
||||
type=str,
|
||||
default=ValueFromConfig("punctuation"),
|
||||
help="List of all punctuation characters",
|
||||
action="store",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sentence-ending",
|
||||
type=str,
|
||||
default=ValueFromConfig("sentence_ending"),
|
||||
help="List of all punctuation characters that end sentence",
|
||||
action="store",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
words = get_words(args.language)
|
||||
config = get_config(args.config)
|
||||
|
||||
random_punctuation = (
|
||||
args.random_punctuation(config)
|
||||
if callable(args.random_punctuation)
|
||||
else args.random_punctuation
|
||||
)
|
||||
random_capitalization = (
|
||||
args.random_capitalization(config)
|
||||
if callable(args.random_capitalization)
|
||||
else args.random_capitalization
|
||||
)
|
||||
sentence_mode = (
|
||||
args.sentence_mode(config)
|
||||
if callable(args.sentence_mode)
|
||||
else args.sentence_mode
|
||||
)
|
||||
punctuation = (
|
||||
args.punctuation(config) if callable(args.punctuation) else args.punctuation
|
||||
)
|
||||
sentence_ending = (
|
||||
args.sentence_ending(config)
|
||||
if callable(args.sentence_ending)
|
||||
else args.sentence_ending
|
||||
)
|
||||
amount = args.amount(config) if callable(args.amount) else args.amount
|
||||
language = args.language(config) if callable(args.language) else args.language
|
||||
|
||||
words = get_words(language)
|
||||
|
||||
while True:
|
||||
rbuffer = " ".join([choice(words) for _ in range(args.amount)])
|
||||
rbuffer = generate_text(
|
||||
words,
|
||||
amount,
|
||||
random_capitalization,
|
||||
random_punctuation,
|
||||
sentence_mode,
|
||||
punctuation,
|
||||
sentence_ending,
|
||||
)
|
||||
initialize(
|
||||
args.config,
|
||||
config,
|
||||
rbuffer,
|
||||
args.unclutter_backspace,
|
||||
args.no_cursor,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[bumpversion]
|
||||
current_version = 2.3.0
|
||||
current_version = 2.4.2
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
|
2
setup.py
2
setup.py
|
@ -9,7 +9,7 @@ with open("requirements.txt", "r", encoding="utf-8") as fh:
|
|||
|
||||
setup(
|
||||
name="fasttyper",
|
||||
version="2.3.0",
|
||||
version="2.4.2",
|
||||
author="Piotr Domanski",
|
||||
author_email="pi.domanski@gmail.com",
|
||||
description="Minimalistic typing exercise",
|
||||
|
|
Loading…
Reference in a new issue