teletype 1.0.1 → 1.2.0
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.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/bin/teletype +58 -7
- data/lib/teletype/page.rb +4 -10
- data/lib/teletype/paginator.rb +57 -0
- data/lib/teletype/practice.rb +15 -16
- data/lib/teletype/screen.rb +4 -1
- data/lib/teletype/stats.rb +8 -10
- data/lib/teletype.rb +1 -1
- metadata +18 -4
- data/lib/teletype/pager.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40a3e0c38deacc68555c1440a9fe679afc623e93decc137836a3066aa6710521
|
4
|
+
data.tar.gz: 640e87ce91e95e40b37e5f768e70ae0e355f68f810c51b1862e2fcdf6dd1ddd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b885502503555e6d5874eeb897928d37db231e72d6467438ebbeba43d810b3a0e8ad7415c98eb7330017e38fe48465732a495f96ad1f2e3ca521e8184117453
|
7
|
+
data.tar.gz: 5146ce812b50f336daa87cbaa256c1227c49481e32921e9235cc026bc7238dd8e7712cb8ddd5d869b0db64b2cef233d6291cddc9b51d1178926e4d995d7a213e
|
data/README.md
CHANGED
data/bin/teletype
CHANGED
@@ -1,16 +1,54 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'optparse'
|
4
5
|
require_relative '../lib/teletype'
|
5
6
|
|
6
7
|
exercises = File.join(File.dirname(__FILE__), '../', 'exercise')
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
profile = nil
|
10
|
+
height = 5
|
11
|
+
width = 120
|
12
|
+
suggest = 3
|
13
|
+
verbose = false
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = 'Usage: teletype [options] [exercise]'
|
17
|
+
|
18
|
+
opts.on('--help', 'Prints this help') do
|
19
|
+
puts opts
|
20
|
+
exit
|
11
21
|
end
|
12
|
-
|
13
|
-
|
22
|
+
|
23
|
+
opts.on('-l', '--list', 'List exercise files') do |_v|
|
24
|
+
Dir.glob(File.join(exercises, '**', '*.*')).each do |path|
|
25
|
+
puts path.sub(File.join(exercises, ''), '')
|
26
|
+
end
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-p', '--profile [DIR]', 'Specify profile dir. Defaults to ~/.teletype') do |f|
|
31
|
+
profile = f
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-s', '--suggest [LINES]', Integer,
|
35
|
+
"Suggested practice lines that needs attention. Defaults to #{suggest}") do |s|
|
36
|
+
suggest = s
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on('-h', '--height [HEIGHT]', Integer, "Height of practice window. Defaults to #{height}") do |h|
|
40
|
+
height = h
|
41
|
+
end
|
42
|
+
opts.on('-w', '--width [WIDTH]', Integer, "Width of practice window. Defaults to #{width}") do |w|
|
43
|
+
width = w
|
44
|
+
end
|
45
|
+
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |v|
|
46
|
+
verbose = v
|
47
|
+
end
|
48
|
+
end.parse!
|
49
|
+
|
50
|
+
profile ||= File.join(Dir.home, '.teletype')
|
51
|
+
Dir.mkdir(profile) unless File.exist?(profile)
|
14
52
|
|
15
53
|
paths = ARGV
|
16
54
|
paths << 'base' if paths.length.zero?
|
@@ -28,5 +66,18 @@ paths.each do |path|
|
|
28
66
|
end
|
29
67
|
end
|
30
68
|
|
31
|
-
|
32
|
-
|
69
|
+
screen = Teletype::Screen.new(height, width, verbose: verbose)
|
70
|
+
stats = Teletype::Stats.new(profile, text)
|
71
|
+
paginator = Teletype::Paginator.new(
|
72
|
+
profile, text,
|
73
|
+
suggest: suggest,
|
74
|
+
screen: screen,
|
75
|
+
stats: stats
|
76
|
+
)
|
77
|
+
|
78
|
+
at_exit do
|
79
|
+
stats.save
|
80
|
+
paginator.save
|
81
|
+
end
|
82
|
+
|
83
|
+
paginator.run
|
data/lib/teletype/page.rb
CHANGED
@@ -7,24 +7,18 @@ module Teletype
|
|
7
7
|
@lines = lines.map { |line| line.gsub("\t", '⇥') }
|
8
8
|
@screen = screen
|
9
9
|
@stats = stats
|
10
|
+
@y = -1
|
10
11
|
end
|
11
12
|
|
12
13
|
def fetch
|
13
14
|
loop do
|
14
15
|
@line = @lines.shift
|
15
|
-
|
16
|
-
@y += 1
|
17
|
-
else
|
18
|
-
@y = 0
|
19
|
-
end
|
16
|
+
@y += 1
|
20
17
|
break if @line.nil? || @line.strip.length.positive?
|
21
18
|
end
|
22
19
|
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
0
|
27
|
-
end
|
20
|
+
# skip white spaces at the beginning of a line
|
21
|
+
@x = @line&.match(/[[:graph:]]/)&.pre_match&.length || 0
|
28
22
|
end
|
29
23
|
|
30
24
|
def run
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Teletype
|
6
|
+
# Pager divides lines of text into proper screen size and provides page suggestion
|
7
|
+
# based on statistics of click accuracy.
|
8
|
+
class Paginator
|
9
|
+
def initialize(profile, text, suggest:, screen:, stats:)
|
10
|
+
@screen = screen
|
11
|
+
@stats = stats
|
12
|
+
@suggest = suggest
|
13
|
+
|
14
|
+
@lines = split(text, @screen.width)
|
15
|
+
|
16
|
+
@linenum = 0
|
17
|
+
@pagefile = File.join(profile, "page-#{Digest::MD5.hexdigest(text)}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
File.write(@pagefile, @linenum) if @linenum.positive?
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
prev = File.exist?(@pagefile) ? Integer(File.read(@pagefile).strip) : 0
|
26
|
+
@lines.each_slice(@screen.height) do |lines|
|
27
|
+
@linenum += @screen.height
|
28
|
+
next if @linenum < prev
|
29
|
+
|
30
|
+
Page.new(lines, @screen, @stats).run
|
31
|
+
|
32
|
+
loop do
|
33
|
+
suggestions = @stats.suggestions
|
34
|
+
break if suggestions.empty?
|
35
|
+
|
36
|
+
Page.new(pick(suggestions), @screen, @stats).run
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def pick(suggestions)
|
42
|
+
index = 0.0 # preserve the original order
|
43
|
+
matches = ->(line) { suggestions.map { |keys| line.scan(keys).count }.sum - (index += 0.0001) }
|
44
|
+
@lines.sort_by { |line| matches.call(line) }.last(@suggest)
|
45
|
+
end
|
46
|
+
|
47
|
+
def split(text, length)
|
48
|
+
lines = []
|
49
|
+
text.each_line do |line|
|
50
|
+
line.chars.each_slice(length) do |slice|
|
51
|
+
lines << slice.join
|
52
|
+
end
|
53
|
+
end
|
54
|
+
lines
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/teletype/practice.rb
CHANGED
@@ -3,26 +3,25 @@
|
|
3
3
|
module Teletype
|
4
4
|
# Initializes screen size and click stats, then start the practice page by page.
|
5
5
|
class Practice
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
@
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
@pager = Pager.new(@lines, @stats, @screen.height)
|
6
|
+
def initialize(profile, text:, height:, width:, suggest:, verbose:)
|
7
|
+
@stats = Stats.new(profile, text)
|
8
|
+
@paginator = Paginator.new(
|
9
|
+
profile, text,
|
10
|
+
height: height,
|
11
|
+
width: width,
|
12
|
+
suggest: suggest,
|
13
|
+
verbose: verbose,
|
14
|
+
stats: @stats
|
15
|
+
)
|
18
16
|
end
|
19
17
|
|
20
18
|
def start
|
21
|
-
at_exit
|
22
|
-
|
23
|
-
|
24
|
-
Page.new(lines, @screen, @stats).run
|
19
|
+
at_exit do
|
20
|
+
@stats.save
|
21
|
+
@paginator.save
|
25
22
|
end
|
23
|
+
|
24
|
+
@paginator.run
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
data/lib/teletype/screen.rb
CHANGED
@@ -5,7 +5,8 @@ module Teletype
|
|
5
5
|
class Screen
|
6
6
|
attr_accessor :height, :width, :top, :left
|
7
7
|
|
8
|
-
def initialize(height
|
8
|
+
def initialize(height, width, verbose: false)
|
9
|
+
@verbose = verbose
|
9
10
|
@maxh, @maxw = $stdout.winsize
|
10
11
|
|
11
12
|
if @maxh > height
|
@@ -43,6 +44,8 @@ module Teletype
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def log(*lines)
|
47
|
+
return unless @verbose
|
48
|
+
|
46
49
|
original = $stdin.cursor
|
47
50
|
invisible do
|
48
51
|
lines.each_with_index do |line, index|
|
data/lib/teletype/stats.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Teletype
|
4
|
-
# Stats keep track of hit/miss rate for
|
4
|
+
# Stats keep track of hit/miss rate for pair of keys.
|
5
|
+
# It also has suggestions for keys that need more practice.
|
5
6
|
class Stats
|
6
|
-
def initialize(text)
|
7
|
+
def initialize(profile, text)
|
8
|
+
@file = File.join(profile, 'stats')
|
7
9
|
@previous = nil
|
8
10
|
@pairs = {}
|
9
11
|
load(text)
|
10
12
|
end
|
11
13
|
|
12
14
|
def load(text)
|
13
|
-
return unless File.exist?(file)
|
15
|
+
return unless File.exist?(@file)
|
14
16
|
|
15
|
-
File.readlines(file).each do |line|
|
17
|
+
File.readlines(@file).each do |line|
|
16
18
|
keys, hit, miss = line.split("\t")
|
17
19
|
@pairs[keys] = Pair.new(keys,
|
18
20
|
hit: Integer(hit),
|
@@ -22,11 +24,7 @@ module Teletype
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def save
|
25
|
-
File.write(file, @pairs.map { |k, p| [k, p.hit, p.miss].join("\t") }.join("\n"))
|
26
|
-
end
|
27
|
-
|
28
|
-
def file
|
29
|
-
File.join(Dir.home, '.teletype-stats')
|
27
|
+
File.write(@file, @pairs.map { |k, p| [k, p.hit, p.miss].join("\t") }.join("\n"))
|
30
28
|
end
|
31
29
|
|
32
30
|
def hit!(key)
|
@@ -46,7 +44,7 @@ module Teletype
|
|
46
44
|
@pairs[keys] ||= Pair.new(keys)
|
47
45
|
end
|
48
46
|
|
49
|
-
def suggestions
|
47
|
+
def suggestions
|
50
48
|
@pairs.values.select(&:available).select(&:inefficient?).sort.map(&:keys)
|
51
49
|
end
|
52
50
|
|
data/lib/teletype.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teletype
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ochirkhuyag.L
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.16'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rake
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -45,7 +59,7 @@ files:
|
|
45
59
|
- lib/teletype.rb
|
46
60
|
- lib/teletype/key.rb
|
47
61
|
- lib/teletype/page.rb
|
48
|
-
- lib/teletype/
|
62
|
+
- lib/teletype/paginator.rb
|
49
63
|
- lib/teletype/practice.rb
|
50
64
|
- lib/teletype/screen.rb
|
51
65
|
- lib/teletype/stats.rb
|
@@ -71,5 +85,5 @@ requirements: []
|
|
71
85
|
rubygems_version: 3.1.6
|
72
86
|
signing_key:
|
73
87
|
specification_version: 4
|
74
|
-
summary: Typing practice on
|
88
|
+
summary: Typing practice on terminal.
|
75
89
|
test_files: []
|
data/lib/teletype/pager.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Teletype
|
4
|
-
# Pager divides lines of text into proper screen size and provides page suggestion
|
5
|
-
# based on statistics of click accuracy.
|
6
|
-
class Pager
|
7
|
-
SUGGEST = 3
|
8
|
-
|
9
|
-
def initialize(lines, stats, height)
|
10
|
-
@lines = lines
|
11
|
-
@stats = stats
|
12
|
-
@height = height
|
13
|
-
end
|
14
|
-
|
15
|
-
def each
|
16
|
-
@lines.each_slice(@height).map do |lines|
|
17
|
-
yield lines
|
18
|
-
|
19
|
-
loop do
|
20
|
-
suggestions = @stats.suggestions(@lines)
|
21
|
-
break if suggestions.empty?
|
22
|
-
|
23
|
-
yield pick(suggestions)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def pick(suggestions)
|
29
|
-
index = 0.0
|
30
|
-
@lines.sort_by { |line| suggestions.map { |keys| -line.scan(keys).count }.sum + (index += 0.0001) }.first(SUGGEST)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|