vimniscient 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vimniscient/__init__.py +3 -0
- vimniscient/__main__.py +3 -0
- vimniscient/answer.py +113 -0
- vimniscient/app.py +310 -0
- vimniscient/cards/foot.yaml +19 -0
- vimniscient/cards/lazyvim.yaml +80 -0
- vimniscient/cards/shell.yaml +22 -0
- vimniscient/cards/vim.yaml +95 -0
- vimniscient/cards/zathura.yaml +32 -0
- vimniscient/cards.py +200 -0
- vimniscient/cli.py +98 -0
- vimniscient/drills/omarchy.yaml +88 -0
- vimniscient/drills.py +198 -0
- vimniscient/endless.py +60 -0
- vimniscient/generate.py +102 -0
- vimniscient/habits.py +116 -0
- vimniscient/keys.py +14 -0
- vimniscient/lessons/sources.md +59 -0
- vimniscient/lessons/w1d1-words.yaml +223 -0
- vimniscient/lessons/w1d2-pairs.yaml +188 -0
- vimniscient/lessons/w1d3-sentences.yaml +247 -0
- vimniscient/lessons/w1d4-find.yaml +184 -0
- vimniscient/lessons/w1d5-lines.yaml +249 -0
- vimniscient/lessons/w1d6-counts.yaml +351 -0
- vimniscient/lessons/w1d7-review.yaml +222 -0
- vimniscient/lessons/w2d1-dot.yaml +195 -0
- vimniscient/lessons/w2d2-search.yaml +194 -0
- vimniscient/lessons/w2d3-cgn.yaml +196 -0
- vimniscient/lessons/w2d4-case-numbers.yaml +201 -0
- vimniscient/lessons/w2d5-insert.yaml +229 -0
- vimniscient/lessons/w2d6-visual.yaml +206 -0
- vimniscient/lessons/w2d7-review.yaml +197 -0
- vimniscient/lessons/w3d1-macros.yaml +240 -0
- vimniscient/lessons/w3d2-macros-more.yaml +170 -0
- vimniscient/lessons/w3d3-registers.yaml +230 -0
- vimniscient/lessons/w3d4-marks.yaml +188 -0
- vimniscient/lessons/w3d5-visual-block.yaml +201 -0
- vimniscient/lessons/w3d6-substitute.yaml +290 -0
- vimniscient/lessons/w3d7-review.yaml +204 -0
- vimniscient/lessons/w4d1-ranges.yaml +214 -0
- vimniscient/lessons/w4d2-sort-filter.yaml +243 -0
- vimniscient/lessons/w4d3-regex.yaml +167 -0
- vimniscient/lessons/w4d4-norm.yaml +158 -0
- vimniscient/lessons/w4d5-motions.yaml +149 -0
- vimniscient/lessons/w4d6-indent.yaml +143 -0
- vimniscient/lessons/w4d7-review.yaml +175 -0
- vimniscient/lessons/w5d1-arguments.yaml +133 -0
- vimniscient/lessons/w5d2-any-quote-bracket.yaml +136 -0
- vimniscient/lessons/w5d3-functions.yaml +193 -0
- vimniscient/lessons/w5d4-small-objects.yaml +185 -0
- vimniscient/lessons/w5d5-comments.yaml +161 -0
- vimniscient/lessons/w5d6-review.yaml +120 -0
- vimniscient/lessons/w6d1-code.yaml +157 -0
- vimniscient/lessons/w6d2-notes.yaml +163 -0
- vimniscient/lessons/w6d3-data.yaml +145 -0
- vimniscient/lessons.py +184 -0
- vimniscient/mnemonics.py +141 -0
- vimniscient/mnemonics.yaml +124 -0
- vimniscient/nvim/keymaps.lua +25 -0
- vimniscient/nvim/runner.lua +711 -0
- vimniscient/progress.py +496 -0
- vimniscient/reminders.py +220 -0
- vimniscient/session.py +152 -0
- vimniscient/settings.py +33 -0
- vimniscient/srs.py +56 -0
- vimniscient/today.py +189 -0
- vimniscient/ui/__init__.py +1 -0
- vimniscient/ui/builtin_lessons.py +99 -0
- vimniscient/ui/cards.py +160 -0
- vimniscient/ui/common.py +129 -0
- vimniscient/ui/drills.py +163 -0
- vimniscient/ui/lesson.py +193 -0
- vimniscient/ui/main.py +217 -0
- vimniscient/ui/stats.py +145 -0
- vimniscient/vimgolf.py +97 -0
- vimniscient-0.2.0.dist-info/METADATA +322 -0
- vimniscient-0.2.0.dist-info/RECORD +80 -0
- vimniscient-0.2.0.dist-info/WHEEL +4 -0
- vimniscient-0.2.0.dist-info/entry_points.txt +2 -0
- vimniscient-0.2.0.dist-info/licenses/LICENSE +661 -0
vimniscient/generate.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Practice texts made from a seed, for skills where any sentence will do.
|
|
2
|
+
|
|
3
|
+
A lesson task can name a `generate` kind. Its Learn round keeps the authored
|
|
4
|
+
text; Recall and reviews can get a generated one instead, so a skill gets
|
|
5
|
+
practised on sentences you haven't seen. The same seed always gives the same
|
|
6
|
+
task, so a run can be replayed and every kind can be checked in Neovim
|
|
7
|
+
(tests/check_generate.py).
|
|
8
|
+
|
|
9
|
+
The sentences stay mnemonic: they're the hooks from mnemonics.yaml (what keys
|
|
10
|
+
stand for) and facts from lessons/sources.md.
|
|
11
|
+
|
|
12
|
+
Kinds, all word text objects (w1d1):
|
|
13
|
+
ciw-typo two letters of a word swapped; ciw and type it right
|
|
14
|
+
daw-double a word doubled; daw deletes one copy and its space
|
|
15
|
+
ciW-dotted a word written with dots between its letters; ciW rewrites it
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import random
|
|
19
|
+
import re
|
|
20
|
+
from dataclasses import replace
|
|
21
|
+
from functools import cache
|
|
22
|
+
|
|
23
|
+
from vimniscient.lessons import Task
|
|
24
|
+
from vimniscient.mnemonics import _tables
|
|
25
|
+
|
|
26
|
+
KINDS = ("ciw-typo", "daw-double", "ciW-dotted")
|
|
27
|
+
|
|
28
|
+
# Facts from lessons/sources.md, one sentence each.
|
|
29
|
+
FACTS = [
|
|
30
|
+
"Ebbinghaus tested himself on nonsense syllables and published his results in 1885.",
|
|
31
|
+
"Forgetting is steepest right after learning, then the curve levels off.",
|
|
32
|
+
"Miller wrote about the magical number seven, plus or minus two.",
|
|
33
|
+
"Cowan later argued that short-term memory holds about four chunks.",
|
|
34
|
+
"Retrieval practice beat restudying on a test a week later.",
|
|
35
|
+
"Restudying looked better after five minutes, which is why it feels like it works.",
|
|
36
|
+
"Spacing reviews out is among the best replicated findings about memory.",
|
|
37
|
+
"Sleep after learning helps new memories stabilise.",
|
|
38
|
+
"Rereading makes text feel familiar, and people mistake that for knowing it.",
|
|
39
|
+
"Words read aloud are remembered better than words read silently.",
|
|
40
|
+
"Mixed practice feels harder than blocked practice but tests better later.",
|
|
41
|
+
"Practising with variation helps a skill carry over to new conditions.",
|
|
42
|
+
"Trying to recall an answer before seeing it helps you remember it later.",
|
|
43
|
+
"Fitts and Posner described three stages of skill: cognitive, associative and autonomous.",
|
|
44
|
+
"Desirable difficulties make learning feel harder but make it last.",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@cache
|
|
49
|
+
def sentences() -> list[str]:
|
|
50
|
+
hooks = list(dict.fromkeys(hook for table in _tables()[:2] for hook in table.values()))
|
|
51
|
+
# as written: a capital would change the key ("u is down" is not "U is down")
|
|
52
|
+
return [h + ("" if h.endswith(".") else ".") for h in hooks] + FACTS
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_WORD = re.compile(r"(?<![\w'.-])[A-Za-z]{4,}(?![\w'-])")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _words(sentence: str, spaced: bool) -> list[re.Match]:
|
|
59
|
+
"""Plain words of four letters or more; `spaced` wants a space on both sides (a WORD)."""
|
|
60
|
+
found = list(_WORD.finditer(sentence))
|
|
61
|
+
if spaced:
|
|
62
|
+
found = [m for m in found if m.start() > 0 and sentence[m.start() - 1] == " "
|
|
63
|
+
and m.end() < len(sentence) and sentence[m.end()] == " "]
|
|
64
|
+
else: # daw and ciw want the word to have a space after it, or anything but a letter
|
|
65
|
+
found = [m for m in found if m.end() < len(sentence)]
|
|
66
|
+
return found
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def generate(kind: str, seed: str, parent: Task) -> Task:
|
|
70
|
+
"""A `kind` task from `seed`, with the parent task's hint and place in its lesson."""
|
|
71
|
+
rng = random.Random(f"{kind}:{seed}")
|
|
72
|
+
while True:
|
|
73
|
+
sentence = rng.choice(sentences())
|
|
74
|
+
words = _words(sentence, spaced=kind == "ciW-dotted")
|
|
75
|
+
if kind == "daw-double":
|
|
76
|
+
words = [m for m in words if sentence[m.end()] == " "]
|
|
77
|
+
if kind == "ciw-typo":
|
|
78
|
+
words = [m for m in words if len(set(m.group()[1:-1])) > 1]
|
|
79
|
+
if words:
|
|
80
|
+
break
|
|
81
|
+
m = rng.choice(words)
|
|
82
|
+
word, before, after = m.group(), sentence[: m.start()], sentence[m.end():]
|
|
83
|
+
if kind == "ciw-typo":
|
|
84
|
+
while True:
|
|
85
|
+
i = rng.randrange(1, len(word) - 2)
|
|
86
|
+
if word[i] != word[i + 1]:
|
|
87
|
+
break
|
|
88
|
+
shown = word[:i] + word[i + 1] + word[i] + word[i + 2:]
|
|
89
|
+
start, cursor = before + shown + after, len(before) + rng.randrange(len(shown))
|
|
90
|
+
goal, solution = f'Fix "{shown}" to "{word}".', f"ciw{word}<Esc>"
|
|
91
|
+
elif kind == "daw-double":
|
|
92
|
+
start = before + word + " " + word + after
|
|
93
|
+
cursor = len(before) + rng.choice([0, len(word) + 1]) + rng.randrange(len(word))
|
|
94
|
+
goal, solution = f'Delete the doubled "{word}".', "daw"
|
|
95
|
+
elif kind == "ciW-dotted":
|
|
96
|
+
shown = ".".join(word)
|
|
97
|
+
start, cursor = before + shown + after, len(before) + rng.randrange(len(shown))
|
|
98
|
+
goal, solution = f'Write the dotted "{shown}" as "{word}".', f"ciW{word}<Esc>"
|
|
99
|
+
else:
|
|
100
|
+
raise ValueError(f"no generator called {kind}")
|
|
101
|
+
return replace(parent, goal=goal, start=[start], cursor=(1, cursor), target=[sentence],
|
|
102
|
+
solution=solution, ft="", variants=[], generated=seed)
|
vimniscient/habits.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Bad habits, read from hardtime.nvim's log (~/.local/state/nvim/hardtime.nvim.log).
|
|
2
|
+
|
|
3
|
+
hardtime writes three kinds of message:
|
|
4
|
+
Use dj instead of 2dd -> a card: what's shorter than 2dd?
|
|
5
|
+
You pressed the j key too soon! Use [count]j… -> drill the motion lessons
|
|
6
|
+
The <Up> key is disabled! -> same, for arrow keys
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
from collections import Counter
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import date, datetime, timedelta
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from vimniscient.cards import Card, Deck
|
|
17
|
+
from vimniscient.keys import split_keys
|
|
18
|
+
|
|
19
|
+
LOG = Path(os.environ.get("XDG_STATE_HOME", Path.home() / ".local/state")) / "nvim" / "hardtime.nvim.log"
|
|
20
|
+
|
|
21
|
+
_LINE = re.compile(r"^\[\w+\s+\w{3} (\d{2} \w{3} \d{4}) [^\]]*\] (.+)$")
|
|
22
|
+
_USE = re.compile(r"^Use (.+) instead of (.+)$")
|
|
23
|
+
_TOO_SOON = re.compile(r"^You pressed the (\S+) key too soon!")
|
|
24
|
+
_DISABLED = re.compile(r"^The (\S+) key is disabled!")
|
|
25
|
+
|
|
26
|
+
# Keys pressed over and over -> lessons that teach the better motions.
|
|
27
|
+
DRILL_LESSONS = {
|
|
28
|
+
"j": ["w1d6-counts", "w2d2-search", "w1d3-sentences"],
|
|
29
|
+
"k": ["w1d6-counts", "w2d2-search", "w1d3-sentences"],
|
|
30
|
+
"h": ["w1d4-find", "w1d1-words"],
|
|
31
|
+
"l": ["w1d4-find", "w1d1-words"],
|
|
32
|
+
}
|
|
33
|
+
_ARROWS = {"<Down>": "j", "<Up>": "k", "<Left>": "h", "<Right>": "l"}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Habit:
|
|
38
|
+
message: str
|
|
39
|
+
total: int
|
|
40
|
+
recent: int # in the last two weeks
|
|
41
|
+
last: date
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_log() -> list[tuple[date, str]]:
|
|
45
|
+
if not LOG.exists():
|
|
46
|
+
return []
|
|
47
|
+
entries = []
|
|
48
|
+
for line in LOG.read_text(errors="replace").splitlines():
|
|
49
|
+
m = _LINE.match(line.strip())
|
|
50
|
+
if not m:
|
|
51
|
+
continue
|
|
52
|
+
try:
|
|
53
|
+
day = datetime.strptime(m.group(1), "%d %b %Y").date()
|
|
54
|
+
except ValueError:
|
|
55
|
+
continue
|
|
56
|
+
entries.append((day, m.group(2).strip()))
|
|
57
|
+
return entries
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def habits(days: int = 14) -> list[Habit]:
|
|
61
|
+
"""Every distinct message, most frequent recently first."""
|
|
62
|
+
since = date.today() - timedelta(days=days)
|
|
63
|
+
total: Counter[str] = Counter()
|
|
64
|
+
recent: Counter[str] = Counter()
|
|
65
|
+
last: dict[str, date] = {}
|
|
66
|
+
for day, message in read_log():
|
|
67
|
+
total[message] += 1
|
|
68
|
+
if day >= since:
|
|
69
|
+
recent[message] += 1
|
|
70
|
+
last[message] = max(day, last.get(message, day))
|
|
71
|
+
return sorted(
|
|
72
|
+
(Habit(m, total[m], recent[m], last[m]) for m in total),
|
|
73
|
+
key=lambda h: (-h.recent, -h.total, h.message),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def habit_deck() -> Deck:
|
|
78
|
+
"""One card per "Use X instead of Y" hint, most frequent first."""
|
|
79
|
+
deck = Deck("habits", "Your habits")
|
|
80
|
+
for i, habit in enumerate(habits()):
|
|
81
|
+
m = _USE.match(habit.message)
|
|
82
|
+
if not m:
|
|
83
|
+
continue
|
|
84
|
+
better, worse = m.group(1), m.group(2)
|
|
85
|
+
n = len(split_keys(better))
|
|
86
|
+
deck.cards.append(
|
|
87
|
+
Card(
|
|
88
|
+
deck="habits",
|
|
89
|
+
id=f"habits:{habit.message}",
|
|
90
|
+
prompt=f"You typed {worse}. hardtime knows the same edit in {n} key{'' if n == 1 else 's'}: which?",
|
|
91
|
+
answers=[better],
|
|
92
|
+
style="keys",
|
|
93
|
+
case_sensitive=True,
|
|
94
|
+
note=f"hardtime caught this {habit.total}×.",
|
|
95
|
+
order=i,
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
return deck
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def drill_lessons(days: int = 7) -> list[str]:
|
|
102
|
+
"""Lessons worth repeating because of keys pressed over and over lately."""
|
|
103
|
+
since = date.today() - timedelta(days=days)
|
|
104
|
+
keys: Counter[str] = Counter()
|
|
105
|
+
for day, message in read_log():
|
|
106
|
+
if day < since:
|
|
107
|
+
continue
|
|
108
|
+
m = _TOO_SOON.match(message) or _DISABLED.match(message)
|
|
109
|
+
if m:
|
|
110
|
+
key = _ARROWS.get(m.group(1), m.group(1))
|
|
111
|
+
if key in DRILL_LESSONS:
|
|
112
|
+
keys[key] += 1
|
|
113
|
+
lessons: list[str] = []
|
|
114
|
+
for key, _ in keys.most_common():
|
|
115
|
+
lessons += [lesson for lesson in DRILL_LESSONS[key] if lesson not in lessons]
|
|
116
|
+
return lessons
|
vimniscient/keys.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Counting keystrokes written in Vim key notation (`ciwfoo<Esc>`)."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
_TOKEN = re.compile(r"<[^<>\s]+>|.", re.S)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def split_keys(keys: str) -> list[str]:
|
|
9
|
+
"""Split `ci"x<Esc>` into ['c', 'i', '"', 'x', '<Esc>']."""
|
|
10
|
+
return _TOKEN.findall(keys)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def count_keys(keys: str) -> int:
|
|
14
|
+
return len(split_keys(keys))
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Sources for the facts in the lesson texts
|
|
2
|
+
|
|
3
|
+
The lesson texts are mnemonic: they describe the key being practised (see
|
|
4
|
+
`../mnemonics.yaml`), and the rest is about the science of learning and memory.
|
|
5
|
+
Every factual claim in them should trace back to one of these.
|
|
6
|
+
|
|
7
|
+
- **Atkinson & Raugh (1975).** An application of the mnemonic keyword method to
|
|
8
|
+
the acquisition of a Russian vocabulary. *JEP: Human Learning and Memory*
|
|
9
|
+
1(2). The keyword method: link a foreign word to a sound-alike word and an
|
|
10
|
+
image.
|
|
11
|
+
- **Bjork (1994).** Memory and metamemory considerations in the training of
|
|
12
|
+
human beings. In *Metacognition*. "Desirable difficulties": conditions that
|
|
13
|
+
make learning feel harder (spacing, testing, interleaving) often make it last.
|
|
14
|
+
- **Cowan (2001).** The magical number 4 in short-term memory. *Behavioral and
|
|
15
|
+
Brain Sciences* 24(1). About four chunks.
|
|
16
|
+
- **Dunlosky, Rawson, Marsh, Nathan & Willingham (2013).** Improving students'
|
|
17
|
+
learning with effective learning techniques. *Psychological Science in the
|
|
18
|
+
Public Interest* 14(1). High utility: practice testing, distributed practice.
|
|
19
|
+
Moderate: interleaved practice, elaborative interrogation, self-explanation.
|
|
20
|
+
Low: highlighting, rereading, summarization, keyword mnemonic, imagery for
|
|
21
|
+
text.
|
|
22
|
+
- **Ebbinghaus (1885).** *Über das Gedächtnis.* Nonsense syllables, tested on
|
|
23
|
+
himself; the forgetting curve, steepest right after learning.
|
|
24
|
+
- **Fitts & Posner (1967).** *Human Performance.* Three stages of skill
|
|
25
|
+
learning: cognitive, associative, autonomous.
|
|
26
|
+
- **Miller (1956).** The magical number seven, plus or minus two. *Psychological
|
|
27
|
+
Review* 63(2).
|
|
28
|
+
- **Roediger & Karpicke (2006).** Test-enhanced learning. *Psychological
|
|
29
|
+
Science* 17(3). Retrieval practice beat restudying on a test a week later,
|
|
30
|
+
though restudying looked better after five minutes.
|
|
31
|
+
- **Spacing effect.** Among the best-replicated findings in memory research;
|
|
32
|
+
see Cepeda, Pashler, Vul, Wixted & Rohrer (2006), Distributed practice in
|
|
33
|
+
verbal recall tasks, *Psychological Bulletin* 132(3).
|
|
34
|
+
- **Sleep and consolidation.** Sleep after learning helps memories stabilise;
|
|
35
|
+
see Diekelmann & Born (2010), The memory function of sleep, *Nature Reviews
|
|
36
|
+
Neuroscience* 11(2).
|
|
37
|
+
- **Fluency illusion.** Rereading makes text feel familiar, which people
|
|
38
|
+
mistake for knowing it; see Bjork, Dunlosky & Kornell (2013), Self-regulated
|
|
39
|
+
learning, *Annual Review of Psychology* 64.
|
|
40
|
+
- **Production effect.** Words read aloud are remembered better than words
|
|
41
|
+
read silently; MacLeod, Gopie, Hourihan, Neary & Ozubko (2010), The
|
|
42
|
+
production effect, *JEP: Learning, Memory, and Cognition* 36(3).
|
|
43
|
+
- **Interleaving.** Mixed practice feels harder than blocked practice but
|
|
44
|
+
tests better later; Rohrer & Taylor (2007), The shuffling of mathematics
|
|
45
|
+
problems improves learning, *Instructional Science* 35.
|
|
46
|
+
- **Variability of practice.** Practising with variation helps a skill carry
|
|
47
|
+
over to new conditions; Kerr & Booth (1978), Specific and varied practice
|
|
48
|
+
of motor skill, *Perceptual and Motor Skills* 46(2).
|
|
49
|
+
- **Lag effect.** Longer gaps between reviews tend to beat shorter ones;
|
|
50
|
+
covered in Cepeda et al. (2006) above.
|
|
51
|
+
- **Life dates and years used:** Ebbinghaus born 1850, *Über das Gedächtnis*
|
|
52
|
+
1885; Miller 1956; Roediger & Karpicke 2006.
|
|
53
|
+
- **Recall before the answer.** Trying to retrieve an answer, even
|
|
54
|
+
unsuccessfully, before seeing it improves later memory of it; Kornell,
|
|
55
|
+
Hays & Bjork (2009), Unsuccessful retrieval attempts enhance subsequent
|
|
56
|
+
learning, *JEP: Learning, Memory, and Cognition* 35(4). This is why the
|
|
57
|
+
app shows what keys stand for only after you answer.
|
|
58
|
+
- **ed's `t`.** The ed manual: "(.,.)t(.) Copies (i.e., transfers) the
|
|
59
|
+
addressed lines". **grep** takes its name from ed's `g/re/p`.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
week: 1
|
|
2
|
+
day: 1
|
|
3
|
+
title: Word text objects
|
|
4
|
+
intro: |
|
|
5
|
+
Read a Vim command as a small sentence: a verb, then a noun. The verbs are
|
|
6
|
+
`c` *change*, `d` *delete* and `y` *yank* (Vim's old word for copy). The noun
|
|
7
|
+
is a text object: `i` *inner* or `a` (*a word*, or *around* it), then what
|
|
8
|
+
kind of thing, `w` for *word*. So `ciw` reads *change inner word*, and it
|
|
9
|
+
works from anywhere inside the word. `iw` is the word alone, `aw` takes its
|
|
10
|
+
space too. Capital `W` is a big WORD: everything between two spaces, dots
|
|
11
|
+
included.
|
|
12
|
+
keys: [ciw, diw, daw, ciW, yiw, viwp]
|
|
13
|
+
tasks:
|
|
14
|
+
- goal: 'Fix "while" to "whole" (the cursor is in the middle of the word).'
|
|
15
|
+
start: |-
|
|
16
|
+
ciw is change inner word: it swaps the wh▮ile word, wherever the cursor sits.
|
|
17
|
+
target: |-
|
|
18
|
+
ciw is change inner word: it swaps the whole word, wherever the cursor sits.
|
|
19
|
+
solution: 'ciwwhole<Esc>'
|
|
20
|
+
hint: 'c changes, i keeps it inner, w names the word: ciw, from anywhere inside it.'
|
|
21
|
+
generate: ciw-typo # Recall and reviews can get a new text (generate.py)
|
|
22
|
+
variants:
|
|
23
|
+
- goal: 'Fix "spaec" to "space" (the cursor is in the middle of the word).'
|
|
24
|
+
start: |-
|
|
25
|
+
The i in ciw means inner: just the word, never the spa▮ec around it.
|
|
26
|
+
target: |-
|
|
27
|
+
The i in ciw means inner: just the word, never the space around it.
|
|
28
|
+
solution: 'ciwspace<Esc>'
|
|
29
|
+
- goal: 'Fix "anywhrere" to "anywhere" (the cursor is in the middle of the word).'
|
|
30
|
+
start: |-
|
|
31
|
+
Change inner word: c is the verb, iw the noun, and the cursor sits anywh▮rere in it.
|
|
32
|
+
target: |-
|
|
33
|
+
Change inner word: c is the verb, iw the noun, and the cursor sits anywhere in it.
|
|
34
|
+
solution: 'ciwanywhere<Esc>'
|
|
35
|
+
- goal: 'Fix "memroy" to "memory" (the cursor is in the middle of the word).'
|
|
36
|
+
start: |-
|
|
37
|
+
Testing yourself beats rereading: type ciw from mem▮roy, not from the page.
|
|
38
|
+
target: |-
|
|
39
|
+
Testing yourself beats rereading: type ciw from memory, not from the page.
|
|
40
|
+
solution: 'ciwmemory<Esc>'
|
|
41
|
+
- goal: 'Fix "undersocres" to "underscores" (the cursor is in the middle of the word).'
|
|
42
|
+
start: |-
|
|
43
|
+
In ciw, the w is a word: letters, digits and under▮socres, nothing else.
|
|
44
|
+
target: |-
|
|
45
|
+
In ciw, the w is a word: letters, digits and underscores, nothing else.
|
|
46
|
+
solution: 'ciwunderscores<Esc>'
|
|
47
|
+
- goal: 'Delete one "own", including its space.'
|
|
48
|
+
start: |-
|
|
49
|
+
The a in daw is around: the word and its own ▮own space.
|
|
50
|
+
target: |-
|
|
51
|
+
The a in daw is around: the word and its own space.
|
|
52
|
+
solution: 'daw'
|
|
53
|
+
hint: 'd deletes, a takes the word with its space, w is the word: daw leaves no double space.'
|
|
54
|
+
generate: daw-double # Recall and reviews can get a new text (generate.py)
|
|
55
|
+
variants:
|
|
56
|
+
- goal: 'Delete the second "behind".'
|
|
57
|
+
start: |-
|
|
58
|
+
daw leaves no double space behind be▮hind.
|
|
59
|
+
target: |-
|
|
60
|
+
daw leaves no double space behind.
|
|
61
|
+
solution: 'daw'
|
|
62
|
+
- goal: 'Delete one "its", including its space.'
|
|
63
|
+
start: |-
|
|
64
|
+
daw takes a word and what is around it, its ▮its trailing space.
|
|
65
|
+
target: |-
|
|
66
|
+
daw takes a word and what is around it, its trailing space.
|
|
67
|
+
- goal: 'Delete one "himself".'
|
|
68
|
+
start: |-
|
|
69
|
+
Ebbinghaus tested ▮himself himself on nonsense syllables; daw cuts the echo.
|
|
70
|
+
target: |-
|
|
71
|
+
Ebbinghaus tested himself on nonsense syllables; daw cuts the echo.
|
|
72
|
+
- goal: 'Delete one "so".'
|
|
73
|
+
start: |-
|
|
74
|
+
With daw, a means around: the word goes, and ▮so so does one space.
|
|
75
|
+
target: |-
|
|
76
|
+
With daw, a means around: the word goes, and so does one space.
|
|
77
|
+
- goal: 'Write the dotted "e.v.e.r.y.t.h.i.n.g" as "everything".'
|
|
78
|
+
start: |-
|
|
79
|
+
Big W, big WORD: ciW takes e.v.e.r.y▮.t.h.i.n.g between two spaces.
|
|
80
|
+
target: |-
|
|
81
|
+
Big W, big WORD: ciW takes everything between two spaces.
|
|
82
|
+
solution: 'ciWeverything<Esc>'
|
|
83
|
+
hint: 'A WORD (capital W) runs from space to space, dots included. Try ciW.'
|
|
84
|
+
generate: ciW-dotted # Recall and reviews can get a new text (generate.py)
|
|
85
|
+
variants:
|
|
86
|
+
- goal: 'Replace the whole URL with "a URL".'
|
|
87
|
+
start: |-
|
|
88
|
+
ciW sees www.wa▮ls.info as one WORD: dots do not split it.
|
|
89
|
+
target: |-
|
|
90
|
+
ciW sees a URL as one WORD: dots do not split it.
|
|
91
|
+
solution: 'ciWa URL<Esc>'
|
|
92
|
+
- goal: 'Replace "e.g." with "for example".'
|
|
93
|
+
start: |-
|
|
94
|
+
A WORD runs from space to space, e.▮g. a dotted abbreviation.
|
|
95
|
+
target: |-
|
|
96
|
+
A WORD runs from space to space, for example a dotted abbreviation.
|
|
97
|
+
solution: 'ciWfor example<Esc>'
|
|
98
|
+
- goal: 'Replace the path with "a path".'
|
|
99
|
+
start: |-
|
|
100
|
+
ciW sees ~/notes/vi▮m.md as one WORD, slashes and all.
|
|
101
|
+
target: |-
|
|
102
|
+
ciW sees a path as one WORD, slashes and all.
|
|
103
|
+
solution: 'ciWa path<Esc>'
|
|
104
|
+
- goal: 'Replace "Fitts&Posner(1967)" with "Fitts-and-Posner".'
|
|
105
|
+
start: |-
|
|
106
|
+
ciW: from space to space, Fitts&Pos▮ner(1967) counts as one WORD.
|
|
107
|
+
target: |-
|
|
108
|
+
ciW: from space to space, Fitts-and-Posner counts as one WORD.
|
|
109
|
+
solution: 'ciWFitts-and-Posner<Esc>'
|
|
110
|
+
- goal: 'Spell the function name out as change_inner_word.'
|
|
111
|
+
ft: python
|
|
112
|
+
start: |-
|
|
113
|
+
def ▮chg_in_wd(word, new):
|
|
114
|
+
"""c = change, i = inner, w = word."""
|
|
115
|
+
return new
|
|
116
|
+
target: |-
|
|
117
|
+
def change_inner_word(word, new):
|
|
118
|
+
"""c = change, i = inner, w = word."""
|
|
119
|
+
return new
|
|
120
|
+
solution: 'ciwchange_inner_word<Esc>'
|
|
121
|
+
hint: 'Underscores belong to a word, so ciw takes the whole name.'
|
|
122
|
+
variants:
|
|
123
|
+
- goal: 'Spell the function name out as delete_a_word.'
|
|
124
|
+
start: |-
|
|
125
|
+
def ▮del_a_wd(text):
|
|
126
|
+
"""d = delete, a = a word and its space, w = word."""
|
|
127
|
+
return text
|
|
128
|
+
target: |-
|
|
129
|
+
def delete_a_word(text):
|
|
130
|
+
"""d = delete, a = a word and its space, w = word."""
|
|
131
|
+
return text
|
|
132
|
+
solution: 'ciwdelete_a_word<Esc>'
|
|
133
|
+
- goal: 'Spell the function name out as change_inner_WORD.'
|
|
134
|
+
start: |-
|
|
135
|
+
def ▮chg_in_WD(text, new):
|
|
136
|
+
"""c = change, i = inner, W = WORD, space to space."""
|
|
137
|
+
return new
|
|
138
|
+
target: |-
|
|
139
|
+
def change_inner_WORD(text, new):
|
|
140
|
+
"""c = change, i = inner, W = WORD, space to space."""
|
|
141
|
+
return new
|
|
142
|
+
solution: 'ciwchange_inner_WORD<Esc>'
|
|
143
|
+
- goal: 'Spell the function name out as yank_inner_word.'
|
|
144
|
+
start: |-
|
|
145
|
+
def ▮ynk_in_wd(text):
|
|
146
|
+
"""y = yank, Vim's older word for copy; i = inner; w = word."""
|
|
147
|
+
return text
|
|
148
|
+
target: |-
|
|
149
|
+
def yank_inner_word(text):
|
|
150
|
+
"""y = yank, Vim's older word for copy; i = inner; w = word."""
|
|
151
|
+
return text
|
|
152
|
+
solution: 'ciwyank_inner_word<Esc>'
|
|
153
|
+
- goal: 'Spell the function name out as snake_case_name.'
|
|
154
|
+
start: |-
|
|
155
|
+
def ▮snk_cs_nm(name):
|
|
156
|
+
"""ciw takes it all: underscores belong to the word."""
|
|
157
|
+
return name
|
|
158
|
+
target: |-
|
|
159
|
+
def snake_case_name(name):
|
|
160
|
+
"""ciw takes it all: underscores belong to the word."""
|
|
161
|
+
return name
|
|
162
|
+
solution: 'ciwsnake_case_name<Esc>'
|
|
163
|
+
- goal: 'Copy "word" over "noun".'
|
|
164
|
+
start: |-
|
|
165
|
+
viwp puts the ▮word you yanked over the other noun
|
|
166
|
+
target: |-
|
|
167
|
+
viwp puts the word you yanked over the other word
|
|
168
|
+
solution: 'yiw$viwp'
|
|
169
|
+
hint: 'yiw yanks the inner word; $ jumps to the last one; viwp selects it and puts the yank over it.'
|
|
170
|
+
variants:
|
|
171
|
+
- goal: 'Copy "verb" over "noun".'
|
|
172
|
+
start: |-
|
|
173
|
+
Put is a ▮verb, and yank is a noun.
|
|
174
|
+
target: |-
|
|
175
|
+
Put is a verb, and yank is a verb.
|
|
176
|
+
solution: 'yiw$bviwp'
|
|
177
|
+
- goal: 'Copy "yank" over "paste".'
|
|
178
|
+
start: |-
|
|
179
|
+
viwp swaps a selection for the ▮yank, so select, then put the paste
|
|
180
|
+
target: |-
|
|
181
|
+
viwp swaps a selection for the yank, so select, then put the yank
|
|
182
|
+
solution: 'yiw$viwp'
|
|
183
|
+
- goal: 'Copy "spaced" over "massed".'
|
|
184
|
+
start: |-
|
|
185
|
+
▮spaced reviews beat massed ones, so keep them massed
|
|
186
|
+
target: |-
|
|
187
|
+
spaced reviews beat massed ones, so keep them spaced
|
|
188
|
+
solution: 'yiw$viwp'
|
|
189
|
+
- goal: 'Copy "mixed" over the last "blocked".'
|
|
190
|
+
start: |-
|
|
191
|
+
Rohrer and Taylor: blocked practice feels better than ▮mixed, yet scores below blocked
|
|
192
|
+
target: |-
|
|
193
|
+
Rohrer and Taylor: blocked practice feels better than mixed, yet scores below mixed
|
|
194
|
+
solution: 'yiw$viwp'
|
|
195
|
+
- goal: 'Delete the doubled "the".'
|
|
196
|
+
start: |-
|
|
197
|
+
daw: delete a word, and ▮the the space after it.
|
|
198
|
+
target: |-
|
|
199
|
+
daw: delete a word, and the space after it.
|
|
200
|
+
solution: 'daw'
|
|
201
|
+
hint: 'daw takes the word and its trailing space.'
|
|
202
|
+
generate: daw-double # Recall and reviews can get a new text (generate.py)
|
|
203
|
+
variants:
|
|
204
|
+
- goal: 'Delete the doubled "like".'
|
|
205
|
+
start: |-
|
|
206
|
+
Doubled words ▮like like this one are what daw is for.
|
|
207
|
+
target: |-
|
|
208
|
+
Doubled words like this one are what daw is for.
|
|
209
|
+
- goal: 'Delete the doubled "four".'
|
|
210
|
+
start: |-
|
|
211
|
+
Cowan put short-term memory at about ▮four four chunks.
|
|
212
|
+
target: |-
|
|
213
|
+
Cowan put short-term memory at about four chunks.
|
|
214
|
+
- goal: 'Delete the doubled "familiar".'
|
|
215
|
+
start: |-
|
|
216
|
+
Rereading builds a fluency illusion: the text feels ▮familiar familiar, not known.
|
|
217
|
+
target: |-
|
|
218
|
+
Rereading builds a fluency illusion: the text feels familiar, not known.
|
|
219
|
+
- goal: 'Delete the doubled "and".'
|
|
220
|
+
start: |-
|
|
221
|
+
daw: d for delete, a for a word ▮and and its space.
|
|
222
|
+
target: |-
|
|
223
|
+
daw: d for delete, a for a word and its space.
|