twterm 1.3.0 → 2.0.0.beta1
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 +14 -18
- data/bin/twterm +1 -1
- data/lib/twterm/app.rb +120 -30
- data/lib/twterm/client.rb +10 -13
- data/lib/twterm/completion_mamanger.rb +11 -6
- data/lib/twterm/direct_message.rb +6 -28
- data/lib/twterm/direct_message_composer.rb +10 -5
- data/lib/twterm/direct_message_manager.rb +5 -6
- data/lib/twterm/event/notification/abstract_notification.rb +27 -0
- data/lib/twterm/event/notification/error.rb +13 -0
- data/lib/twterm/event/notification/info.rb +13 -0
- data/lib/twterm/event/notification/success.rb +13 -0
- data/lib/twterm/event/notification/warning.rb +13 -0
- data/lib/twterm/event_dispatcher.rb +1 -1
- data/lib/twterm/extensions/array.rb +5 -0
- data/lib/twterm/extensions/enumerator/lazy.rb +3 -0
- data/lib/twterm/extensions/string.rb +0 -4
- data/lib/twterm/friendship.rb +1 -85
- data/lib/twterm/image/between.rb +31 -0
- data/lib/twterm/image/blank_line.rb +21 -0
- data/lib/twterm/image/bold.rb +31 -0
- data/lib/twterm/image/brackets.rb +21 -0
- data/lib/twterm/image/color.rb +45 -0
- data/lib/twterm/image/empty.rb +21 -0
- data/lib/twterm/image/horizontal_sequential_image.rb +48 -0
- data/lib/twterm/image/parens.rb +21 -0
- data/lib/twterm/image/string_image.rb +38 -0
- data/lib/twterm/image/vertical_sequential_image.rb +43 -0
- data/lib/twterm/image.rb +107 -0
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +51 -0
- data/lib/twterm/key_mapper/app_key_mapper.rb +13 -0
- data/lib/twterm/key_mapper/cursor_key_mapper.rb +13 -0
- data/lib/twterm/key_mapper/general_key_mapper.rb +18 -0
- data/lib/twterm/key_mapper/no_such_command.rb +20 -0
- data/lib/twterm/key_mapper/no_such_key.rb +16 -0
- data/lib/twterm/key_mapper/status_key_mapper.rb +18 -0
- data/lib/twterm/key_mapper/tab_key_mapper.rb +31 -0
- data/lib/twterm/key_mapper.rb +127 -0
- data/lib/twterm/list.rb +0 -31
- data/lib/twterm/notifier.rb +7 -7
- data/lib/twterm/repository/abstract_entity_repository.rb +41 -0
- data/lib/twterm/repository/abstract_expirable_entity_repository.rb +35 -0
- data/lib/twterm/repository/abstract_repository.rb +64 -0
- data/lib/twterm/repository/direct_message_repository.rb +14 -0
- data/lib/twterm/repository/friendship_repository.rb +108 -0
- data/lib/twterm/repository/hashtag_repository.rb +39 -0
- data/lib/twterm/repository/list_repository.rb +14 -0
- data/lib/twterm/repository/status_repository.rb +36 -0
- data/lib/twterm/repository/user_repository.rb +22 -0
- data/lib/twterm/rest_client.rb +107 -63
- data/lib/twterm/screen.rb +21 -15
- data/lib/twterm/search_query_window.rb +139 -0
- data/lib/twterm/status.rb +14 -108
- data/lib/twterm/streaming_client.rb +13 -12
- data/lib/twterm/tab/base.rb +48 -8
- data/lib/twterm/tab/direct_message/conversation.rb +53 -52
- data/lib/twterm/tab/direct_message/conversation_list.rb +46 -45
- data/lib/twterm/tab/dumpable.rb +3 -3
- data/lib/twterm/tab/key_assignments_cheatsheet.rb +58 -57
- data/lib/twterm/tab/loadable.rb +20 -0
- data/lib/twterm/tab/new/list.rb +32 -43
- data/lib/twterm/tab/new/search.rb +31 -44
- data/lib/twterm/tab/new/start.rb +44 -55
- data/lib/twterm/tab/new/user.rb +15 -12
- data/lib/twterm/tab/rate_limit_status.rb +84 -0
- data/lib/twterm/tab/scrollable.rb +39 -19
- data/lib/twterm/tab/searchable.rb +133 -0
- data/lib/twterm/tab/statuses/base.rb +139 -129
- data/lib/twterm/tab/statuses/conversation.rb +26 -15
- data/lib/twterm/tab/statuses/favorites.rb +10 -8
- data/lib/twterm/tab/statuses/home.rb +10 -9
- data/lib/twterm/tab/statuses/list_timeline.rb +12 -8
- data/lib/twterm/tab/statuses/mentions.rb +17 -11
- data/lib/twterm/tab/statuses/search.rb +8 -5
- data/lib/twterm/tab/statuses/user_timeline.rb +11 -8
- data/lib/twterm/tab/user_list_management.rb +109 -0
- data/lib/twterm/tab/user_tab.rb +125 -126
- data/lib/twterm/tab/users/base.rb +39 -41
- data/lib/twterm/tab/users/followers.rb +9 -6
- data/lib/twterm/tab/users/friends.rb +9 -6
- data/lib/twterm/tab_manager.rb +64 -40
- data/lib/twterm/tweetbox.rb +18 -13
- data/lib/twterm/uri_opener.rb +2 -1
- data/lib/twterm/user.rb +2 -110
- data/lib/twterm/version.rb +1 -1
- data/lib/twterm/view.rb +30 -0
- data/lib/twterm.rb +3 -9
- data/spec/fixtures/status.json +107 -0
- data/spec/fixtures/user.json +102 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/supports/shared_examples/abstract_key_mapper.rb +17 -0
- data/spec/twterm/extension/enumerator/lazy_spec.rb +11 -0
- data/spec/twterm/friendship_spec.rb +0 -102
- data/spec/twterm/image/blank_line_spec.rb +11 -0
- data/spec/twterm/image/brackets_spec.rb +12 -0
- data/spec/twterm/image/color_spec.rb +22 -0
- data/spec/twterm/image/empry_spec.rb +11 -0
- data/spec/twterm/image/horizontal_sequential_image_spec.rb +15 -0
- data/spec/twterm/image/parens_spec.rb +12 -0
- data/spec/twterm/image/string_image_spec.rb +12 -0
- data/spec/twterm/image/vertical_sequential_image_spec.rb +14 -0
- data/spec/twterm/image_spec.rb +65 -0
- data/spec/twterm/key_mapper/abstract_key_mapper_spec.rb +21 -0
- data/spec/twterm/key_mapper/app_key_mapper_spec.rb +7 -0
- data/spec/twterm/key_mapper/status_key_mapper_spec.rb +7 -0
- data/spec/twterm/key_mapper/tab_key_mapper_spec.rb +7 -0
- data/spec/twterm/repository/friendship_repository_spec.rb +108 -0
- data/spec/twterm/status_spec.rb +58 -0
- data/spec/twterm/user_spec.rb +94 -0
- data/twterm.gemspec +13 -10
- metadata +129 -35
- data/lib/twterm/event/notification.rb +0 -33
- data/lib/twterm/extensions/integer.rb +0 -5
- data/lib/twterm/filter_query_window.rb +0 -91
- data/lib/twterm/filterable_list.rb +0 -41
- data/lib/twterm/history/base.rb +0 -21
- data/lib/twterm/history/hashtag.rb +0 -13
- data/lib/twterm/history/savable.rb +0 -37
- data/lib/twterm/history/screen_name.rb +0 -11
- data/lib/twterm/promise.rb +0 -143
data/lib/twterm/friendship.rb
CHANGED
@@ -9,15 +9,10 @@ module Twterm
|
|
9
9
|
|
10
10
|
attr_reader :status, :from, :to
|
11
11
|
|
12
|
-
@@instances = []
|
13
|
-
@@user_ids = Set.new
|
14
|
-
|
15
12
|
def initialize(status, from, to)
|
16
|
-
fail ArgumentError,
|
17
|
-
'' unless STATUSES.include? status
|
13
|
+
fail ArgumentError, '`status` must be one of the following: :blocking, :follwing, :following_requested, or :muting' unless STATUSES.include? status
|
18
14
|
|
19
15
|
@status, @from, @to = status, from, to
|
20
|
-
@@instances << self
|
21
16
|
end
|
22
17
|
|
23
18
|
def blocking?
|
@@ -36,85 +31,6 @@ module Twterm
|
|
36
31
|
status?(:muting)
|
37
32
|
end
|
38
33
|
|
39
|
-
def self.already_looked_up?(user_id)
|
40
|
-
@@user_ids.include?(user_id)
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.block(from, to)
|
44
|
-
new(:blocking, from, to)
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.blocking?(from, to)
|
48
|
-
!find(:blocking, from, to).nil?
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.cancel_follow_request(from, to)
|
52
|
-
new(:following_requested, from, to)
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.delete(status, from, to)
|
56
|
-
cond = -> f { f.status == status && f.from == from && f.to == to }
|
57
|
-
@@instances.delete_if(&cond)
|
58
|
-
end
|
59
|
-
private_class_method :delete
|
60
|
-
|
61
|
-
def self.find(status, from, to)
|
62
|
-
cond = -> f { f.status == status && f.from == from && f.to == to }
|
63
|
-
@@instances.find(&cond)
|
64
|
-
end
|
65
|
-
private_class_method :find
|
66
|
-
|
67
|
-
def self.follow(from, to)
|
68
|
-
new(:following, from, to)
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.following?(from, to)
|
72
|
-
!find(:following, from, to).nil?
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.following_not_requested(from, to)
|
76
|
-
delete(:following_requested, from, to)
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.following_requested(from, to)
|
80
|
-
new(:following_requested, from, to)
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.following_requested?(from, to)
|
84
|
-
!find(:following_requested, from, to).nil?
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.looked_up!(user_id)
|
88
|
-
@@user_ids << user_id
|
89
|
-
user_id
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.mute(from, to)
|
93
|
-
new(:muting, from, to)
|
94
|
-
end
|
95
|
-
|
96
|
-
def self.muting?(from, to)
|
97
|
-
!find(:muting, from, to).nil?
|
98
|
-
end
|
99
|
-
|
100
|
-
def self.new(status, from, to)
|
101
|
-
instance = find(status, from, to)
|
102
|
-
instance.nil? ? super : instance
|
103
|
-
end
|
104
|
-
private_class_method :new
|
105
|
-
|
106
|
-
def self.unblock(from, to)
|
107
|
-
delete(:blocking, from, to)
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.unfollow(from, to)
|
111
|
-
delete(:following, from, to)
|
112
|
-
end
|
113
|
-
|
114
|
-
def self.unmute(from, to)
|
115
|
-
delete(:muting, from, to)
|
116
|
-
end
|
117
|
-
|
118
34
|
private
|
119
35
|
|
120
36
|
def status?(status)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Twterm::Image
|
2
|
+
class Between < Twterm::Image
|
3
|
+
def initialize(image, open, close)
|
4
|
+
@image, @open, @close = image, open, close
|
5
|
+
end
|
6
|
+
|
7
|
+
def height
|
8
|
+
1
|
9
|
+
end
|
10
|
+
|
11
|
+
def render(window)
|
12
|
+
open.at(line, column).render(window)
|
13
|
+
image.at(line, column + open.width).render(window)
|
14
|
+
close.at(line, column + open.width + image.width).render(window)
|
15
|
+
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"#{open}#{image}#{close}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def width
|
24
|
+
[open, image, close].map(&:width).reduce(0, :+)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :open, :close, :image
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Twterm
|
2
|
+
class Image
|
3
|
+
class Bold < Twterm::Image
|
4
|
+
def initialize(image)
|
5
|
+
@image = image
|
6
|
+
end
|
7
|
+
|
8
|
+
def height
|
9
|
+
image.height
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(window)
|
13
|
+
window.attron(Curses::A_BOLD)
|
14
|
+
image.at(line, column).render(window)
|
15
|
+
window.attroff(Curses::A_BOLD)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"\e[1m#{image}\e[0m"
|
20
|
+
end
|
21
|
+
|
22
|
+
def width
|
23
|
+
image.width
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :image
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'twterm/image/between'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
class Image
|
5
|
+
class Brackets < Twterm::Image::Between
|
6
|
+
def initialize(image)
|
7
|
+
super(image, open, close)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def open
|
13
|
+
StringImage.new('[')
|
14
|
+
end
|
15
|
+
|
16
|
+
def close
|
17
|
+
StringImage.new(']')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Twterm
|
2
|
+
class Image
|
3
|
+
class Color < Twterm::Image
|
4
|
+
def initialize(image, fg, bg = :transparent)
|
5
|
+
@image, @fg, @bg = image, fg, bg
|
6
|
+
end
|
7
|
+
|
8
|
+
def height
|
9
|
+
image.height
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(window)
|
13
|
+
window.attron(Curses.color_pair(color_pair_index))
|
14
|
+
image.at(line, column).render(window)
|
15
|
+
window.attroff(Curses.color_pair(color_pair_index))
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
fg_colors = {
|
20
|
+
black: 30, red: 31, green: 32, yellow: 33,
|
21
|
+
blue: 34, magenta: 35, cyan: 36, white: 37
|
22
|
+
}
|
23
|
+
bg_colors = {
|
24
|
+
black: 40, red: 41, green: 42, yellow: 43,
|
25
|
+
blur: 44, magenta: 45, cyan: 46, white: 47
|
26
|
+
}
|
27
|
+
|
28
|
+
str = "\e[#{fg_colors[@fg]}m#{image}\e[0m"
|
29
|
+
@bg == :transparent ? str : "\e[#{bg_colors[@bg]}m#{str}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def width
|
33
|
+
image.width
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :image
|
39
|
+
|
40
|
+
def color_pair_index
|
41
|
+
Twterm::ColorManager.instance.get_color_pair_index(@fg, @bg)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Twterm
|
2
|
+
class Image
|
3
|
+
class HorizontalSequentialImage < Twterm::Image
|
4
|
+
def initialize(images)
|
5
|
+
@images = images
|
6
|
+
end
|
7
|
+
|
8
|
+
def -(other)
|
9
|
+
append(other)
|
10
|
+
end
|
11
|
+
|
12
|
+
def append(image)
|
13
|
+
if image.is_a?(self.class)
|
14
|
+
@images += image.images
|
15
|
+
else
|
16
|
+
images << image
|
17
|
+
end
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def height
|
23
|
+
images.map(&:height).max
|
24
|
+
end
|
25
|
+
|
26
|
+
def render(window)
|
27
|
+
window.setpos(line, column)
|
28
|
+
images
|
29
|
+
.zip(images.lazy.map(&:width).scan(column, :+))
|
30
|
+
.each { |r, c| r.at(line, c).render(window) }
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
images.map(&:to_s).join
|
37
|
+
end
|
38
|
+
|
39
|
+
def width
|
40
|
+
images.map(&:width).reduce(0, :+)
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
attr_reader :images
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'twterm/image/between'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
class Image
|
5
|
+
class Parens < Twterm::Image::Between
|
6
|
+
def initialize(image)
|
7
|
+
super(image, open, close)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def open
|
13
|
+
StringImage.new('(')
|
14
|
+
end
|
15
|
+
|
16
|
+
def close
|
17
|
+
StringImage.new(')')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Twterm
|
2
|
+
class Image
|
3
|
+
class StringImage < Twterm::Image
|
4
|
+
def initialize(string)
|
5
|
+
@string = string
|
6
|
+
end
|
7
|
+
|
8
|
+
def -(other)
|
9
|
+
if other.is_a?(self.class)
|
10
|
+
self.class.new(string + other.string)
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def height
|
17
|
+
1
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(window)
|
21
|
+
window.setpos(line, column)
|
22
|
+
window.addstr(string)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
@string.dup
|
27
|
+
end
|
28
|
+
|
29
|
+
def width
|
30
|
+
string.width
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
attr_reader :string
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Twterm
|
2
|
+
class Image
|
3
|
+
class VerticalSequentialImage < Twterm::Image
|
4
|
+
def initialize(images)
|
5
|
+
@images = images
|
6
|
+
end
|
7
|
+
|
8
|
+
def |(other)
|
9
|
+
append(other)
|
10
|
+
end
|
11
|
+
|
12
|
+
def append(image)
|
13
|
+
images << image
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def height
|
18
|
+
images.map(&:height).reduce(0, :+)
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(window)
|
22
|
+
window.setpos(line, column)
|
23
|
+
images
|
24
|
+
.zip(images.lazy.map(&:height).scan(line, :+))
|
25
|
+
.each { |r, l| r.at(l, column).render(window) }
|
26
|
+
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
images.map(&:to_s).join("\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def width
|
35
|
+
images.map(&:width).max
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
attr_reader :images
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/twterm/image.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'twterm/image/between'
|
2
|
+
require 'twterm/image/blank_line'
|
3
|
+
require 'twterm/image/bold'
|
4
|
+
require 'twterm/image/brackets'
|
5
|
+
require 'twterm/image/color'
|
6
|
+
require 'twterm/image/empty'
|
7
|
+
require 'twterm/image/horizontal_sequential_image'
|
8
|
+
require 'twterm/image/parens'
|
9
|
+
require 'twterm/image/string_image'
|
10
|
+
require 'twterm/image/vertical_sequential_image'
|
11
|
+
|
12
|
+
class Twterm::Image
|
13
|
+
def initialize(column: 0, line: 0)
|
14
|
+
@column, @line = column, line
|
15
|
+
end
|
16
|
+
|
17
|
+
def !
|
18
|
+
bold
|
19
|
+
end
|
20
|
+
|
21
|
+
def |(other)
|
22
|
+
VerticalSequentialImage.new([self, other])
|
23
|
+
end
|
24
|
+
|
25
|
+
def -(other)
|
26
|
+
HorizontalSequentialImage.new([self, other])
|
27
|
+
end
|
28
|
+
|
29
|
+
def at(line, column)
|
30
|
+
@line, @column = line, column
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.blank_line
|
36
|
+
BlankLine.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def bold
|
40
|
+
Bold.new(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
def brackets
|
44
|
+
Brackets.new(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.checkbox(checked)
|
48
|
+
string(checked ? 'x' : ' ').brackets
|
49
|
+
end
|
50
|
+
|
51
|
+
def color(fg, bg = :transparent)
|
52
|
+
Color.new(self, fg, bg)
|
53
|
+
end
|
54
|
+
|
55
|
+
def column
|
56
|
+
@column || 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.cursor(height, current)
|
60
|
+
color = current ? [:black, :magenta] : [:black]
|
61
|
+
VerticalSequentialImage.new([whitespace] * height).color(*color)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.empty
|
65
|
+
Empty.new
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.remaining_resource(remaining, total, length)
|
69
|
+
ratio = remaining * 100 / total
|
70
|
+
color =
|
71
|
+
if ratio >= 40
|
72
|
+
:green
|
73
|
+
elsif ratio >= 20
|
74
|
+
:yellow
|
75
|
+
else
|
76
|
+
:red
|
77
|
+
end
|
78
|
+
|
79
|
+
bars = string(('|' * (remaining * length / total)).ljust(length)).color(color)
|
80
|
+
|
81
|
+
Between.new(bars, !string('['), !string(']'))
|
82
|
+
end
|
83
|
+
|
84
|
+
def line
|
85
|
+
@line || 0
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.number(n)
|
89
|
+
string(n.to_s.gsub(/(\d)(?=(\d{3})+(?!\d))/, '\1,'))
|
90
|
+
end
|
91
|
+
|
92
|
+
def parens
|
93
|
+
Parens.new(self)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.plural(n, singular, plural = "#{singular}s")
|
97
|
+
string(n == 1 ? singular : plural)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.string(str)
|
101
|
+
StringImage.new(str)
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.whitespace
|
105
|
+
string(' ')
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'curses'
|
2
|
+
require 'twterm/key_mapper/no_such_command'
|
3
|
+
require 'twterm/key_mapper/no_such_key'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
class KeyMapper
|
7
|
+
class AbstractKeyMapper
|
8
|
+
def initialize(mappings)
|
9
|
+
commands = self.class.commands
|
10
|
+
|
11
|
+
mappings.keys.each do |k|
|
12
|
+
raise NoSuchCommand.new(self.class.category, k) unless commands.include?(k)
|
13
|
+
end
|
14
|
+
|
15
|
+
@mappings = Hash[mappings.map { |k, v| [k, translate(v)] }]
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](key)
|
19
|
+
raise NoSuchCommand.new(self.class.category, key) unless @mappings.keys.include?(key)
|
20
|
+
@mappings[key]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def translate(key)
|
26
|
+
case key
|
27
|
+
when '!'..'}' then key
|
28
|
+
when /\A\^([A-Z]?)\Z/ then $1.ord - 'A'.ord + 1
|
29
|
+
when 'F1' then Curses::Key::F1
|
30
|
+
when 'F2' then Curses::Key::F2
|
31
|
+
when 'F3' then Curses::Key::F3
|
32
|
+
when 'F4' then Curses::Key::F4
|
33
|
+
when 'F5' then Curses::Key::F5
|
34
|
+
when 'F6' then Curses::Key::F6
|
35
|
+
when 'F7' then Curses::Key::F7
|
36
|
+
when 'F8' then Curses::Key::F8
|
37
|
+
when 'F9' then Curses::Key::F9
|
38
|
+
when 'F10' then Curses::Key::F10
|
39
|
+
when 'F11' then Curses::Key::F11
|
40
|
+
when 'F12' then Curses::Key::F12
|
41
|
+
else
|
42
|
+
raise NoSuchKey.new(key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.commands
|
47
|
+
self::DEFAULT_MAPPINGS.keys
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'twterm/key_mapper/abstract_key_mapper'
|
2
|
+
|
3
|
+
class Twterm::KeyMapper::AppKeyMapper < Twterm::KeyMapper::AbstractKeyMapper
|
4
|
+
DEFAULT_MAPPINGS = {
|
5
|
+
cheatsheet: 'F1',
|
6
|
+
me: 'm',
|
7
|
+
quit: 'F10',
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
def self.category
|
11
|
+
'app'.freeze
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'twterm/key_mapper/abstract_key_mapper'
|
2
|
+
|
3
|
+
class Twterm::KeyMapper::CursorKeyMapper < Twterm::KeyMapper::AbstractKeyMapper
|
4
|
+
DEFAULT_MAPPINGS = {
|
5
|
+
bottom_of_window: 'L',
|
6
|
+
middle_of_window: 'M',
|
7
|
+
top_of_window: 'H',
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
def self.category
|
11
|
+
'cursor'.freeze
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'twterm/key_mapper/abstract_key_mapper'
|
2
|
+
|
3
|
+
class Twterm::KeyMapper::GeneralKeyMapper < Twterm::KeyMapper::AbstractKeyMapper
|
4
|
+
DEFAULT_MAPPINGS = {
|
5
|
+
bottom: 'G',
|
6
|
+
down: 'j',
|
7
|
+
left: 'h',
|
8
|
+
page_down: 'd',
|
9
|
+
page_up: 'u',
|
10
|
+
right: 'l',
|
11
|
+
top: 'g',
|
12
|
+
up: 'k',
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.category
|
16
|
+
'general'.freeze
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Twterm
|
2
|
+
class KeyMapper
|
3
|
+
class NoSuchCommand < StandardError
|
4
|
+
attr_reader :category, :command
|
5
|
+
|
6
|
+
def initialize(category, command)
|
7
|
+
@category, @command = category, command
|
8
|
+
super(message)
|
9
|
+
end
|
10
|
+
|
11
|
+
def full_command
|
12
|
+
"#{category}.#{command}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
"No such command: #{full_command}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'twterm/key_mapper/abstract_key_mapper'
|
2
|
+
|
3
|
+
class Twterm::KeyMapper::StatusKeyMapper < Twterm::KeyMapper::AbstractKeyMapper
|
4
|
+
DEFAULT_MAPPINGS = {
|
5
|
+
compose: '^N',
|
6
|
+
conversation: 'c',
|
7
|
+
destroy: 'D',
|
8
|
+
like: 'L',
|
9
|
+
open_link: 'o',
|
10
|
+
reply: 'r',
|
11
|
+
retweet: 'R',
|
12
|
+
user: 'U',
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.category
|
16
|
+
'status'.freeze
|
17
|
+
end
|
18
|
+
end
|