twterm 2.0.1 → 2.1.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/lib/twterm.rb +1 -1
- data/lib/twterm/app.rb +2 -2
- data/lib/twterm/completer/abstract_completer.rb +25 -0
- data/lib/twterm/completer/default_completer.rb +21 -0
- data/lib/twterm/completer/screen_name_completer.rb +17 -0
- data/lib/twterm/completer/search_query_completer.rb +180 -0
- data/lib/twterm/completion_manager.rb +37 -0
- data/lib/twterm/key_mapper.rb +14 -2
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +10 -3
- data/lib/twterm/key_mapper/status_key_mapper.rb +1 -0
- data/lib/twterm/repository/abstract_entity_repository.rb +4 -0
- data/lib/twterm/repository/status_repository.rb +7 -5
- data/lib/twterm/repository/user_repository.rb +0 -4
- data/lib/twterm/rest_client.rb +7 -9
- data/lib/twterm/scheduler.rb +1 -0
- data/lib/twterm/status.rb +16 -1
- data/lib/twterm/tab/new/search.rb +2 -2
- data/lib/twterm/tab/new/user.rb +1 -1
- data/lib/twterm/tab/statuses/base.rb +9 -1
- data/lib/twterm/tab/statuses/cacheable.rb +18 -0
- data/lib/twterm/tab/statuses/conversation.rb +66 -1
- data/lib/twterm/tab/statuses/list_timeline.rb +2 -2
- data/lib/twterm/tab/statuses/user_timeline.rb +11 -2
- data/lib/twterm/tweetbox.rb +46 -57
- data/lib/twterm/version.rb +1 -1
- data/spec/fixtures/list.json +69 -0
- data/spec/twterm/completer/search_query_completer_spec.rb +231 -0
- data/twterm.gemspec +1 -1
- metadata +15 -8
- data/lib/twterm/completion_mamanger.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a822edd0e5719ca957ab183b1859bd6ec01113ca
|
4
|
+
data.tar.gz: ecec2ab95f599a575226d0a3239f1b93ef63a4f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1608685c830f8f614bc6549b498c271b7851deb05ee2e7054b62857ff89dae64f0d17f9f3d1fc22338493644aa35c9d4beb78c7ee2f6054eecd939108255d216
|
7
|
+
data.tar.gz: f188471c3974d031c257b2a6fd9169926d1350b663d4b71ce4ee180d20e6d63a2a61657c1755e019c01ca38f1402493d386b53cb4e4e90b752051969e184ecf0
|
data/lib/twterm.rb
CHANGED
@@ -17,7 +17,7 @@ require 'twterm/app'
|
|
17
17
|
require 'twterm/auth'
|
18
18
|
require 'twterm/client'
|
19
19
|
require 'twterm/color_manager'
|
20
|
-
require 'twterm/
|
20
|
+
require 'twterm/completion_manager'
|
21
21
|
require 'twterm/config'
|
22
22
|
require 'twterm/extensions/array'
|
23
23
|
require 'twterm/extensions/curses/window'
|
data/lib/twterm/app.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'curses'
|
2
2
|
|
3
|
-
require 'twterm/
|
3
|
+
require 'twterm/completion_manager'
|
4
4
|
require 'twterm/direct_message_composer'
|
5
5
|
require 'twterm/event/screen/resize'
|
6
6
|
require 'twterm/repository/direct_message_repository'
|
@@ -22,7 +22,7 @@ module Twterm
|
|
22
22
|
DATA_DIR = "#{ENV['HOME']}/.twterm".freeze
|
23
23
|
|
24
24
|
def completion_manager
|
25
|
-
@
|
25
|
+
@completion_manager ||= CompletionManager.new(self)
|
26
26
|
end
|
27
27
|
|
28
28
|
def direct_message_composer
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Twterm
|
2
|
+
module Completer
|
3
|
+
class AbstractCompleter
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def basic_word_break_characters
|
9
|
+
" \t\n\"\\'`$><=;|&{("
|
10
|
+
end
|
11
|
+
|
12
|
+
def complete(_query)
|
13
|
+
raise NotImplementedError, '`complete` method must be implemented'
|
14
|
+
end
|
15
|
+
|
16
|
+
def completion_append_character
|
17
|
+
' '
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :app
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'twterm/completer/abstract_completer'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Completer
|
5
|
+
class DefaultCompleter < AbstractCompleter
|
6
|
+
def complete(query)
|
7
|
+
if query.start_with?('#')
|
8
|
+
app.hashtag_repository.all
|
9
|
+
.map { |tag| "##{tag}" }
|
10
|
+
.select { |tag| tag.start_with?(query) }
|
11
|
+
elsif query.start_with?('@')
|
12
|
+
app.user_repository.all
|
13
|
+
.map { |user| "@#{user.screen_name}" }
|
14
|
+
.select { |name| name.start_with?(query) }
|
15
|
+
else
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'twterm/completer/abstract_completer'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Completer
|
5
|
+
class ScreenNameCompleter < AbstractCompleter
|
6
|
+
def complete(query)
|
7
|
+
app.user_repository.all
|
8
|
+
.map { |user| user.screen_name }
|
9
|
+
.select { |name| name.start_with?(query) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def completion_append_character
|
13
|
+
''
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'twterm/completer/abstract_completer'
|
2
|
+
|
3
|
+
module Twterm
|
4
|
+
module Completer
|
5
|
+
class SearchQueryCompleter < AbstractCompleter
|
6
|
+
def completion_append_character
|
7
|
+
''
|
8
|
+
end
|
9
|
+
|
10
|
+
def complete(q)
|
11
|
+
possible_operators = possible_operators_for_query(q)
|
12
|
+
|
13
|
+
if q.empty?
|
14
|
+
operators
|
15
|
+
elsif q.start_with?('#')
|
16
|
+
app.hashtag_repository.all
|
17
|
+
.map { |tag| "##{tag} " }
|
18
|
+
.select { |tag| tag.start_with?(q) }
|
19
|
+
elsif q.start_with?('@')
|
20
|
+
app.user_repository.all
|
21
|
+
.map { |user| "@#{user.screen_name} " }
|
22
|
+
.select { |name| name.start_with?(q) }
|
23
|
+
elsif !possible_operators.empty?
|
24
|
+
possible_operators
|
25
|
+
elsif q.start_with?('-from:')
|
26
|
+
app.user_repository.all
|
27
|
+
.map { |user| "-from:#{user.screen_name} " }
|
28
|
+
.select { |name| name.start_with?(q) }
|
29
|
+
elsif q.start_with?('from:')
|
30
|
+
app.user_repository.all
|
31
|
+
.map { |user| "from:#{user.screen_name} " }
|
32
|
+
.select { |name| name.start_with?(q) }
|
33
|
+
elsif q.start_with?('-to:')
|
34
|
+
app.user_repository.all
|
35
|
+
.map { |user| "-to:#{user.screen_name} " }
|
36
|
+
.select { |name| name.start_with?(q) }
|
37
|
+
elsif q.start_with?('to:')
|
38
|
+
app.user_repository.all
|
39
|
+
.map { |user| "to:#{user.screen_name} " }
|
40
|
+
.select { |name| name.start_with?(q) }
|
41
|
+
elsif q.start_with?('filter:')
|
42
|
+
filters.map { |f| "filter:#{f} " }.select { |f| f.start_with?(q) }
|
43
|
+
elsif q.start_with?('-filter:')
|
44
|
+
filters.map { |f| "-filter:#{f} " }.select { |f| f.start_with?(q) }
|
45
|
+
elsif q.start_with?('lang:')
|
46
|
+
langs.map { |l| "lang:#{l} " }.select { |l| l.start_with?(q) }
|
47
|
+
elsif q.start_with?('-lang:')
|
48
|
+
langs.map { |l| "-lang:#{l} " }.select { |l| l.start_with?(q) }
|
49
|
+
elsif q.start_with?('list:')
|
50
|
+
app.list_repository.all
|
51
|
+
.map { |list| "list:#{list.full_name.sub('@', '')} " }
|
52
|
+
.select { |name| name.start_with?(q) }
|
53
|
+
else
|
54
|
+
[]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def filters
|
61
|
+
[
|
62
|
+
'images',
|
63
|
+
'links',
|
64
|
+
'media',
|
65
|
+
'native_video',
|
66
|
+
'periscope',
|
67
|
+
'retweets',
|
68
|
+
'safe',
|
69
|
+
'twimg',
|
70
|
+
'vine',
|
71
|
+
]
|
72
|
+
end
|
73
|
+
|
74
|
+
def langs
|
75
|
+
[
|
76
|
+
'am',
|
77
|
+
'ar',
|
78
|
+
'bg',
|
79
|
+
'bn',
|
80
|
+
'bo',
|
81
|
+
'ckb',
|
82
|
+
'da',
|
83
|
+
'de',
|
84
|
+
'dv',
|
85
|
+
'el',
|
86
|
+
'en',
|
87
|
+
'es',
|
88
|
+
'et',
|
89
|
+
'fa',
|
90
|
+
'fi',
|
91
|
+
'fr',
|
92
|
+
'gu',
|
93
|
+
'he',
|
94
|
+
'hi',
|
95
|
+
'ht',
|
96
|
+
'hu',
|
97
|
+
'hy',
|
98
|
+
'id',
|
99
|
+
'is',
|
100
|
+
'it',
|
101
|
+
'ja',
|
102
|
+
'ka',
|
103
|
+
'km',
|
104
|
+
'kn',
|
105
|
+
'ko',
|
106
|
+
'lo',
|
107
|
+
'lt',
|
108
|
+
'lv',
|
109
|
+
'ml',
|
110
|
+
'mr',
|
111
|
+
'my',
|
112
|
+
'ne',
|
113
|
+
'nl',
|
114
|
+
'no',
|
115
|
+
'or',
|
116
|
+
'pa',
|
117
|
+
'pl',
|
118
|
+
'ps',
|
119
|
+
'pt',
|
120
|
+
'ro',
|
121
|
+
'ru',
|
122
|
+
'sd',
|
123
|
+
'si',
|
124
|
+
'sl',
|
125
|
+
'sr',
|
126
|
+
'sv',
|
127
|
+
'ta',
|
128
|
+
'te',
|
129
|
+
'th',
|
130
|
+
'tl',
|
131
|
+
'tr',
|
132
|
+
'ug',
|
133
|
+
'ur',
|
134
|
+
'vi',
|
135
|
+
'zh',
|
136
|
+
]
|
137
|
+
end
|
138
|
+
|
139
|
+
def nullary_operators
|
140
|
+
[
|
141
|
+
':(',
|
142
|
+
':)',
|
143
|
+
'?',
|
144
|
+
'OR',
|
145
|
+
]
|
146
|
+
end
|
147
|
+
|
148
|
+
def operators
|
149
|
+
nullary_operators + unary_operators
|
150
|
+
end
|
151
|
+
|
152
|
+
def possible_operators_for_query(q)
|
153
|
+
(nullary_operators.map { |o| "#{o} " } + unary_operators).select { |x| x.start_with?(q) && x != q }
|
154
|
+
end
|
155
|
+
|
156
|
+
def substrs(str)
|
157
|
+
0.upto(str.length - 1).map { |n| str.slice(0...n) }
|
158
|
+
end
|
159
|
+
|
160
|
+
def unary_operators
|
161
|
+
[
|
162
|
+
'-filter:',
|
163
|
+
'-from:',
|
164
|
+
'-lang:',
|
165
|
+
'-to:',
|
166
|
+
'-url:',
|
167
|
+
'@',
|
168
|
+
'filter:',
|
169
|
+
'from:',
|
170
|
+
'lang:',
|
171
|
+
'list:',
|
172
|
+
'since:',
|
173
|
+
'to:',
|
174
|
+
'until:',
|
175
|
+
'url:',
|
176
|
+
]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'twterm/completer/default_completer'
|
2
|
+
require 'twterm/completer/screen_name_completer'
|
3
|
+
require 'twterm/completer/search_query_completer'
|
4
|
+
|
5
|
+
module Twterm
|
6
|
+
class CompletionManager
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
|
10
|
+
Readline.completion_case_fold = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_default_mode!
|
14
|
+
complete_with!(Completer::DefaultCompleter.new(app))
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_screen_name_mode!
|
18
|
+
complete_with!(Completer::ScreenNameCompleter.new(app))
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_search_mode!
|
22
|
+
complete_with!(Completer::SearchQueryCompleter.new(app))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :app
|
28
|
+
|
29
|
+
def complete_with!(completer)
|
30
|
+
Readline.basic_word_break_characters = completer.basic_word_break_characters
|
31
|
+
Readline.completion_append_character = completer.completion_append_character
|
32
|
+
Readline.completion_proc = -> q { completer.complete(q) }
|
33
|
+
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/twterm/key_mapper.rb
CHANGED
@@ -74,9 +74,15 @@ module Twterm
|
|
74
74
|
exit
|
75
75
|
end
|
76
76
|
|
77
|
+
def complete_missing_commands!(dict)
|
78
|
+
completed_dict = default_mappings.merge(dict.map { |k, v| [k, v.to_h] }.to_h) { |_, l, r| l.merge(r.to_h) }
|
79
|
+
|
80
|
+
assign_mappings!(completed_dict)
|
81
|
+
save!(completed_dict)
|
82
|
+
end
|
83
|
+
|
77
84
|
def create_default_dict_file!
|
78
|
-
|
79
|
-
File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
|
85
|
+
save!(default_mappings)
|
80
86
|
end
|
81
87
|
|
82
88
|
def default_mappings
|
@@ -122,6 +128,12 @@ Press any key to continue
|
|
122
128
|
getc
|
123
129
|
ensure
|
124
130
|
assign_mappings!(dict || default_mappings)
|
131
|
+
complete_missing_commands!(dict) unless dict.nil?
|
132
|
+
end
|
133
|
+
|
134
|
+
def save!(mappings)
|
135
|
+
dict = TOML.dump(mappings).gsub("\n[", "\n\n[")
|
136
|
+
File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
|
125
137
|
end
|
126
138
|
end
|
127
139
|
end
|
@@ -5,14 +5,17 @@ require 'twterm/key_mapper/no_such_key'
|
|
5
5
|
module Twterm
|
6
6
|
class KeyMapper
|
7
7
|
class AbstractKeyMapper
|
8
|
-
def initialize(
|
8
|
+
def initialize(dict)
|
9
9
|
commands = self.class.commands
|
10
10
|
|
11
|
-
|
11
|
+
dict ||= {}
|
12
|
+
|
13
|
+
dict.keys.each do |k|
|
12
14
|
raise NoSuchCommand.new(self.class.category, k) unless commands.include?(k)
|
13
15
|
end
|
14
16
|
|
15
|
-
@mappings = Hash[
|
17
|
+
@mappings = Hash[dict.map { |k, v| [k, translate(v)] }]
|
18
|
+
@dict = dict
|
16
19
|
end
|
17
20
|
|
18
21
|
def [](key)
|
@@ -20,6 +23,10 @@ module Twterm
|
|
20
23
|
@mappings[key]
|
21
24
|
end
|
22
25
|
|
26
|
+
def to_h
|
27
|
+
@dict
|
28
|
+
end
|
29
|
+
|
23
30
|
private
|
24
31
|
|
25
32
|
def translate(key)
|
@@ -5,11 +5,8 @@ require 'twterm/status'
|
|
5
5
|
module Twterm
|
6
6
|
module Repository
|
7
7
|
class StatusRepository < AbstractExpirableEntityRepository
|
8
|
-
def
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
def create(tweet, is_retweeted_status = false)
|
8
|
+
def create(tweet, is_referred_status = false)
|
9
|
+
create(tweet.quoted_status, true) unless tweet.quoted_status.is_a?(Twitter::NullObject)
|
13
10
|
create(tweet.retweeted_status, true) unless tweet.retweeted_status.is_a?(Twitter::NullObject)
|
14
11
|
super
|
15
12
|
end
|
@@ -23,10 +20,15 @@ module Twterm
|
|
23
20
|
status = super
|
24
21
|
|
25
22
|
touch(status.retweeted_status_id) if !status.nil? && status.retweet?
|
23
|
+
touch(status.quoted_status_id) if !status.nil? && status.quote?
|
26
24
|
|
27
25
|
status
|
28
26
|
end
|
29
27
|
|
28
|
+
def find_quotes_for(id)
|
29
|
+
repository.values.select { |s| s.quoted_status_id == id }
|
30
|
+
end
|
31
|
+
|
30
32
|
def find_replies_for(id)
|
31
33
|
repository.values.select { |s| s.in_reply_to_status_id == id }
|
32
34
|
end
|